209

I'm trying to find the definition of a binary search tree and I keep finding different definitions everywhere.

Some say that for any given subtree the left child key is less than or equal to the root.

Some say that for any given subtree the right child key is greater than or equal to the root.

And my old college data structures book says "every element has a key and no two elements have the same key."

Is there a universal definition of a bst? Particularly in regards to what to do with trees with multiple instances of the same key.

EDIT: Maybe I was unclear, the definitions I'm seeing are

1) left <= root < right

2) left < root <= right

3) left < root < right, such that no duplicate keys exist.

laike9m
  • 18,344
  • 20
  • 107
  • 140
Tim Merrifield
  • 6,088
  • 5
  • 31
  • 33

13 Answers13

103

Many algorithms will specify that duplicates are excluded. For example, the example algorithms in the MIT Algorithms book usually present examples without duplicates. It is fairly trivial to implement duplicates (either as a list at the node, or in one particular direction.)

Most (that I've seen) specify left children as <= and right children as >. Practically speaking, a BST which allows either of the right or left children to be equal to the root node, will require extra computational steps to finish a search where duplicate nodes are allowed.

It is best to utilize a list at the node to store duplicates, as inserting an '=' value to one side of a node requires rewriting the tree on that side to place the node as the child, or the node is placed as a grand-child, at some point below, which eliminates some of the search efficiency.

You have to remember, most of the classroom examples are simplified to portray and deliver the concept. They aren't worth squat in many real-world situations. But the statement, "every element has a key and no two elements have the same key", is not violated by the use of a list at the element node.

So go with what your data structures book said!

Edit:

Universal Definition of a Binary Search Tree involves storing and search for a key based on traversing a data structure in one of two directions. In the pragmatic sense, that means if the value is <>, you traverse the data structure in one of two 'directions'. So, in that sense, duplicate values don't make any sense at all.

This is different from BSP, or binary search partition, but not all that different. The algorithm to search has one of two directions for 'travel', or it is done (successfully or not.) So I apologize that my original answer didn't address the concept of a 'universal definition', as duplicates are really a distinct topic (something you deal with after a successful search, not as part of the binary search.)

Chris
  • 4,852
  • 1
  • 22
  • 17
  • 2
    What are the disadvantages of using a list at the node? – Pacerier Jun 12 '12 at 19:18
  • 4
    @Pacerier I think instead of maintaining a list, we can maintain a reference count at each node and update the count when duplicate occurs. Such an algorithm would be much easier and efficient in searching and storing. Also, it would require minimal changes to the existing algorithm which does not support duplicates. – SimpleGuy Sep 01 '17 at 02:08
  • 2
    @SimpleGuy If you meant a reference *list*, I can agree with that. If you really meant a reference *count* (i.e. have multiple nodes but one stores the count), I think that's not going to work. Which node will maintain the count? What if the tree needs to rebalance that node to a lower level etc.? – Andrew Sep 16 '20 at 01:01
  • 1
    @Andrew I think @SimpleGuy means to store count at each node for its value `{num = 1, count 5}`, I think will work well for primary datatypes & String, but not for other Non-primitive datatypes – k4anubhav Jun 02 '23 at 16:31
69

If your binary search tree is a red black tree, or you intend to any kind of "tree rotation" operations, duplicate nodes will cause problems. Imagine your tree rule is this:

left < root <= right

Now imagine a simple tree whose root is 5, left child is nil, and right child is 5. If you do a left rotation on the root you end up with a 5 in the left child and a 5 in the root with the right child being nil. Now something in the left tree is equal to the root, but your rule above assumed left < root.

I spent hours trying to figure out why my red/black trees would occasionally traverse out of order, the problem was what I described above. Hopefully somebody reads this and saves themselves hours of debugging in the future!

Rich
  • 12,068
  • 9
  • 62
  • 94
  • 24
    Don't rotate when you have equal nodes! Traverse down to the next level and rotate that. – Rich Jun 19 '12 at 15:51
  • 2
    Other solutions are to either change the tree rule to be `left <= node <= right`, or only insert before the *first* occurrence of a value. – paxdiablo Jun 28 '16 at 01:51
  • What problems can this cause in practice? Seem to me that if you are ok with left <= node <= right, then all the red-black tree operations will work out anyhow. – Björn Lindqvist Nov 03 '16 at 15:25
  • @BjörnLindqvist As mentioned in another answer, the problem with allowing `<= <=` is that you're basically destroying one of the main purposes of having a BST in the first place: to have quick operations on your sorted collection. Unless you do what Rich said or you just put all of the duplicates into the same node, you're then going to have to traverse down possibly to the very bottom of the tree to check for any duplicates. – Andrew Sep 16 '20 at 00:56
  • @Rich One problem I have with your solution is it basically assumes that there won't be many, many duplicates of the same key. If there are, then your tree will end up extremely unbalanced. So this should only be considered, if ever, imo, if you're certain that the duplicates won't **ever** be a very common occurrence. It seems like for a general-purpose BST, duplicates using the same node is the way to go. – Andrew Sep 16 '20 at 00:58
  • @Andrew Why? You'd designate the left or right branch for equality and you'll have just as fast lookup as before. – Björn Lindqvist Sep 16 '20 at 02:09
  • @BjörnLindqvist No you won't. Either you'll need to break that paradigm for the rebalance, or as I said you're opening yourself up to a potentially awful BST if you have many duplicates of the same key. If you break that paradigm for the rebalance, then you could have right descendants of the left child or left descendants of the right child that are equivalent after the rebalance. – Andrew Sep 16 '20 at 04:16
60

All three definitions are acceptable and correct. They define different variations of a BST.

Your college data structure's book failed to clarify that its definition was not the only possible.

Certainly, allowing duplicates adds complexity. If you use the definition "left <= root < right" and you have a tree like:

      3
    /   \
  2       4

then adding a "3" duplicate key to this tree will result in:

      3
    /   \
  2       4
    \
     3

Note that the duplicates are not in contiguous levels.

This is a big issue when allowing duplicates in a BST representation as the one above: duplicates may be separated by any number of levels, so checking for duplicate's existence is not that simple as just checking for immediate childs of a node.

An option to avoid this issue is to not represent duplicates structurally (as separate nodes) but instead use a counter that counts the number of occurrences of the key. The previous example would then have a tree like:

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

and after insertion of the duplicate "3" key it will become:

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

This simplifies lookup, removal and insertion operations, at the expense of some extra bytes and counter operations.

duilio
  • 701
  • 5
  • 4
  • 1
    I'm very surprised that this was never even mentioned in the textbook I'm using. The prof didn't mention it either, nor the fact that duplicate keys are even an issue smh... – Oloff Biermann Jul 17 '19 at 23:12
  • 1
    @OloffBiermann Probably because most people just think, "We've covered Binary Search Trees, ./" without considering the significant implications of allowing duplicates. Perhaps they decided if you understand BST's then you can make your own modifications as needed. In their defense, the number of tree structures alone that are possible is humongous; there are sooooo many different implementation details about them. As just a starter, take a look here: https://en.wikipedia.org/wiki/List_of_data_structures#Trees – Andrew Sep 16 '20 at 00:52
  • Really like the counter approach here, seems like a solid example. – Nuclearman Oct 10 '20 at 05:47
35

In a BST, all values descending on the left side of a node are less than (or equal to, see later) the node itself. Similarly, all values descending on the right side of a node are greater than (or equal to) that node value(a).

Some BSTs may choose to allow duplicate values, hence the "or equal to" qualifiers above. The following example may clarify:

     14
    /  \
  13    22
 /     /  \
1    16    29
          /  \
        28    29

This shows a BST that allows duplicates(b) - you can see that to find a value, you start at the root node and go down the left or right subtree depending on whether your search value is less than or greater than the node value.

This can be done recursively with something like:

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)

