Tại sao tôi muốn sử dụng vector để deque


86

Từ

  1. chúng đều là vùng chứa bộ nhớ liền kề nhau;
  2. tính năng khôn ngoan, deque có hầu hết mọi thứ mà vector có nhưng nhiều hơn thế, vì nó hiệu quả hơn khi chèn vào phía trước.

Tại sao whould ai thích std::vectorđến std::deque?


2
Việc triển khai Visual C ++ của std::dequecó kích thước khối tối đa rất nhỏ (~ 16 byte, nếu tôi nhớ chính xác; có thể là 32), và như vậy không hoạt động rất tốt cho các ứng dụng thực tế. A deque<T>trong đó sizeof(T) > 8(hoặc 16? Đó là một số nhỏ) có các đặc tính hiệu suất tương tự như a vector<T*>, trong đó mỗi phần tử được phân bổ động. Các triển khai khác có kích thước khối tối đa khác nhau, do đó khó viết mã có các đặc tính hiệu suất tương đối giống nhau trên các nền tảng khác nhau deque.
James McNellis

12
Deque không phải là vùng chứa bộ nhớ liên tục.
Mohamed El-Nakib

@ravil Không, đó là bản sao, chỉ vào câu hỏi này.

1
Thật khó tin khi một câu hỏi với một sai sót thực tế trắng trợn không được sửa chữa lại nằm ở số dư 34 phiếu bầu
underscore_d

2
@underscore_d đó là lý do tại sao nó là một câu hỏi. Câu chuyện khác nếu đó là một câu trả lời;)
Assimilater

Câu trả lời:


115

Các yếu tố trong một dequekhông tiếp giáp trong bộ nhớ; vectorcác yếu tố được đảm bảo. Vì vậy, nếu bạn cần tương tác với một thư viện C đơn giản cần các mảng liền nhau hoặc nếu bạn quan tâm (nhiều) về địa phương không gian, thì bạn có thể thích hơn vector. Ngoài ra, vì có một số kế toán bổ sung, các hoạt động khác có thể đắt hơn (một chút) so với các vectorhoạt động tương đương của chúng . Mặt khác, việc sử dụng nhiều phiên bản / lớn vectorcó thể dẫn đến phân mảnh heap không cần thiết (làm chậm các cuộc gọi đến new).

Ngoài ra, như đã chỉ ra ở những nơi khác trên StackOverflow , có nhiều cuộc thảo luận hay hơn ở đây: http://www.gotw.ca/gotw/054.htm .


3
Có vẻ như liên kết đến "nơi khác" hiện đã chết (do kiểm duyệt?).
esilk

37

Để biết sự khác biệt, người ta nên biết cách dequethực hiện chung. Bộ nhớ được cấp phát theo các khối có kích thước bằng nhau và chúng được xâu chuỗi với nhau (dưới dạng một mảng hoặc có thể là một vectơ).

Vì vậy, để tìm phần tử thứ n, bạn tìm khối thích hợp sau đó truy cập phần tử bên trong nó. Đây là thời gian không đổi, vì nó luôn luôn chính xác là 2 lần tra cứu, nhưng điều đó vẫn nhiều hơn véc tơ.

vectorcũng hoạt động tốt với các API muốn có bộ đệm liền kề vì chúng là API C hoặc linh hoạt hơn trong việc có thể lấy con trỏ và độ dài. (Vì vậy, bạn có thể có một vectơ bên dưới hoặc một mảng thông thường và gọi API từ khối bộ nhớ của bạn).

Nơi dequecó lợi thế lớn nhất của nó là:

  1. Khi phát triển hoặc thu nhỏ bộ sưu tập từ một trong hai đầu
  2. Khi bạn đang xử lý kích thước bộ sưu tập rất lớn.
  3. Khi xử lý bools và bạn thực sự muốn bools hơn là một bitet.

Cái thứ hai trong số này ít được biết đến hơn, nhưng đối với kích thước bộ sưu tập rất lớn:

  1. Chi phí tái phân bổ lớn
  2. Việc phải tìm một khối bộ nhớ liền kề là hạn chế, vì vậy bạn có thể hết bộ nhớ nhanh hơn.

