Sự khác biệt giữa vùng chứa deque và list STL là gì?


93

Sự khác biệt giữa hai là gì? Ý tôi là tất cả các phương pháp đều giống nhau. Vì vậy, đối với người dùng, chúng hoạt động giống hệt nhau.

Đúng không??


1
tôi quan tâm đến hiệu suất lặp lại .. điều gì nhanh hơn để chuyển từ cầu xin đến kết thúc?
nkint

Câu trả lời:


60

Từ bản tóm tắt SGI STL (có ngày nhưng vẫn rất hữu ích) về deque:

Một deque rất giống một vectơ: giống như vectơ, nó là một chuỗi hỗ trợ truy cập ngẫu nhiên vào các phần tử, chèn thời gian không đổi và loại bỏ các phần tử ở cuối dãy, chèn và loại bỏ thời gian tuyến tính các phần tử ở giữa.

Cách chính mà deque khác với vector là deque cũng hỗ trợ chèn thời gian không đổi và loại bỏ các phần tử ở đầu dãy. Ngoài ra, deque không có bất kỳ hàm thành viên nào tương tự với dung lượng () và dự trữ () của vectơ, và không cung cấp bất kỳ đảm bảo nào về tính hợp lệ của trình lặp được liên kết với các hàm thành viên đó.

Đây là bản tóm tắt listtừ cùng một trang web:

Một danh sách là một danh sách được liên kết kép. Có nghĩa là, nó là một Trình tự hỗ trợ cả chuyển tiếp và duyệt ngược, và (phân bổ) theo thời gian không đổi và loại bỏ các phần tử ở đầu hoặc cuối, hoặc ở giữa. Danh sách có thuộc tính quan trọng là việc chèn và nối không làm mất hiệu lực các trình vòng lặp để liệt kê các phần tử và thậm chí việc loại bỏ chỉ làm mất hiệu lực các trình vòng lặp trỏ đến các phần tử bị loại bỏ. Thứ tự của các trình vòng lặp có thể được thay đổi (nghĩa là, danh sách :: trình vòng lặp có thể có một người tiền nhiệm hoặc người kế nhiệm khác sau một thao tác với danh sách so với trước đó), nhưng bản thân các trình vòng lặp sẽ không bị vô hiệu hoặc được thực hiện để trỏ đến các phần tử khác trừ khi sự vô hiệu hóa đó hoặc đột biến là rõ ràng.

Tóm lại, các vùng chứa có thể có các quy trình được chia sẻ nhưng thời gian đảm bảo cho các quy trình đó khác nhau giữa các vùng chứa . Điều này rất quan trọng khi xem xét vùng chứa nào trong số những vùng chứa này sẽ sử dụng cho một tác vụ: tính đến cách vùng chứa sẽ được sử dụng thường xuyên nhất (ví dụ: nhiều hơn để tìm kiếm hơn là chèn / xóa) sẽ giúp bạn hướng bạn đến đúng vùng chứa .


2
std :: danh sách còn có phương pháp 'splice' cho phép bạn hợp nhất hai danh sách với nhau
Rick

23
Trên thực tế, đảm bảo thời gian là tính năng quan trọng thứ hai của danh sách. Các hầu hết tính năng quan trọng của danh sách là bạn có thể thêm và loại bỏ các yếu tố và không làm mất hiệu lực lặp của bạn! Trong (hầu hết?) Mọi vùng chứa STL khác, mọi thao tác chỉnh sửa đều làm mất hiệu lực của tất cả các trình vòng lặp của bạn - vì vậy để "xóa các mục phù hợp", bạn cần tích lũy các mục phù hợp trong một thao tác và sau đó xóa chúng trong một thao tác khác. Trong một danh sách, bạn có thể xem qua nó, xóa và thêm tùy ý và không bao giờ phải tính toán lại một trình lặp.
Tom Swirly

1
Đây cũng là những khác biệt trừu tượng, vì vậy hãy đo lường thực tế cho trường hợp của bạn! Cả list và deque đều có chèn / xóa O (1), nhưng đừng quên điều đó có nghĩa là k * O (1), và k có các giá trị khác nhau cho list và deque. Trong trường hợp của tôi, việc thêm một đối tượng vào danh sách mất nhiều thời gian hơn mười lần so với deque vì danh sách cần nhiều lệnh gọi đến new / delete. Điều đó rõ ràng sẽ khác nhau dựa trên việc triển khai STL nào bạn có.
Andy Krouwel