and calling it with:

foundIt = hasVal (rootNode, valToLookFor)

Duplicates add a little complexity since you may need to keep searching once you've found your value, for other nodes of the same value. Obviously that doesn't matter for hasVal since it doesn't matter how many there are, just whether at least one exists. It will however matter for things like countVal, since it needs to know how many there are.


(a) You could actually sort them in the opposite direction should you so wish provided you adjust how you search for a specific key. A BST need only maintain some sorted order, whether that's ascending or descending (or even some weird multi-layer-sort method like all odd numbers ascending, then all even numbers descending) is not relevant.


(b) Interestingly, if your sorting key uses the entire value stored at a node (so that nodes containing the same key have no other extra information to distinguish them), there can be performance gains from adding a count to each node, rather than allowing duplicate nodes.

The main benefit is that adding or removing a duplicate will simply modify the count rather than inserting or deleting a new node (an action that may require re-balancing the tree).

So, to add an item, you first check if it already exists. If so, just increment the count and exit. If not, you need to insert a new node with a count of one then rebalance.

To remove an item, you find it then decrement the count - only if the resultant count is zero do you then remove the actual node from the tree and rebalance.

Searches are also quicker given there are fewer nodes but that may not be a large impact.

For example, the following two trees (non-counting on the left, and counting on the right) would be equivalent (in the counting tree, i.c means c copies of item i):

     __14__                    ___22.2___
    /      \                  /          \
  14        22             7.1            29.1
 /  \      /  \           /   \          /    \
1    14  22    29      1.1     14.3  28.1      30.1
 \            /  \
  7         28    30

Removing the leaf-node 22 from the left tree would involve rebalancing (since it now has a height differential of two) the resulting 22-29-28-30 subtree such as below (this is one option, there are others that also satisfy the "height differential must be zero or one" rule):

