A Binary Search Tree (BST) is a binary tree (max. 2 childs per node) with every node a key and associated value. Also a BST has the property that for every node, the left subtree contains only nodes with a smaller (or equal) key and the right subtree contains only nodes with strictly larger keys. This property will allow us to perform insertion and deletion operations in time proportional to the tree height. Later more on time complexity.
The BST class is declared as follows:
- public class BinarySearchTree<Tkey, Tvalue> where Tkey : IComparable<Tkey>
- protected class BinaryKeyValueNode<Tkey, Tvalue> where Tkey : IComparable<Tkey>
- {
- public Tkey Key { get; set; }
- public Tvalue Value { get; set; }
- public BinaryKeyValueNode<Tkey, Tvalue> Parent { get; set; }
- public BinaryKeyValueNode<Tkey, Tvalue> LeftChild { get; set; }
- public BinaryKeyValueNode<Tkey, Tvalue> RightChild { get; set; }
- public BinaryKeyValueNode(Tkey key, Tvalue value)
- {
- Value = value;
- Key = key;
- }
- }
- private Random random;
- protected BinaryKeyValueNode<Tkey, Tvalue> Root { get; set; }
- public int Count { get; protected set; }
- public BinarySearchTree()
- {
- Root = null;
- random = new Random(1);
- Count = 0;
- }
- public void Insert(Tkey key, Tvalue value)
- {
- BinaryKeyValueNode<Tkey, Tvalue> parent = null;
- BinaryKeyValueNode<Tkey, Tvalue> current = Root;
- int compare = 0;
- while (current != null)
- {
- parent = current;
- compare = current.Key.CompareTo(key);
- current = compare < 0 ? current.RightChild : current.LeftChild;
- }
- BinaryKeyValueNode<Tkey, Tvalue> newNode = new BinaryKeyValueNode<Tkey, Tvalue>(key, value);
- if (parent != null)
- if (compare < 0)
- parent.RightChild = newNode;
- else
- parent.LeftChild = newNode;
- else
- Root = newNode;
- newNode.Parent = parent;
- Count++;
- }
Finding a value, given its key, works very much the same as in the following:
- public Tvalue FindFirst(Tkey key)
- {
- return FindNode(key).Value;
- }
- public Tvalue FindFirstOrDefault(Tkey key)
- {
- var node=FindNode(key, false);
- return node == null ? default(Tvalue) : node.Value;
- }
- protected BinaryKeyValueNode<Tkey, Tvalue> FindNode(Tkey key, bool ExceptionIfKeyNotFound = true)
- {
- BinaryKeyValueNode<Tkey, Tvalue> current = Root;
- while (current != null)
- {
- int compare = current.Key.CompareTo(key);
- if (compare == 0)
- return current;
- if (compare < 0)
- current = current.RightChild;
- else
- current = current.LeftChild;
- }
- if (ExceptionIfKeyNotFound)
- throw new KeyNotFoundException();
- else
- return null;
- }
Starting from the root, we compare the given key to the key of the current node. If they are equal, we can return it. If the given key is strictly larger we continue the search in the right subtree, otherwise in the left. Next we have the delete operation:
- protected void DeleteNode(BinaryKeyValueNode<Tkey, Tvalue> node)
- {
- if (node == null)
- throw new ArgumentNullException();
- if (node.LeftChild != null && node.RightChild != null) //2 childs
- {
- BinaryKeyValueNode<Tkey, Tvalue> replaceBy = random.NextDouble() > .5 ? InOrderSuccesor(node) : InOrderPredecessor(node);
- DeleteNode(replaceBy);
- node.Value = replaceBy.Value;
- node.Key = replaceBy.Key;
- }
- else //1 or less childs
- {
- var child = node.LeftChild == null ? node.RightChild : node.LeftChild;
- if (node.Parent.RightChild == node)
- node.Parent.RightChild = child;
- else
- node.Parent.LeftChild = child;
- }
- Count--;
- }
- protected BinaryKeyValueNode<Tkey, Tvalue> InOrderSuccesor(BinaryKeyValueNode<Tkey, Tvalue> node)
- {
- BinaryKeyValueNode<Tkey, Tvalue> succesor = node.RightChild;
- while (succesor.LeftChild != null)
- succesor = succesor.LeftChild;
- return succesor;
- }
- protected BinaryKeyValueNode<Tkey, Tvalue> InOrderPredecessor(BinaryKeyValueNode<Tkey, Tvalue> node)
- {
- BinaryKeyValueNode<Tkey, Tvalue> succesor = node.LeftChild;
- while (succesor.RightChild != null)
- succesor = succesor.RightChild;
- return succesor;
We now have all the essential operations that must be done on a BST. If one would like to traverse the entire tree, to enumerate all its elements, then there are multiple ways to do so, namely depth-first or breadth-first. Here is the implementation to all 3 ways to make a depth-first tree traversal:
- public IEnumerable<Tvalue> TraverseTree(DepthFirstTraversalMethod method)
- {
- return TraverseNode(Root, method);
- }
- protected IEnumerable<Tvalue> TraverseNode(BinaryKeyValueNode<Tkey, Tvalue> node, DepthFirstTraversalMethod method)
- {
- IEnumerable<Tvalue> TraverseLeft = node.LeftChild == null ? new Tvalue[0] : TraverseNode(node.LeftChild, method),
- TraverseRight = node.RightChild == null ? new Tvalue[0] : TraverseNode(node.RightChild, method),
- Self = new Tvalue[1] { node.Value };
- switch(method)
- {
- case DepthFirstTraversalMethod.PreOrder:
- return Self.Concat(TraverseLeft).Concat(TraverseRight);
- case DepthFirstTraversalMethod.InOrder:
- return TraverseLeft.Concat(Self).Concat(TraverseRight);
- case DepthFirstTraversalMethod.PostOrder:
- return TraverseLeft.Concat(TraverseRight).Concat(Self);
- default:
- throw new ArgumentException();
- }
- }
- public enum DepthFirstTraversalMethod
- {
- PreOrder,
- InOrder,
- PostOrder
- }
On Time Complexity
The three basic operations, insert, find and delete, all have time complexity O(h) (linearly dependant), where h is the height of the tree. The height of the tree is bound by the 2-log of the count (lower bound) and the count (upper bound). The expected height of the tree is the square root of the count (that grows faster than the 2-log), after many random insertions and deletions. The performance of a BST can be improved uon by self-balancing; keeping the height small (2-log). I will implement a self-balancing BST in my next article soon.
Arpita BiswalPosted Oct 15, 2017, 10:19 AM
Where is the main method
sumit kumarPosted Mar 2, 2015, 5:10 AM
Good article
mayank prajapatiPosted Feb 10, 2015, 12:42 PM
nice article thanx
Vithal WadjePosted Feb 3, 2015, 9:49 AM
nice,thanks for sharing
Dinesh BeniwalPosted Jan 31, 2015, 4:19 AM
Thanks for sharing.
Sam HobbsPosted Jan 30, 2015, 6:47 PM
Welcome to C# Corner. Thank you for an interesting article. I wish had time to try the code.