Câu trả lời của Joe cực kỳ tốt và cung cấp cho bạn tất cả các từ khóa quan trọng.
Bạn nên lưu ý rằng nghiên cứu cấu trúc dữ liệu cô đọng vẫn còn ở giai đoạn đầu và nhiều kết quả chủ yếu là lý thuyết. Nhiều cấu trúc dữ liệu được đề xuất khá phức tạp để thực hiện, nhưng hầu hết sự phức tạp là do thực tế là bạn cần duy trì độ phức tạp tiệm cận cả về kích thước vũ trụ và số lượng phần tử được lưu trữ. Nếu một trong hai thứ này tương đối ổn định, thì rất nhiều sự phức tạp sẽ biến mất.
Nếu bộ sưu tập là bán tĩnh (nghĩa là, chèn rất hiếm hoặc ít nhất là âm lượng thấp), thì chắc chắn nên xem xét cấu trúc dữ liệu tĩnh dễ thực hiện (sdarray của Sadakane là một lựa chọn tốt) kết hợp với cập nhật bộ nhớ cache. Về cơ bản, bạn ghi lại các cập nhật trong cấu trúc dữ liệu truyền thống (ví dụ: B-tree, trie, bảng băm) và cập nhật hàng loạt cấu trúc dữ liệu "chính". Đây là một kỹ thuật rất phổ biến trong truy xuất thông tin, vì các chỉ mục đảo ngược có nhiều lợi thế để tìm kiếm nhưng khó cập nhật tại chỗ. Nếu đây là trường hợp, xin vui lòng cho tôi biết trong một nhận xét và tôi sẽ sửa đổi câu trả lời này để cung cấp cho bạn một số gợi ý.
Nếu chèn thường xuyên hơn, thì tôi đề nghị băm ngắn gọn. Ý tưởng cơ bản đủ đơn giản để giải thích ở đây, vì vậy tôi sẽ làm như vậy.
Vì vậy, kết quả lý thuyết thông tin cơ bản là nếu bạn lưu trữ phần tử từ vũ trụ của các mục và không có thông tin nào khác (ví dụ: không có mối tương quan giữa các phần tử) thì bạn cần bit để lưu trữ nó. (Tất cả các logarit là cơ sở-2 trừ khi có quy định khác.) Bạn cần nhiều bit này. Không có cách nào xung quanh nó.unulog(un)+O(1)
Bây giờ một số thuật ngữ:
- Nếu bạn có cấu trúc dữ liệu có thể lưu trữ dữ liệu và hỗ trợ các hoạt động của bạn trong bit, chúng tôi gọi đây là cấu trúc dữ liệu ẩn .log(un)+O(1)
- Nếu bạn có cấu trúc dữ liệu có thể lưu trữ dữ liệu và hỗ trợ các hoạt động của bạn trong bit của không gian, chúng tôi gọi đây là cấu trúc dữ liệu nhỏ gọn . Lưu ý rằng trong thực tế điều này có nghĩa là tổng chi phí tương đối (so với mức tối thiểu theo lý thuyết) nằm trong một hằng số. Nó có thể là 5% trên không, hoặc 10% trên không, hoặc 10 lần trên không.log(un)+O(log(un))=(1+O(1))log(un)
- Nếu bạn có cấu trúc dữ liệu có thể lưu trữ dữ liệu và hỗ trợ các hoạt động của bạn trong bit của không gian, chúng tôi gọi đây là cấu trúc dữ liệu cô đọng .log(un)+o(log(un))=(1+o(1))log(un)
Sự khác biệt giữa cô đọng và nhỏ gọn là sự khác biệt giữa little-oh và big-oh. Bỏ qua những thứ có giá trị tuyệt đối trong giây lát ...
- g(n)=O(f(n)) có nghĩa là tồn tại hằng số và một số sao cho tất cả , .cn0n>n0g(n)<c⋅f(n)
- g(n)=o(f(n)) có nghĩa là với tất cả các hằng số tồn tại một số sao cho tất cả , .cn0n>n0g(n)<c⋅f(n)
Một cách không chính thức, big-oh và little-oh đều là "trong một yếu tố không đổi", nhưng với big-oh, hằng số được chọn cho bạn (bởi nhà thiết kế thuật toán, nhà sản xuất CPU, định luật vật lý hoặc bất cứ điều gì), nhưng với rất ít -oh bạn chọn hằng số cho mình và nó có thể nhỏ như bạn muốn . Nói cách khác, với các cấu trúc dữ liệu cô đọng, chi phí tương đối sẽ nhỏ tùy ý khi kích thước của vấn đề tăng lên.
Tất nhiên, quy mô của vấn đề có thể phải lớn để nhận ra chi phí tương đối mà bạn muốn, nhưng bạn không thể có mọi thứ.
OK, với điều đó dưới vành đai của chúng tôi, hãy đặt một số con số cho vấn đề. Giả sử các khóa là số nguyên -bit (vì vậy kích thước vũ trụ là ) và chúng tôi muốn lưu trữ số nguyên này. Giả sử rằng chúng ta có thể sắp xếp một cách kỳ diệu một bảng băm lý tưởng hóa với đầy đủ công suất và không lãng phí, do đó chúng ta cần chính xác các khe băm .n2n2m2m
Một thao tác tra cứu sẽ băm khóa -bit, che đi các bit để tìm các vị trí băm, sau đó kiểm tra xem giá trị trong bảng có khớp với khóa không. Càng xa càng tốt.nm
Một bảng băm như vậy sử dụng bit . Chúng ta có thể làm tốt hơn thế này không?n2m
Giả sử rằng hàm băm là không thể đảo ngược. Sau đó, chúng tôi không phải lưu trữ toàn bộ khóa trong mỗi khe băm. Vị trí của vị trí băm cung cấp cho bạn bit của giá trị băm, vì vậy nếu bạn chỉ lưu trữ các bit còn lại của , bạn có thể xây dựng lại khóa từ hai mẩu thông tin đó (vị trí của vị trí băm và giá trị được lưu trữ ở đó). Vì vậy, bạn sẽ chỉ cần bit lưu trữ.hmn−m(n−m)2m
Nếu nhỏ so với , phép tính gần đúng của Stirling và một số học nhỏ (bằng chứng là một bài tập!) Cho thấy:2m2n
(n−m)2m=log(2n2m)+o(log(2n2m))
Vì vậy, cấu trúc dữ liệu này là ngắn gọn.
Tuy nhiên, có hai cách bắt.
Cái bắt đầu tiên là xây dựng các hàm băm khả nghịch "tốt". May mắn thay, điều này là dễ dàng hơn nhiều so với vẻ ngoài của nó; Các nhà mật mã tạo ra các chức năng không thể đảo ngược mọi lúc, chỉ họ gọi chúng là "cyphers". Ví dụ, bạn có thể dựa vào hàm băm trên mạng Feistel, đây là cách đơn giản để xây dựng hàm băm không thể đảo ngược từ các hàm băm không thể đảo ngược.
Điều hấp dẫn thứ hai là các bảng băm thực sự không lý tưởng, nhờ nghịch lý Sinh nhật. Vì vậy, bạn muốn sử dụng một loại bảng băm tinh vi hơn, giúp bạn gần gũi hơn với công suất đầy đủ mà không bị đổ. Băm cuckoo là hoàn hảo cho điều này, vì nó cho phép bạn tùy ý gần với lý tưởng trong lý thuyết, và khá gần trong thực tế.
Băm cuckoo yêu cầu nhiều hàm băm và nó yêu cầu các giá trị trong các vị trí băm được gắn thẻ với hàm băm nào được sử dụng. Vì vậy, nếu bạn sử dụng bốn hàm băm, ví dụ, bạn cần lưu trữ thêm hai bit trong mỗi khe băm. Điều này vẫn ngắn gọn khi phát triển, vì vậy nó không phải là vấn đề trong thực tế và vẫn còn lưu trữ toàn bộ chìa khóa.m
Ồ, bạn cũng có thể muốn nhìn vào cây van Emde Boas.
THÊM
Nếu ở đâu đó xung quanh , thì xấp xỉ , vì vậy (một lần nữa) giả sử rằng không có mối tương quan nào nữa giữa các giá trị, về cơ bản bạn không thể thực hiện bất kỳ tốt hơn một vectơ bit. Bạn sẽ lưu ý rằng giải pháp băm ở trên thực sự làm suy giảm hiệu quả trường hợp đó (cuối cùng bạn lưu trữ một bit trên mỗi khe băm), nhưng rẻ hơn là chỉ sử dụng khóa làm địa chỉ thay vì sử dụng hàm băm.nu2log(un)u
Nếu rất gần với , tất cả các tài liệu cấu trúc dữ liệu cô đọng đều khuyên bạn nên đảo ngược ý nghĩa của từ điển. Lưu trữ các giá trị không xảy ra trong tập hợp. Tuy nhiên, bây giờ bạn thực sự phải hỗ trợ thao tác xóa và để duy trì hành vi cô đọng, bạn cũng cần có khả năng thu nhỏ cấu trúc dữ liệu khi nhiều phần tử được "thêm". Mở rộng bảng băm là một hoạt động được hiểu rõ, nhưng ký hợp đồng thì không.nu