Các khóa trùng lặp có được phép trong định nghĩa của cây tìm kiếm nhị phân không?


139

Tôi đang cố gắng tìm định nghĩa của cây tìm kiếm nhị phân và tôi tiếp tục tìm các định nghĩa khác nhau ở mọi nơi.

Một số người nói rằng với bất kỳ cây con nào, khóa con bên trái nhỏ hơn hoặc bằng gốc.

Một số người nói rằng với bất kỳ cây con nào, khóa con bên phải lớn hơn hoặc bằng gốc.

Và cuốn sách cấu trúc dữ liệu đại học cũ của tôi nói rằng "mọi yếu tố đều có một khóa và không có hai yếu tố nào có cùng một khóa".

Có một định nghĩa phổ quát của một bst? Đặc biệt liên quan đến những gì cần làm với cây với nhiều phiên bản của cùng một khóa.

EDIT: Có thể tôi không rõ ràng, các định nghĩa tôi đang thấy là

1) trái <= root <phải

2) trái <root <= phải

3) trái <root <phải, sao cho không tồn tại khóa trùng lặp.

Câu trả lời:


78

Nhiều thuật toán sẽ chỉ định rằng các bản sao được loại trừ. Ví dụ, các thuật toán ví dụ trong sách Thuật toán MIT thường trình bày các ví dụ mà không trùng lặp. Việc thực hiện các bản sao khá đơn giản (như là một danh sách tại nút hoặc theo một hướng cụ thể.)

Hầu hết (mà tôi đã thấy) chỉ định trẻ trái là <= và trẻ phải là>. Nói một cách thực tế, một BST cho phép một trong hai con phải hoặc trái bằng với nút gốc, sẽ yêu cầu các bước tính toán bổ sung để hoàn thành tìm kiếm trong đó cho phép các nút trùng lặp.

Tốt nhất là sử dụng một danh sách tại nút để lưu trữ các bản sao, vì việc chèn giá trị '=' vào một bên của nút yêu cầu viết lại cây ở phía đó để đặt nút là con hoặc nút được đặt là grand -child, tại một số điểm dưới đây, giúp loại bỏ một số hiệu quả tìm kiếm.

Bạn phải nhớ, hầu hết các ví dụ trong lớp học được đơn giản hóa để miêu tả và đưa ra khái niệm. Họ không đáng ngồi xổm trong nhiều tình huống thực tế. Nhưng tuyên bố, "mọi phần tử đều có một khóa và không có hai phần tử nào có cùng một khóa", không bị vi phạm bởi việc sử dụng danh sách tại nút phần tử.

Vì vậy, đi với những gì cuốn sách cấu trúc dữ liệu của bạn nói!

Biên tập:

Định nghĩa phổ quát của Cây tìm kiếm nhị phân liên quan đến việc lưu trữ và tìm kiếm khóa dựa trên việc truyền tải cấu trúc dữ liệu theo một trong hai hướng. Theo nghĩa thực dụng, điều đó có nghĩa là nếu giá trị là <>, bạn đi qua cấu trúc dữ liệu theo một trong hai "hướng". Vì vậy, theo nghĩa đó, các giá trị trùng lặp không có ý nghĩa gì cả.

Điều này khác với BSP hoặc phân vùng tìm kiếm nhị phân, nhưng không khác nhau lắm. Thuật toán tìm kiếm có một trong hai hướng cho 'du lịch', hoặc nó đã được thực hiện (thành công hay không.) Vì vậy, tôi xin lỗi rằng câu trả lời ban đầu của tôi không đề cập đến khái niệm 'định nghĩa phổ quát', vì các bản sao thực sự là một sự khác biệt chủ đề (một cái gì đó bạn xử lý sau khi tìm kiếm thành công, không phải là một phần của tìm kiếm nhị phân.)


1
Những bất lợi của việc sử dụng một danh sách tại nút là gì?
Pacerier

1
@Pacerier Tôi nghĩ thay vì duy trì một danh sách, chúng ta có thể duy trì số tham chiếu tại mỗi nút và cập nhật số đếm khi xảy ra trùng lặp. Một thuật toán như vậy sẽ dễ dàng và hiệu quả hơn nhiều trong việc tìm kiếm và lưu trữ. Ngoài ra, nó sẽ yêu cầu thay đổi tối thiểu đối với thuật toán hiện có không hỗ trợ trùng lặp.
SimpleGuy

50

Nếu cây tìm kiếm nhị phân của bạn là cây đen đỏ hoặc bạn có ý định thực hiện bất kỳ loại hoạt động "xoay cây" nào, các nút trùng lặp sẽ gây ra sự cố. Hãy tưởng tượng quy tắc cây của bạn là thế này:

trái <root <= phải