125

Hãy để tôi liệt kê những điểm khác biệt:

  • Deque quản lý các phần tử của nó bằng một mảng động , cung cấp quyền truy cập ngẫu nhiên và có giao diện gần giống như một vectơ.
  • Danh sách quản lý các phần tử của nó như một danh sách được liên kết kép và không cung cấp quyền truy cập ngẫu nhiên .

  • Deque cung cấp tính năng chèn và xóa nhanh ở cả phần cuối và phần đầu. Việc chèn và xóa các phần tử ở giữa tương đối chậm vì tất cả các phần tử ở cả hai đầu có thể bị di chuyển để nhường chỗ hoặc lấp đầy khoảng trống.
  • Trong Danh sách , việc chèn và xóa các phần tử diễn ra nhanh chóng ở mỗi vị trí, bao gồm cả hai đầu.

  • Deque : Bất kỳ việc chèn hoặc xóa các phần tử không phải ở đầu hoặc cuối đều làm mất hiệu lực của tất cả các con trỏ, tham chiếu và trình vòng lặp tham chiếu đến các phần tử của deque.
  • Danh sách : Việc chèn và xóa các phần tử không làm mất hiệu lực của con trỏ, tham chiếu và trình vòng lặp đến các phần tử khác.

Phức tạp

             Insert/erase at the beginning       in middle        at the end

Deque:       Amortized constant                  Linear           Amortized constant
List:        Constant                            Constant         Constant

5
@aJ: Sự khác biệt giữa constantvà là amortized constantgì?
Lazer

16
Các hoạt động trong dài hạn hoạt động như mô tả. Tuy nhiên, một thao tác có thể mất nhiều thời gian hơn quy định. ví dụ: để chèn một phần tử vào một vectơ có dung lượng hiện tại là 10 và kích thước đã là 9 là không đổi, trong đó thời gian là tuyến tính nếu dung lượng là 10 và kích thước cũng là 10. Vì nó phải cấp phát và sao chép tất cả các phần tử vào bộ nhớ mới .
aJ.

5
@aJ: Deque cung cấp quyền truy cập ngẫu nhiên như thế nào? Ngoài ra cấu trúc này được thực hiện như thế nào?

9

std::list về cơ bản là một danh sách được liên kết kép.

std::deque, mặt khác, được thực hiện giống như std::vector. Nó có thời gian truy cập liên tục theo chỉ mục, cũng như chèn và xóa ở đầu và cuối, cung cấp các đặc điểm hiệu suất khác biệt đáng kể so với danh sách.


5

Một đảm bảo quan trọng khác là cách mỗi vùng chứa khác nhau lưu trữ dữ liệu của nó trong bộ nhớ:

  • Vectơ là một khối bộ nhớ liền kề duy nhất.
  • Một deque là một tập hợp các khối bộ nhớ được liên kết, trong đó nhiều hơn một phần tử được lưu trữ trong mỗi khối bộ nhớ.
  • Danh sách là một tập hợp các phần tử được phân tán trong bộ nhớ, nghĩa là: chỉ một phần tử được lưu trữ trên mỗi "khối" bộ nhớ.

Lưu ý rằng deque được thiết kế để cố gắng cân bằng các ưu điểm của cả vector và list mà không có nhược điểm tương ứng của chúng. Nó là một vùng chứa đặc biệt thú vị trong các nền tảng giới hạn bộ nhớ, ví dụ, vi điều khiển.

Tuy nhiên, chiến lược lưu trữ bộ nhớ thường bị bỏ qua, tuy nhiên, đây thường là một trong những lý do quan trọng nhất để chọn vùng chứa phù hợp nhất cho một ứng dụng nhất định.


4

Không. Một deque chỉ hỗ trợ chèn và xóa O (1) ở mặt trước và mặt sau. Ví dụ, nó có thể được triển khai trong một vectơ có bao bọc. Vì nó cũng đảm bảo O (1) truy cập ngẫu nhiên, bạn có thể chắc chắn rằng nó không sử dụng (chỉ) một danh sách được liên kết kép.


2