\                      \
 22                     29
   \                   /  \
    29      -->      28    30
   /  \             /
 28    30         22

Doing the same operation on the right tree is a simple modification of the root node from 22.2 to 22.1 (with no rebalancing required).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    For the duplicate case, can you just check if the right child is the same as the current node in the node.val == srchval: clause, and then if so go right? – bneil Feb 11 '13 at 16:43
  • @bneil Way late, but: No, you cannot, because, due to the way self-balancing BST's rebalance to the medians in order to maintain reasonable heights/weights of subtrees (you don't want a doubly linked list), you can easily have situations where the left child, the current node, and the right child are all the same value, unless you were to explicitly ensure that e.g. with a `>=` comparison, only the leftmost node of a set of duplicates can be the parent (thus ensuring all are right children); this could lead to a disastrous tree if there are many of the same duplicates though. – Andrew Sep 16 '20 at 00:46
  • @bneil Lazy Ren's [answer](https://stackoverflow.com/a/61292717/1599699) explains it well: "...even if search() finds the node with the key, it must traverse down to the leaf node for the nodes with [the] duplicate key." As an example, take the tree in paxdiablo's answer here and swap out that 28 with another 29. You can imagine there might be more 29's in their children as well. duilio's [answer](https://stackoverflow.com/a/20426419/1599699) has another great example. – Andrew Sep 16 '20 at 00:48
12

In the book "Introduction to algorithms", third edition, by Cormen, Leiserson, Rivest and Stein, a binary search tree (BST) is explicitly defined as allowing duplicates. This can be seen in figure 12.1 and the following (page 287):

"The keys in a binary search tree are always stored in such a way as to satisfy the binary-search-tree property: Let x be a node in a binary search tree. If y is a node in the left subtree of x, then y:key <= x:key. If y is a node in the right subtree of x, then y:key >= x:key."

In addition, a red-black tree is then defined on page 308 as:

"A red-black tree is a binary search tree with one extra bit of storage per node: its color"

Therefore, red-black trees defined in this book support duplicates.

Cabbage soup
  • 1,344
  • 1
  • 18
  • 26
Laurent Martin
  • 121
  • 1
  • 2
  • A binary search tree doesn't *have* to allow duplicates, that's just an option. It could also disallow odd numbers, or primes, or strings with too many vowels, or any other type of data. The only *real* requirement is that it's ordered in some way, and *preferably* self-balancing. – paxdiablo Sep 16 '20 at 01:01
6

Any definition is valid. As long as you are consistent in your implementation (always put equal nodes to the right, always put them to the left, or never allow them) then you're fine. I think it is most common to not allow them, but it is still a BST if they are allowed and place either left or right.

SoapBox
  • 20,457
  • 3
  • 51
  • 87
  • 1
    if you have a set of data which contains duplicate keys, then those items should all be stored within the 1 node on the tree via a different method (linked list, etc). the tree should only contain unique keys. – nickf Nov 19 '08 at 03:55
  • Should perhaps, but it is not required for the properties of a search tree. – SoapBox Nov 19 '08 at 03:57
  • #1 condition of a BST: "each node (item in the tree) has a distinct value;" http://en.wikipedia.org/wiki/Binary_search_tree – nickf Nov 19 '08 at 03:58
  • Wikipedia isn't the end all source of knowledge. Explain to me what is invalid about having duplicate keys in a BST other than wasting space... it maintains all important properties. – SoapBox Nov 19 '08 at 04:01
  • 1
    Also note from the wiki that the right subtree contains values "greater than or equal to" the root. Hence the wiki definition is self-contradictory. – SoapBox Nov 19 '08 at 04:02
  • 1
    +1: Different people use different definitions. If you implement a new BST, you need to make sure you're explicit about which assumptions you're making about duplicate entries. – Mr Fooz Nov 19 '08 at 04:04
  • The usual convention is less than => left, greater than => right – Mitch Wheat Nov 19 '08 at 04:05
  • 1
    Seems like the consensus is (left <= root <= right) when allowing duplicates. But that some folks definition of a BST does not allow dups. Or maybe some people teach it that way to avoid the additional complexity. – Tim Merrifield Nov 19 '08 at 04:09
  • 1
    incorrect! it's EITHER left <= root < right OR left < root <= right, OR left > root >= right OR left >= root > right – Mitch Wheat Nov 19 '08 at 04:10
  • What?! Where are you getting this definition from? – Tim Merrifield Nov 19 '08 at 04:32
  • 1
    The less than or greater than relationship either points left OR right. Where you implement the equals is implementation dependent but it must only be on one side. – Mitch Wheat Nov 19 '08 at 04:37
  • Again, provide me a link to a definition of a BST that says the relationship can go either way. – Tim Merrifield Nov 19 '08 at 04:42
  • 1
    @Mitch, it doesn't HAVE to be so, it just makes the processing of the tree a lot easier. Having said that, I'd question the sanity of anyone who implemented it otherwise (arbitrarily left or right rather than same way always). – paxdiablo Nov 19 '08 at 04:42
5

I just want to add some more information to what @Robert Paulson answered.

Let's assume that node contains key & data. So nodes with the same key might contain different data.
(So the search must find all nodes with the same key)

  1. left <= cur < right
  1. left < cur <= right
  1. left <= cur <= right
  1. left < cur < right && cur contain sibling nodes with the same key.
  1. left < cur < right, such that no duplicate keys exist.

1 & 2. works fine if the tree does not have any rotation-related functions to prevent skewness.
But this form doesn't work with AVL tree or Red-Black tree, because rotation will break the principal.
And even if search() finds the node with the key, it must traverse down to the leaf node for the nodes with duplicate key.
Making time complexity for search = theta(logN)

3. will work well with any form of BST with rotation-related functions.
But the search will take O(n), ruining the purpose of using BST.
Say we have the tree as below, with 3) principal.

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