Bây giờ hãy tưởng tượng một cây đơn giản có gốc là 5, con trái là 0 và con phải là 5. Nếu bạn thực hiện xoay trái trên gốc, bạn kết thúc với 5 ở con trái và 5 ở gốc với con phải được không Bây giờ một cái gì đó trong cây bên trái bằng gốc, nhưng quy tắc của bạn ở trên giả định trái <root.

Tôi đã dành hàng giờ cố gắng để tìm ra lý do tại sao cây đỏ / đen của tôi thỉnh thoảng đi ngang khỏi trật tự, vấn đề là những gì tôi đã mô tả ở trên. Hy vọng ai đó đọc được điều này và tiết kiệm cho mình hàng giờ gỡ lỗi trong tương lai!


18
Đừng xoay khi bạn có các nút bằng nhau! Đi xuống cấp độ tiếp theo và xoay nó.
Giàu

2
Các giải pháp khác là thay đổi quy tắc cây thành left <= node <= righthoặc chỉ chèn trước khi xuất hiện giá trị đầu tiên .
paxdiablo

Những vấn đề này có thể gây ra trong thực tế? Dường như với tôi rằng nếu bạn ổn với trái <= nút <= phải, thì tất cả các hoạt động của cây đỏ-đen sẽ hoạt động bằng mọi cách.
Bjorn Lindqvist

39

Tất cả ba định nghĩa đều được chấp nhận và chính xác. Họ định nghĩa các biến thể khác nhau của một BST.

Cuốn sách cấu trúc dữ liệu đại học của bạn không thể làm rõ rằng định nghĩa của nó không phải là duy nhất có thể.

Chắc chắn, cho phép trùng lặp thêm phức tạp. Nếu bạn sử dụng định nghĩa "trái <= root <phải" và bạn có một cây như:

      3
    /   \
  2       4

sau đó thêm khóa trùng lặp "3" vào cây này sẽ dẫn đến:

      3
    /   \
  2       4
    \
     3

Lưu ý rằng các bản sao không ở mức tiếp giáp nhau.

Đây là một vấn đề lớn khi cho phép các bản sao trong biểu diễn BST như trên: các bản sao có thể được phân tách bằng bất kỳ số cấp nào, vì vậy việc kiểm tra sự tồn tại của bản sao không đơn giản chỉ là kiểm tra các nút con ngay lập tức.

Một tùy chọn để tránh vấn đề này là không thể hiện các bản sao có cấu trúc (như các nút riêng biệt) mà thay vào đó sử dụng bộ đếm đếm số lần xuất hiện của khóa. Ví dụ trước sẽ có một cây như:

      3(1)
    /     \
  2(1)     4(1)

và sau khi chèn khóa "3" trùng lặp, nó sẽ trở thành:

      3(2)
    /     \
  2(1)     4(1)

Điều này đơn giản hóa các hoạt động tra cứu, loại bỏ và chèn, với chi phí của một số byte bổ sung và các hoạt động truy cập.


Tôi rất ngạc nhiên vì điều này thậm chí không bao giờ được đề cập trong sách giáo khoa tôi đang sử dụng. Các prof cũng không đề cập đến nó, cũng như thực tế là các khóa trùng lặp thậm chí là một vấn đề ...
Oloff Biermann 17/07/19

22

Trong một BST, tất cả các giá trị giảm dần ở phía bên trái của một nút nhỏ hơn (hoặc bằng, xem sau) chính nút đó. Tương tự, tất cả các giá trị giảm dần ở phía bên phải của một nút lớn hơn (hoặc bằng) giá trị nút (a) .

Một số BST có thể chọn cho phép các giá trị trùng lặp, do đó, vòng loại "hoặc bằng" ở trên.

Ví dụ sau có thể làm rõ:

            |
      +--- 14 ---+
      |          |
+--- 13    +--- 22 ---+
|          |          |
1         16    +--- 29 ---+
                |          |
               28         29

Điều này cho thấy một BST cho phép trùng lặp - bạn có thể thấy rằng để tìm giá trị, bạn bắt đầu tại nút gốc và đi xuống cây con bên trái hoặc bên phải tùy thuộc vào giá trị tìm kiếm của bạn nhỏ hơn hoặc lớn hơn giá trị nút.

Điều này có thể được thực hiện đệ quy với một cái gì đó như:

def hasVal (node, srchval):
    if node == NULL:
         return false
    if node.val == srchval:
        return true
    if node.val > srchval:
        return hasVal (node.left, srchval)
    return hasVal (node.right, srchval)

và gọi nó bằng:

foundIt = hasVal (rootNode, valToLookFor)

Các bản sao thêm một chút phức tạp vì bạn có thể cần tiếp tục tìm kiếm một khi bạn đã tìm thấy giá trị của mình cho các nút khác có cùng giá trị.