Những khác biệt về hiệu suất đã được giải thích bởi những người khác. Tôi chỉ muốn nói thêm rằng các giao diện tương tự hoặc thậm chí giống hệt nhau rất phổ biến trong lập trình hướng đối tượng - một phần của phương pháp luận chung để viết phần mềm hướng đối tượng. Bạn KHÔNG THỂ NÀO cho rằng hai lớp hoạt động giống nhau chỉ đơn giản là vì chúng triển khai cùng một giao diện, hơn nữa bạn nên giả định rằng một con ngựa hoạt động như một con chó vì cả hai đều thực hiện tấn công () và make_noise ().


1

Đây là cách sử dụng mã bằng chứng về khái niệm của danh sách, bản đồ không có thứ tự giúp tra cứu O (1) và duy trì LRU chính xác của O (1). Cần các trình vòng lặp (không bị xóa) để tồn tại các thao tác xóa. Lên kế hoạch sử dụng bộ nhớ đệm được quản lý bằng phần mềm lớn tùy ý O (1) cho con trỏ CPU trên bộ nhớ GPU. Bắt đầu với bộ lập lịch Linux O (1) (hàng đợi chạy LRU <-> trên mỗi bộ xử lý). Bản đồ không có thứ tự có quyền truy cập thời gian liên tục thông qua bảng băm.

#include <iostream> 
#include <list> 
#include <unordered_map>  
using namespace std; 

struct MapEntry {
  list<uint64_t>::iterator LRU_entry;
  uint64_t CpuPtr;
};
typedef unordered_map<uint64_t,MapEntry> Table;
typedef list<uint64_t> FIFO;
FIFO  LRU;        // LRU list at a given priority 
Table DeviceBuffer; // Table of device buffers

void Print(void){
  for (FIFO::iterator l = LRU.begin(); l != LRU.end(); l++) {
    std::cout<< "LRU    entry "<< *l << "   :    " ;
    std::cout<< "Buffer entry "<< DeviceBuffer[*l].CpuPtr <<endl;
  }  
}
int main() 
{ 

  LRU.push_back(0);
  LRU.push_back(1);
  LRU.push_back(2);
  LRU.push_back(3);
  LRU.push_back(4);

  for (FIFO::iterator i = LRU.begin(); i != LRU.end(); i++) {
    MapEntry ME = { i, *i}; 
    DeviceBuffer[*i] = ME;
  }

  std::cout<< "************ Initial set of CpuPtrs" <<endl;
  Print();

  {
    // Suppose evict an entry - find it via "key - memory address uin64_t" and remove from 
    // cache "tag" table AND LRU list with O(1) operations
    uint64_t key=2;
    LRU.erase(DeviceBuffer[2].LRU_entry);
    DeviceBuffer.erase(2);
  }

  std::cout<< "************ Remove item 2 " <<endl;
  Print();

  { 
    // Insert a new allocation in both tag table, and LRU ordering wiith O(1) operations
    uint64_t key=9;
    LRU.push_front(key); 
    MapEntry ME = { LRU.begin(), key };
    DeviceBuffer[key]=ME;
  }

  std::cout<< "************ Add item 9  " <<endl;
  Print();

  std::cout << "Victim "<<LRU.back()<<endl;
} 

Bạn đã đăng điều này ở đúng nơi? Điều này không trả lời câu hỏi.
Blastfurnace

1

Trong số những khác biệt nổi bật giữa dequelist

  • Đối với deque :

    Các mặt hàng được lưu trữ cạnh nhau;

    Tối ưu hóa để thêm dữ liệu từ hai phía (trước, sau);

    Các phần tử được lập chỉ mục bởi số (số nguyên).

    Có thể được duyệt bằng trình vòng lặp và thậm chí bằng chỉ mục của phần tử.

    Thời gian truy cập dữ liệu nhanh hơn.

  • Đối với list

    Các mục được lưu trữ "ngẫu nhiên" trong bộ nhớ;

    Chỉ có thể được duyệt bởi trình lặp;

    Tối ưu hóa cho việc chèn và loại bỏ ở giữa.

    Thời gian truy cập dữ liệu chậm hơn, chậm lặp lại do tính định vị không gian rất kém.

    Xử lý rất tốt các yếu tố lớn

Bạn cũng có thể kiểm tra Liên kết sau , này so sánh hiệu suất giữa hai vùng chứa STL (với std :: vector)

Hy vọng tôi đã chia sẻ một số thông tin hữu ích.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.