If we do search(12) on this tree, even tho we found 12 at the root, we must keep search both left & right child to seek for the duplicate key.
This takes O(n) time as I've told.

4. is my personal favorite. Let's say sibling means the node with the same key.
We can change above tree into below.

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

Now any search will take O(logN) because we don't have to traverse children for the duplicate key.
And this principal also works well with AVL or RB tree.

Lazy Ren
  • 704
  • 6
  • 17
  • 1
    This was a great answer. I'd mark it as the answer if I could. #4 is definitely the "right" way. (P.S. There's a 6th way not addressed here, but I responded to it with a comment below: https://stackoverflow.com/a/339597/1599699) – Andrew Sep 16 '20 at 01:06
3

Working on a red-black tree implementation I was getting problems validating the tree with multiple keys until I realized that with the red-black insert rotation, you have to loosen the constraint to

left <= root <= right

Since none of the documentation I was looking at allowed for duplicate keys and I didn't want to rewrite the rotation methods to account for it, I just decided to modify my nodes to allow for multiple values within the node, and no duplicate keys in the tree.

Jherico
  • 28,584
  • 8
  • 61
  • 87
2

Those three things you said are all true.

  • Keys are unique
  • To the left are keys less than this one
  • To the right are keys greater than this one

I suppose you could reverse your tree and put the smaller keys on the right, but really the "left" and "right" concept is just that: a visual concept to help us think about a data structure which doesn't really have a left or right, so it doesn't really matter.

nickf
  • 537,072
  • 198
  • 649
  • 721
1

Duplicate Keys • What happens if there's more than one data item with the same key? – This presents a slight problem in red-black trees. – It's important that nodes with the same key are distributed on both sides of other nodes with the same key. – That is, if keys arrive in the order 50, 50, 50, • you want the second 50 to go to the right of the first one, and the third 50 to go to the left of the first one. • Otherwise, the tree becomes unbalanced. • This could be handled by some kind of randomizing process in the insertion algorithm. – However, the search process then becomes more complicated if all items with the same key must be found. • It's simpler to outlaw items with the same key. – In this discussion we'll assume duplicates aren't allowed

One can create a linked list for each node of the tree that contains duplicate keys and store data in the list.

mszlazak
  • 59
  • 1
  • 6
1

1.) left <= root < right

2.) left < root <= right

3.) left < root < right, such that no duplicate keys exist.

I might have to go and dig out my algorithm books, but off the top of my head (3) is the canonical form.

(1) or (2) only come about when you start to allow duplicates nodes and you put duplicate nodes in the tree itself (rather than the node containing a list).

Community
  • 1
  • 1
Robert Paulson
  • 17,603
  • 5
  • 34
  • 53
  • Could you explain why left <= root <= right is not ideal? – Helin Wang Aug 10 '18 at 05:51
  • 1
    Have a look at the accepted answer by @paxdiablo - You can see the duplicate value can exist with `>=`. _Ideal_ depends on your requirements, but if you do have a lot of duplicate values, and you allow the duplicates to exist in the structure, your bst can end up being linear - ie O(n). – Robert Paulson Aug 13 '18 at 03:11
0

BST definition uses keys to store. For a database keys are unique. BST is also useful to store database in the physical file system for a DBMS. (In practical situations keys with some other data are used to store a record in the database.) Such BST with Red-Black or AVL like operations is an efficient structure. If we allow duplicate then we have use extra link field to point all records with duplicate key in a linked list form.

0

The elements ordering relation <= is a total order so the relation must be reflexive but commonly a binary search tree (aka BST) is a tree without duplicates.

Otherwise if there are duplicates you need run twice or more the same function of deletion!

Alberto
  • 2,881
  • 7
  • 35
  • 66