Hiệu quả của từ điển C #


13

Từ điển C # là một cách đơn giản để tìm nếu có thứ gì đó tồn tại, v.v. Tôi có một câu hỏi mặc dù về cách chúng hoạt động. Giả sử thay vì từ điển tôi sử dụng ArrayList. Thay vì sử dụng ContainsKey(hoặc một phương thức tương đương trong ngôn ngữ khác), tôi lặp qua ArrayList để kiểm tra xem có thứ gì đó tồn tại ở đó không (hoặc thực hiện tìm kiếm nhị phân nếu dữ liệu được sắp xếp hoặc một cái gì đó tương tự). Sự khác biệt về hiệu quả là gì? Là ContainsKeyphương pháp sử dụng một số cách hiệu quả hơn thay vì lặp qua các phím và kiểm tra xem những gì tôi đang tìm kiếm có tồn tại không?

Nếu giả sử tôi đã tạo một hàm băm cụ thể tương ứng với loại dữ liệu mà tôi đang có và được thiết kế riêng cho bộ dữ liệu đó thì có, hàm băm đó thực sự nhanh hơn việc lặp qua dữ liệu. Nhưng từ điển là chung. Phương thức ContainsKey không dành riêng cho dữ liệu mà nó nhận được, đây là phương pháp tìm kiếm chung.

Về cơ bản những gì tôi đang hỏi là. Từ điển rất hữu ích cho các lập trình viên. Chúng bao gồm các phương thức trợ giúp nhiều thứ và chúng kết hợp các chuỗi với số nguyên, (khóa và giá trị) và nhiều thứ khác. Nhưng liên quan đến hiệu quả, họ cung cấp những gì? Sự khác biệt trong việc có một là gì dictionaryvs một ArrayListcủastructs(string,int)


Bạn đang thực sự so sánh táo với cam ở đây. Tôi nghĩ rằng từ khóa bạn đang tìm kiếm là Data Structures liên kết wiki này có thể giúp ích nhiều hơn cho bạn
Ampt 6/12/14

Câu trả lời:


20

Bạn đã phải đào sâu một chút để xem cách thực hiện Từ điển trong C # - Nó không rõ ràng như HashMap (bảng băm) hoặc TreeMap (một cây được sắp xếp) (hoặc ConcảnSkipListMap - một danh sách bỏ qua ).

Nếu bạn đi sâu vào phần "Ghi chú":

Lớp chung từ điển cung cấp ánh xạ từ một tập hợp các khóa thành một tập các giá trị. Mỗi bổ sung vào từ điển bao gồm một giá trị và khóa liên quan của nó. Lấy một giá trị bằng cách sử dụng khóa của nó rất nhanh, gần với O (1), vì lớp Từ điển được triển khai dưới dạng bảng băm.

Và chúng tôi đã có nó. Đó là một bảng băm . Lưu ý rằng tôi đã liên kết bài viết Wikipedia ở đó - nó khá tốt. Bạn có thể muốn đọc phần về giải quyết va chạm. Có thể lấy tập dữ liệu bệnh lý trong đó tra cứu chuyển sang O (N) (ví dụ: mọi thứ bạn chèn đều rơi vào cùng một giá trị băm hoặc chỉ mục trong bảng băm vì một số lý do và bạn còn lại với việc thăm dò tuyến tính ).

Mặc dù Từ điển là một giải pháp cho mục đích chung, bạn không nên chuyển qua các loại cụ thể (chẳng hạn như Từ điển) - bạn nên chuyển qua các giao diện. Trong trường hợp này, giao diện đó là IDictionary( docs ). Về vấn đề này, bạn hoàn toàn có khả năng viết triển khai từ điển của riêng bạn, thực hiện mọi thứ một cách tối ưu cho dữ liệu bạn có.

Như hiệu quả của việc tra cứu / chứa khác nhau?

  • Đi bộ một danh sách chưa sắp xếp: O (N)
  • Tìm kiếm nhị phân của một mảng được sắp xếp: O (log N)
  • Cây được sắp xếp: O (log N)
  • Bảng băm: O (1)

Đối với hầu hết mọi người, bảng băm là những gì họ muốn.