(a) Bạn thực sự có thể sắp xếp chúng theo hướng ngược lại nếu bạn muốn với điều kiện bạn điều chỉnh cách bạn tìm kiếm một khóa cụ thể. Một BST chỉ cần duy trì một số thứ tự được sắp xếp, cho dù tăng dần hay giảm dần đều không liên quan.


Đối với trường hợp trùng lặp, bạn có thể kiểm tra xem con đúng có giống nút hiện tại trong mệnh đề node.val == srchval: không, và sau đó nếu đúng thì sao?
bneil

9

Trong cuốn sách "Giới thiệu về thuật toán", ấn bản thứ ba, của Cormen, Leiserson, Rivest và Stein, một cây tìm kiếm nhị phân (BST) được định nghĩa rõ ràng là cho phép trùng lặp . Điều này có thể được nhìn thấy trong hình 12.1 và sau đây (trang 287):

"Các khóa trong cây tìm kiếm nhị phân luôn được lưu trữ theo cách để thỏa mãn thuộc tính cây tìm kiếm nhị phân: Hãy xlà một nút trong cây tìm kiếm nhị phân. Nếu ylà một nút trong cây con bên trái của x, thì y:key <= x:key. Nếu ylà một nút trong cây con bên phải của x, sau đó y:key >= x:key. "

Ngoài ra, một cây đỏ đen sau đó được xác định trên trang 308 là:

"Cây đỏ-đen là cây tìm kiếm nhị phân có thêm một bit lưu trữ cho mỗi nút: màu của nó"

Do đó, cây đỏ-đen được định nghĩa trong cuốn sách này hỗ trợ các bản sao.


4

Bất kỳ định nghĩa là hợp lệ. Miễn là bạn nhất quán trong việc thực hiện (luôn đặt các nút bằng nhau ở bên phải, luôn đặt chúng ở bên trái hoặc không bao giờ cho phép chúng) thì bạn vẫn ổn. Tôi nghĩ rằng điều phổ biến nhất là không cho phép họ, nhưng nó vẫn là BST nếu họ được phép và đặt bên trái hoặc bên phải.


1
nếu bạn có một tập hợp dữ liệu chứa các khóa trùng lặp, thì tất cả các mục đó sẽ được lưu trữ trong nút 1 trên cây thông qua một phương thức khác (danh sách được liên kết, v.v.). cây chỉ nên chứa các khóa duy nhất.
nickf

1
Cũng lưu ý từ wiki rằng cây con bên phải chứa các giá trị "lớn hơn hoặc bằng" gốc. Do đó định nghĩa wiki là tự mâu thuẫn.
SoapBox

1
+1: Những người khác nhau sử dụng các định nghĩa khác nhau. Nếu bạn triển khai BST mới, bạn cần đảm bảo rằng bạn rõ ràng về những giả định mà bạn đang thực hiện về các mục trùng lặp.
Ông Fooz

1
Có vẻ như sự đồng thuận là (trái <= root <= phải) khi cho phép trùng lặp. Nhưng một số người định nghĩa về BST không cho phép dups. Hoặc có thể một số người dạy nó theo cách đó để tránh sự phức tạp thêm.
Tim Merrifield

1
sai! đó là EITHER trái <= root <phải HOẶC trái <root <= phải, OR trái> root> = phải HOẶC trái> = root> phải
Mitch Wheat

3

Làm việc trên một triển khai cây đỏ-đen Tôi gặp vấn đề khi xác thực cây bằng nhiều khóa cho đến khi tôi nhận ra rằng với vòng xoay chèn đỏ-đen, bạn phải nới lỏng các ràng buộc để

left <= root <= right

Vì không có tài liệu nào tôi đang xem được phép cho các khóa trùng lặp và tôi không muốn viết lại các phương thức xoay vòng để giải thích cho nó, tôi chỉ quyết định sửa đổi các nút của mình để cho phép nhiều giá trị trong nút và không có khóa trùng lặp nào trong cái cây.


2

Ba điều bạn nói đều đúng.

  • Chìa khóa là duy nhất
  • Bên trái là các phím nhỏ hơn khóa này
  • Bên phải là các phím lớn hơn khóa này

Tôi cho rằng bạn có thể đảo ngược cây của mình và đặt các phím nhỏ hơn ở bên phải, nhưng thực sự khái niệm "trái" và "phải" chỉ là: một khái niệm trực quan để giúp chúng ta suy nghĩ về cấu trúc dữ liệu không thực sự có bên trái hoặc đúng, vì vậy nó không thực sự quan trọng.


1

1.) trái <= root <phải

2.) trái <root <= phải

3.) trái <root <phải, sao cho không tồn tại khóa trùng lặp.

Tôi có thể phải đi và đào những cuốn sách thuật toán của mình, nhưng ngoài đỉnh đầu (3) là dạng chính tắc.