Trước đây, khi tôi xử lý các tập hợp lớn và chuyển từ mô hình liền kề sang mô hình khối, chúng tôi có thể lưu trữ một tập hợp lớn gấp khoảng 5 lần trước khi hết bộ nhớ trong hệ thống 32 bit. Điều này một phần là do, khi phân bổ lại, nó thực sự cần lưu trữ khối cũ cũng như khối mới trước khi sao chép các phần tử qua.

Đã nói tất cả những điều này, bạn có thể gặp rắc rối với std::dequecác hệ thống sử dụng phân bổ bộ nhớ "lạc quan". Trong khi nỗ lực của nó để yêu cầu kích thước bộ đệm lớn để phân bổ lại a vectorcó thể sẽ bị từ chối vào một thời điểm nào đó với a bad_alloc, bản chất lạc quan của bộ cấp phát có khả năng luôn cấp yêu cầu cho bộ đệm nhỏ hơn do a yêu cầu dequevà điều đó có thể gây ra hệ điều hành giết một tiến trình để cố gắng lấy một số bộ nhớ. Bất kỳ cái nào nó chọn có thể không quá dễ chịu.

Các giải pháp thay thế trong trường hợp này là đặt cờ cấp hệ thống để ghi đè phân bổ lạc quan (không phải lúc nào cũng khả thi) hoặc quản lý bộ nhớ theo cách thủ công hơn, ví dụ: sử dụng trình cấp phát của riêng bạn để kiểm tra việc sử dụng bộ nhớ hoặc tương tự. Rõ ràng là không lý tưởng. (Có thể trả lời câu hỏi của bạn là thích vectơ hơn ...)


29

Tôi đã triển khai cả vector và deque nhiều lần. deque cực kỳ phức tạp hơn từ quan điểm triển khai. Sự phức tạp này chuyển thành nhiều mã hơn và mã phức tạp hơn. Vì vậy, bạn thường thấy kích thước mã được nhấn khi bạn chọn deque trên vector. Bạn cũng có thể gặp phải một tốc độ nhỏ nếu mã của bạn chỉ sử dụng những thứ mà vectơ vượt trội (tức là push_back).

Nếu bạn cần một hàng đợi kết thúc kép, deque là người chiến thắng rõ ràng. Nhưng nếu bạn đang thực hiện hầu hết các lần chèn và xóa ở phía sau, thì vector sẽ là người chiến thắng rõ ràng. Khi bạn không chắc chắn, hãy khai báo vùng chứa của bạn với một typedef (vì vậy dễ dàng chuyển đổi qua lại) và đo lường.


3
Câu hỏi - ủy ban đã xem xét việc thêm một kết hợp của hai (giả sử, một "bộ bài") vào C ++ chưa? (tức là kết thúc kép vector.) Tôi đã viết một triển khai được liên kết với bên dưới trong câu trả lời của tôi . Nó có thể nhanh như một vectornhưng áp dụng rộng rãi hơn nhiều (ví dụ: khi thực hiện một hàng đợi nhanh).
user541686,

5

std::dequekhông có bộ nhớ liên tục được đảm bảo - và nó thường hơi chậm hơn đối với truy cập được lập chỉ mục. Một deque thường được triển khai dưới dạng "danh sách các vectơ".


14
Tôi không nghĩ "danh sách vectơ" là đúng: hiểu biết của tôi là hầu hết các triển khai đều là "vectơ của con trỏ đến mảng", mặc dù nó phụ thuộc vào định nghĩa của bạn về "danh sách" (tôi đọc "danh sách" là "danh sách liên kết ", mà sẽ không đáp ứng các yêu cầu phức tạp).
James McNellis

2

Theo http://www.cplusplus.com/reference/stl/deque/ , "không giống như vectơ, deques không được đảm bảo có tất cả các phần tử của nó ở các vị trí lưu trữ liền kề, do đó loại bỏ khả năng truy cập an toàn thông qua số học con trỏ."

Deques phức tạp hơn một chút, một phần vì chúng không nhất thiết phải có bố cục bộ nhớ liền nhau. Nếu bạn cần tính năng đó, bạn không nên sử dụng deque.

(Trước đây, câu trả lời của tôi đưa ra sự thiếu chuẩn hóa (từ cùng một nguồn như trên, "deques có thể được triển khai bởi các thư viện cụ thể theo những cách khác nhau"), nhưng điều đó thực sự áp dụng cho bất kỳ kiểu dữ liệu thư viện chuẩn nào.)