Bạn có thể thấy rằng SortedDipedia là những gì bạn muốn thay thế:

Lớp SortedDictionary<TKey, TValue>chung là một cây tìm kiếm nhị phân với truy xuất O (log n), trong đó n là số phần tử trong từ điển. Về mặt này, nó tương tự như SortedList<TKey, TValue>lớp chung. Hai lớp có các mô hình đối tượng tương tự nhau và cả hai đều có truy xuất O (log n).

Mặc dù, một lần nữa, nếu cấu trúc dữ liệu không phải là cấu trúc phù hợp với dữ liệu của bạn, bạn sẽ được cung cấp các công cụ (giao diện) để có thể viết một công cụ phù hợp nhất với dữ liệu của bạn.

Từ điển là một loại dữ liệu trừu tượng . Bạn đưa cho tôi một cuốn Từ điển và tôi biết tôi có thể làm gì với nó và tất cả các công cụ ở đó để tôi sử dụng theo bản chất của nó là một cuốn Từ điển. Nếu bạn đưa cho tôi một ArrayList, tôi sẽ thấy mình viết mã của riêng mình để tìm kiếm, chèn hoặc xóa các mục khỏi danh sách. Điều này làm lãng phí thời gian của tôi và cũng có nghĩa là có nhiều khả năng xảy ra lỗi khi tôi sao chép mã nhiều lần từ điểm này sang điểm khác.


5
O (1) không nhất thiết là "nhanh". Việc duyệt qua danh sách vẫn có thể nhanh hơn hàm băm cho kích thước bộ sưu tập mà ứng dụng đang xử lý.
whatsisname

5
@whatsisname không bao giờ tôi khẳng định rằng O (1) nhanh. Nó chắc chắn có tiềm năng là nhanh nhất. Lặp lại các khóa của hashtable chậm hơn so với ArrayList (trừ khi bạn đang sử dụng một cái gì đó như LinkedHashMap mà Java cung cấp). Điều quan trọng là phải biết dữ liệu của bạn và cách nó hoạt động và chọn bộ sưu tập phù hợp cho dữ liệu đó - và nếu điều đó không tồn tại, hãy viết nó. Tất nhiên, giả sử rằng một nỗ lực như vậy thực sự đáng giá thời gian (hồ sơ đầu tiên!).

Câu trích dẫn của bạn cho biết "Truy xuất giá trị bằng cách sử dụng khóa của nó rất nhanh, gần với O (1), vì lớp Từ điển được triển khai dưới dạng bảng băm.", Vì vậy OP có thể nhầm lẫn giữa hai khái niệm. Nói cách khác, tôi muốn làm rõ rằng chữ O lớn không nói lên toàn bộ câu chuyện liên quan đến "tốc độ".
whatsisname

3
@whatsisname đó là trực tiếp từ Microsoft. Sử dụng khóa để tra cứu giá trị, trừ khi bạn có hàm băm bệnh lý (giải quyết xung đột băm với một số cơ chế khác) sẽ nhanh hơn tìm kiếm trong cây hoặc danh sách được sắp xếp (hoặc danh sách chưa được sắp xếp). Java, ví dụ, sử dụng thăm dò tuyến tính (bước 1) cho độ phân giải va chạm của nó - thể chậm hơn trong trường hợp bảng quá đầy hoặc quá nhiều băm va chạm. Đối với trường hợp chung, nó là đủ tốt.

Như một ví dụ có liên quan, gần đây tôi đã tối ưu hóa một số mã trong c ++ ban đầu sử dụng bảng băm cho các bộ dữ liệu khoảng 20 mục và mất khoảng 400ms để hoàn thành. Chuyển sang cây nhị phân đã giảm xuống 200ms, vì cây đơn giản hơn để truy cập. Nhưng tôi đã có thể cắt giảm hơn nữa bằng cách sử dụng một loạt các cặp giá trị tên và một hàm tìm kiếm heuristic để đoán nơi bắt đầu tìm kiếm dựa trên các mẫu truy cập trong quá khứ. Vì vậy, tất cả chỉ là vấn đề có bao nhiêu dữ liệu và loại mô hình nào có trong các truy cập (ví dụ: địa phương).
Jules
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.