(1) hoặc (2) chỉ xuất hiện khi bạn bắt đầu cho phép các nút trùng lặp bạn đặt các nút trùng lặp trong chính cây (chứ không phải là nút chứa danh sách).


Bạn có thể giải thích tại sao trái <= root <= right không lý tưởng?
Helin Wang

Hãy xem câu trả lời được chấp nhận bởi @paxdiablo - Bạn có thể thấy giá trị trùng lặp có thể tồn tại với >=. Lý tưởng phụ thuộc vào yêu cầu của bạn, nhưng nếu bạn có nhiều giá trị trùng lặp và bạn cho phép các bản sao tồn tại trong cấu trúc, thì bst của bạn cuối cùng có thể là tuyến tính - tức là O (n).
Robert Paulson

1

Khóa trùng lặp • Điều gì xảy ra nếu có nhiều mục dữ liệu có cùng khóa? - Điều này trình bày một vấn đề nhỏ trong cây đỏ đen. - Điều quan trọng là các nút có cùng khóa được phân phối ở cả hai phía của các nút khác có cùng khóa. - Nghĩa là, nếu các phím đến theo thứ tự 50, 50, 50, • bạn muốn 50 thứ hai ở bên phải của cái thứ nhất và 50 thứ ba ở bên trái của cái thứ nhất. • Nếu không, cây trở nên mất cân bằng. • Điều này có thể được xử lý bởi một số loại quy trình ngẫu nhiên trong thuật toán chèn. - Tuy nhiên, quá trình tìm kiếm sau đó trở nên phức tạp hơn nếu tất cả các mục có cùng khóa phải được tìm thấy. • Đơn giản hơn đối với các mục ngoài vòng pháp luật có cùng khóa. - Trong cuộc thảo luận này, chúng tôi sẽ cho rằng các bản sao không được phép

Người ta có thể tạo một danh sách được liên kết cho mỗi nút của cây chứa các khóa trùng lặp và lưu trữ dữ liệu trong danh sách.


1

Tôi chỉ muốn thêm một số thông tin vào những gì @Robert Paulson đã trả lời.

Giả sử nút đó chứa khóa & dữ liệu. Vì vậy, các nút có cùng khóa có thể chứa dữ liệu khác nhau.
(Vì vậy, tìm kiếm phải tìm tất cả các nút có cùng khóa)

1) trái <= cur <phải

2) trái <cur <= phải

3) trái <= cur <= phải

4) bên trái <cur <right && cur chứa các nút anh chị em có cùng khóa.

5) trái <cur <phải, sao cho không tồn tại khóa trùng lặp.

1) & 2) hoạt động tốt nếu cây không có bất kỳ chức năng nào liên quan đến xoay để tránh xiên.
Nhưng hình thức này không hoạt động với cây AVL hoặc cây Đỏ-Đen , vì xoay vòng sẽ phá vỡ hiệu trưởng.
Và ngay cả khi search () tìm thấy nút có khóa, nó phải đi xuống nút lá cho các nút có khóa trùng lặp.
Tạo độ phức tạp thời gian cho tìm kiếm = theta (logN)

3) sẽ hoạt động tốt với mọi hình thức BST với các chức năng liên quan đến xoay vòng.
Nhưng việc tìm kiếm sẽ mất O (n) , làm hỏng mục đích sử dụng BST.
Nói rằng chúng ta có cây như dưới đây, với 3) hiệu trưởng.

         12
       /    \
     10     20
    /  \    /
   9   11  12 
      /      \
    10       12

Nếu chúng ta tìm kiếm (12) trên cây này, thậm chí tho chúng ta đã tìm thấy 12 ở gốc, chúng ta phải tiếp tục tìm kiếm cả con trái và phải để tìm khóa trùng lặp.
Điều này làm mất thời gian O (n) như tôi đã nói.

4) là sở thích cá nhân của tôi. Giả sử anh chị em có nghĩa là nút có cùng khóa.
Chúng ta có thể thay đổi cây trên thành dưới.

         12 - 12 - 12
       /    \
10 - 10     20
    /  \    /
   9   11  12

Bây giờ, bất kỳ tìm kiếm nào cũng sẽ mất O (logN) vì chúng ta không phải duyệt trẻ em để lấy khóa trùng lặp.
Và hiệu trưởng này cũng hoạt động tốt với cây AVL hoặc RB .


0

Quan hệ thứ tự các phần tử <= là một tổng thứ tự vì vậy mối quan hệ phải là phản xạ nhưng thông thường cây tìm kiếm nhị phân (còn gọi là BST) là một cây không có trùng lặp.

Nếu không, nếu có trùng lặp, bạn cần chạy hai lần hoặc nhiều hơn cùng chức năng xóa!

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.