5
std::dequeđược tiêu chuẩn hóa không kém hơn std::vector. Tôi không tin rằng std::dequecó thể đáp ứng các yêu cầu phức tạp đối với bộ nhớ liền kề.
Jerry Coffin,

1
Có lẽ cách viết của tôi kém: mặc dù đúng là việc chuẩn hóa không triệt để, theo tôi hiểu, các vectơ được chuẩn hóa để trở thành một chuỗi liên hợp, còn deques thì không. Đó dường như là một trong những yếu tố quyết định.
Patrickvacek

1
@JerryCoffin: Yêu cầu phức tạp nào dequekhông thể đáp ứng với bộ nhớ liền kề?
user541686

1
@Mehrdad: Thành thật mà nói, tôi không nhớ mình đã nghĩ gì. Gần đây tôi đã không xem xét phần đó của tiêu chuẩn đủ để tôi cảm thấy thoải mái khi nói rõ ràng rằng nhận xét trước đó của tôi là sai, nhưng nhìn vào nó ngay bây giờ, tôi cũng không thể nghĩ nó sẽ đúng như thế nào.
Jerry Coffin

3
@JerryCoffin: Các yêu cầu về độ phức tạp thực sự rất nhỏ: bạn có thể phân bổ một mảng lớn và bắt đầu đẩy chuỗi của mình từ giữa ra ngoài (tôi đoán đây là những gì triển khai Mehrdad thực hiện), sau đó phân bổ lại khi bạn đi đến cuối. Vấn đề với cách tiếp cận này là nó không đáp ứng một trong các yêu cầu deque, cụ thể là phần chèn ở các đầu sẽ không làm mất hiệu lực được tham chiếu đến các phần tử hiện có. Yêu cầu này ngụ ý bộ nhớ không liên tục.
Yakov Galka

0

Một deque là một vùng chứa trình tự cho phép truy cập ngẫu nhiên vào các phần tử của nó nhưng nó không được đảm bảo là có bộ nhớ liền kề.


0

Tôi nghĩ rằng ý tưởng hay để thực hiện kiểm tra hiệu suất của từng trường hợp. Và đưa ra quyết định dựa trên các bài kiểm tra này.

Tôi muốn std::dequehơn std::vectortrong hầu hết các trường hợp.


1
Câu hỏi, nếu có thể được chắt lọc từ những lỗi thực tế và những từ còn thiếu, là tại sao ai đó lại thích vector. Chúng ta có thể suy ra rằng tại sao không phải là một hệ quả tất yếu. Nói rằng bạn thích deque, vì những lý do không xác định, từ các bài kiểm tra không xác định, không phải là một câu trả lời.
underscore_d


0

Mặt khác, vector thường nhanh hơn deque. Nếu bạn không thực sự cần tất cả các tính năng của deque, hãy sử dụng một vector.

Mặt khác, đôi khi bạn làm các tính năng cần thiết mà vector không cung cấp cho bạn, trong trường hợp này bạn phải sử dụng một deque. Ví dụ: tôi thách thức bất kỳ ai cố gắng viết lại mã này , mà không sử dụng deque và không thay đổi nhiều thuật toán.


Trên thực tế, trên cùng một chuỗi push_backpop_backhoạt động, deque<int>luôn nhanh hơn ít nhất 20% so với vector<int>trong các thử nghiệm của tôi (gcc với O3). Tôi đoán đó là lý do tại sao dequelà lựa chọn tiêu chuẩn cho những thứ như std::stack...
igel

-1

Lưu ý rằng bộ nhớ vectơ được cấp phát lại khi mảng phát triển. Nếu bạn có con trỏ đến các phần tử vectơ, chúng sẽ trở nên không hợp lệ.

Ngoài ra, nếu bạn xóa một phần tử, các trình vòng lặp sẽ trở nên không hợp lệ (nhưng không phải là "for (auto ...)").

Chỉnh sửa: đã thay đổi 'deque' thành 'vector'


-1: Chèn và xóa ở đầu hoặc cuối KHÔNG làm mất hiệu lực của các tham chiếu và con trỏ đến các phần tử khác. (Mặc dù chèn và xóa ở giữa không)
Sjoerd

@Sjoerd Tôi đã đổi nó thành 'vector'.
Pierre
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.