Bảng băm so với cây nhị phân


30

Khi triển khai từ điển ('Tôi muốn tra cứu dữ liệu khách hàng bằng ID khách hàng của họ'), các cấu trúc dữ liệu điển hình được sử dụng là bảng băm và cây tìm kiếm nhị phân. Ví dụ, tôi biết rằng thư viện C ++ STL thực hiện các từ điển (họ gọi chúng là bản đồ) bằng cách sử dụng các cây tìm kiếm nhị phân (cân bằng) và .NET framework sử dụng các bảng băm dưới mui xe.

Những lợi thế và bất lợi của các cấu trúc dữ liệu này là gì? Có một số lựa chọn khác là hợp lý trong các tình huống nhất định?

Lưu ý rằng tôi không đặc biệt quan tâm đến các trường hợp khóa có cấu trúc cơ bản mạnh, giả sử, tất cả chúng đều là số nguyên giữa 1 và n hoặc một cái gì đó.


1
Tôi sẽ làm bạn bực tức nhưng bạn không thể chỉ nói "số nguyên giữa 1 và n" vì trong trường hợp đó, một mảng sẽ vượt qua tất cả các cấu trúc dữ liệu khác :-). "Chuỗi" có vẻ công bằng và bao gồm hầu hết các tình huống.
jmad

@jmad anh nói anh không hứng thú với trường hợp đó.
Joe

@Joe Tôi nghĩ rằng rõ ràng tôi đã tính đến điều này. Dù sao đó không phải là một lý do để đưa ra ví dụ tồi tệ nhất có thể.
jmad

1
Trên thực tế .NET có cả hai từ điển được triển khai bằng cây và từ điển được triển khai bằng bảng băm (và C ++ kể từ tiêu chuẩn 2011).
sepp2k

Câu trả lời:


26

Toàn bộ chuyên luận có thể được viết về chủ đề này; Tôi sẽ chỉ đề cập đến một số điểm nổi bật và tôi sẽ giữ cho cuộc thảo luận về các cấu trúc dữ liệu khác ở mức tối thiểu (thực sự có nhiều biến thể). Trong suốt câu trả lời này, là số lượng khóa trong từ điển.n

Câu trả lời ngắn gọn là các bảng băm nhanh hơn trong hầu hết các trường hợp , nhưng có thể rất tệ ở mức tồi tệ nhất của chúng. Cây tìm kiếm có nhiều lợi thế, bao gồm hành vi trong trường hợp xấu nhất , nhưng có phần chậm hơn trong các trường hợp điển hình.

Cây tìm kiếm nhị phân cân bằng có độ phức tạp khá thống nhất: mỗi phần tử có một nút trong cây (thường là 4 từ bộ nhớ), và các thao tác cơ bản (tra cứu, chèn, xóa) mất thời gian (đảm bảo giới hạn trên tiệm cận). Chính xác hơn, một truy cập trong cây mất khoảng so sánh .Ôi(tôig(n))tôiog2(n)

Bảng băm là một chút thay đổi. Họ yêu cầu một mảng khoảng con trỏ. Truy cập vào một yếu tố phụ thuộc vào chất lượng của hàm băm. Mục đích của hàm băm là phân tán các phần tử. Một bảng băm có tác dụng nghiêm trọng nếu tất cả các yếu tố bạn muốn lưu trữ trong đó có các giá trị băm khác nhau. Nếu đây là trường hợp, thì các thao tác cơ bản (tra cứu, chèn, xóa) mất thời gian , với hằng số khá nhỏ (một phép tính băm cộng với một lần tra cứu con trỏ). Điều này làm cho bảng băm rất nhanh trong nhiều trường hợp điển hình.2nÔi(1)

Một vấn đề chung với các bảng băm là độ phức tạp không được đảm bảo.Ôi(1)

  • Ngoài ra, có một điểm mà bảng trở nên đầy đủ; khi điều đó xảy ra (hoặc, tốt hơn, một chút trước khi điều đó xảy ra), bảng cần được mở rộng, đòi hỏi phải di chuyển tất cả các phần tử của nó, với chi phí . Điều này có thể giới thiệu hành vi giật giật của Hồi giáo khi có rất nhiều yếu tố được thêm vào.Ôi(n)
  • Đầu vào có thể va chạm vào một vài giá trị băm. Điều này hiếm khi xảy ra một cách tự nhiên, nhưng nó có thể là một vấn đề bảo mật nếu đầu vào được chọn bởi kẻ tấn công: đó là cách để làm chậm đáng kể một số máy chủ. Vấn đề này đã khiến một số triển khai ngôn ngữ lập trình (như Perl và Python) chuyển từ bảng băm cũ đơn giản sang hàm băm liên quan đến một số ngẫu nhiên được chọn khi bảng băm được xây dựng, cùng với hàm băm lan truyền tốt dữ liệu ngẫu nhiên này (làm tăng hằng số nhân trong ) hoặc đến cây tìm kiếm nhị phân. Mặc dù bạn có thể tránh va chạm bằng cách sử dụng hàm băm mật mã, nhưng điều này không được thực hiện trong thực tế vì băm mật mã tương đối chậm để tính toán.Ôi(1)

Khi bạn ném dữ liệu cục bộ vào hỗn hợp, các bảng băm hoạt động kém. Chúng hoạt động chính xác vì chúng lưu trữ các phần tử liên quan cách xa nhau, điều đó có nghĩa là nếu ứng dụng tìm kiếm các phần tử chia sẻ tiền tố theo trình tự, nó sẽ không được hưởng lợi từ các hiệu ứng bộ đệm. Điều này không liên quan nếu ứng dụng thực hiện tra cứu ngẫu nhiên.

Một yếu tố khác có lợi cho cây tìm kiếm là chúng là một cấu trúc dữ liệu bất biến : nếu bạn cần lấy một bản sao của cây và thay đổi một vài yếu tố trong đó, bạn có thể chia sẻ hầu hết cấu trúc dữ liệu. Nếu bạn lấy một bản sao của bảng băm, bạn cần sao chép toàn bộ mảng con trỏ. Ngoài ra, nếu bạn đang làm việc trong một ngôn ngữ chức năng thuần túy, bảng băm thường không phải là một tùy chọn.

Khi bạn vượt ra ngoài chuỗi, bảng băm và cây tìm kiếm nhị phân đưa ra các yêu cầu khác nhau về kiểu dữ liệu của khóa: bảng băm yêu cầu hàm băm (hàm từ khóa đến số nguyên sao cho , trong khi cây tìm kiếm nhị phân yêu cầu tổng thứ tự. Đôi khi, băm có thể được lưu vào bộ đệm, nếu có đủ chỗ trong cấu trúc dữ liệu nơi khóa được lưu trữ, lưu trữ kết quả so sánh (hoạt động nhị phân) thường không thực tế. Mặt khác, các so sánh có thể được hưởng lợi từ việc rút ngắn: nếu các khóa thường khác nhau trong một vài byte đầu tiên, thì so sánh âm có thể rất nhanh.k1k2h(k1)= =h(k2)

Cụ thể, nếu bạn sẽ cần thứ tự trên các phím, ví dụ nếu bạn muốn có thể liệt kê các khóa theo thứ tự bảng chữ cái, thì bảng băm không có ích (bạn sẽ cần sắp xếp chúng), trong khi bạn có thể đi thẳng qua một cây tìm kiếm theo thứ tự.

Bạn có thể kết hợp cây tìm kiếm nhị phân và bảng băm dưới dạng cây băm . Cây băm lưu trữ các khóa trong cây tìm kiếm theo hàm băm của chúng. Điều này rất hữu ích, ví dụ, trong một ngôn ngữ lập trình chức năng thuần túy, nơi bạn muốn làm việc trên dữ liệu không có mối quan hệ thứ tự dễ tính toán.

Khi các phím là chuỗi (hoặc số nguyên), một Trie có thể tùy chọn khác. Trie là một cây, nhưng được lập chỉ mục khác với cây tìm kiếm: bạn viết khóa ở dạng nhị phân và sang trái là 0 và phải cho 1. Chi phí của một truy cập do đó tỷ lệ thuận với độ dài của khóa. Tries có thể được nén để loại bỏ các nút trung gian; cây này được gọi là cây patricia trie hoặc cây radix . Cây Radix có thể vượt trội hơn cây cân bằng, đặc biệt khi nhiều khóa chia sẻ tiền tố chung.


2
BST không có địa phương dữ liệu xấu?
Svick 17/03/2016

@svick Họ có thể hoặc không, tùy thuộc vào cách các nút được phân bổ. Tăng độ thơm của cây có thể giúp đỡ mà không ảnh hưởng đến thời gian chạy (chi phí lớn hơn và mã phức tạp hơn).
Gilles 'SO- ngừng trở thành ác quỷ'

2
Trên BST, thật dễ dàng để có được các phần tử "theo thứ tự", đối với một bảng băm thì không có vấn đề gì.
vonbrand

Ngoài lý do bảo mật, tại sao các bảng băm có thời gian trong trường hợp xấu nhất nếu trường hợp trung bình của chúng tốt hơn so với cây nhị phân? Tôi tưởng tượng rằng tiện ích / sự tiện lợi của người dùng có mối quan hệ gần như tuyến tính với việc cây mất bao lâu để hoàn thành, vì vậy giá trị (trung bình) dự kiến ​​sẽ là tất cả vấn đề.
Kelmikra

@ Kyth'Py1k Ý của bạn là gì khi kết thúc cây? Điểm của các bảng băm là truy cập một giá trị tại một thời điểm, không phải toàn bộ cây, nếu không, một danh sách hoặc mảng sẽ hoạt động tốt hơn. Ngay cả trong các tình huống trong đó giá trị trung bình là vấn đề quan trọng (không phải luôn luôn như vậy, ví dụ như khi bạn có các ràng buộc về thời gian thực), đó là mức trung bình trên các yêu cầu được đưa ra trong một tình huống nhất định, thường không hoàn toàn thống nhất trên bảng - vd: thiên vị cho một tiền tố nhất định.
Gilles 'SO- ngừng trở nên xấu xa'
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.