Read more from wikipedia: http://en.wikipedia.org/wiki/Binary_search_tree
1. what is it?
a node-based binary tree data structure which has the following properties:
- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than the node's key.
- The left and right subtree must each also be a binary search tree.
- There must be no duplicate nodes.
2. Time complexity:
Time complexity in big O notation | ||
---|---|---|
Average | Worst case | |
Space | O(n) | O(n) |
Search | O(log n) | O(n) |
Insert | O(log n) | O(n) |
Delete | O(log n) | O(n) |
3. Operations:
1) search algorithm in pseudocode:
iterative version, finds a BST node:
algorithm Find(key, root): current-node := root while current-node is not Nil do if current-node.key = key then return current-node else if key < current-node.key then current-node := current-node.left else current-node := current-node.right
The following recursive version is equivalent:
algorithm Find-recursive(key, node): // call initially with node = root if node = Nil or node.key = key then node else if key < node.key then Find-recursive(key, node.left) else Find-recursive(key, node.right)
2) a typical BST insertion might be performed in C++:
void insert(int value) { if(root == NULL) root = new Node(value); else insertHelper(root, value); } void insertHelper(Node* node, int value) { if(value < node->key) { if(node->leftChild == NULL) node->leftChild = new Node(value); else insertHelper(node->leftChild, value); } else { if(node->rightChild == NULL) node->rightChild = new Node(value); else insertHelper(node->rightChild, value); } }
an iterative approach to inserting into a binary search tree in Java:
private Node m_root; public void insert(int data) { if (m_root == null) { m_root = new TreeNode(data, null, null); return; } Node root = m_root; while (root != null) { // Choose not add 'data' if already present (an implementation decision) if (data == root.getData()) { return; } else if (data < root.getData()) { // insert left if (root.getLeft() == null) { root.setLeft(new TreeNode(data, null, null)); return; } else { root = root.getLeft(); } } else { // insert right if (root.getRight() == null) { root.setRight(new TreeNode(data, null, null)); return; } else { root = root.getRight(); } } } }
Below is a recursive approach to the insertion method.
private Node m_root; public void insert(int data){ if (m_root == null) { m_root = new TreeNode(data, null, null); } else { internalInsert(m_root, data); } } private static void internalInsert(Node node, int data){ // Choose not add 'data' if already present (an implementation decision) if (data == node.getKey()) { return; } else if (data < node.getKey()) { if (node.getLeft() == null) { node.setLeft(new TreeNode(data, null, null)); } else { internalInsert(node.getLeft(), data); } } else { if (node.getRight() == null) { node.setRight(new TreeNode(data, null, null)); } else { internalInsert(node.getRight(), data); } } }
3) Deletion: 3 types:
- Deleting a leaf (node with no children): Deleting a leaf is easy, as we can simply remove it from the tree.
- Deleting a node with one child: Remove the node and replace it with its child.
- Deleting a node with two children: Call the node to be deleted N. Do not delete N. Instead, choose either its in-order successor node or its in-order predecessor node, R. Replace the value of N with the value of R, then delete R.
code in C Sharp:
private static Node Search(Node tree, double data, out Node parent) { // empty tree if (tree == null) { parent = null; return null; } // node to find is the tree root if (data == tree.data) { parent = null; return tree; } // peek left if (tree.left != null && data == tree.left.data) { parent = tree; return tree.left; } // peek right if (tree.right != null && data == tree.right.data) { parent = tree; return tree.right; } if (data < tree.data) return Search(tree.left, data, out parent); if (data > tree.data) return Search(tree.right, data, out parent); // unreacheable code parent = null; return null; } public static Node Smallest(Node tree, out Node parent) { if (tree == null) { parent = null; return null; } if (tree.left == null) { parent = null; return tree; } if (tree.left.left == null) { parent = tree; return tree.left; } return Smallest(tree.left, out parent); } public static void Remove(ref Node tree, double data) { Node NodeToRemove, NodeToMove, parent; // Find node to remove NodeToRemove = Search(tree, data, out parent); // if the value is not found, return if (NodeToRemove == null) return; // Deleting a leaf or a node with one child: // Remove the node and replace it with the child (or null if it has none) if (NodeToRemove.left == null || NodeToRemove.right == null) { // Get a reference to the child Node child; if (NodeToRemove.left == null) child = NodeToRemove.right; else child = NodeToRemove.left; // if the node is the root of the tree if (parent == null) tree = child; // if the node has a parent else { if (NodeToRemove.data > parent.data) parent.right = child; else parent.left = child; } return; } // Deleting a node with two children: // Call the node to be deleted N. Do not delete N. // Instead, choose either its in-order successor node or its in-order predecessor node, R. // Replace the value of N with the value of R, then delete R. NodeToMove = Smallest(NodeToRemove.right, out parent); // replace the value NodeToRemove.data = NodeToMove.data; // Remove the bottom node Remove(ref NodeToMove, NodeToMove.data); if (parent == null) NodeToRemove.right = NodeToMove; else parent.left = NodeToMove; }
code in C++.
template <typename T> bool BST<T>::remove(const T & itemToDelete) { return remove(root, itemToDelete); } template <typename T> bool BST<T>::remove(Node<T>* & ptr, const T& key) //helper remove function { if (ptr==nullptr) return false; // item not in BST if (key < ptr->data) remove(ptr->LeftChild, key); else if (key > ptr->data) remove(ptr->RightChild, key); else { Node<T> *temp; if (ptr->LeftChild==nullptr) { temp = ptr->RightChild; delete ptr; ptr = temp; } else if (ptr->RightChild==nullptr) { temp = ptr->LeftChild; delete ptr; ptr = temp; } else //2 children { temp = ptr->RightChild; Node<T> *parent = nullptr; while(temp->LeftChild!=nullptr) { parent = temp; temp = temp->LeftChild; } ptr->data = temp->data; if (parent != nullptr) remove(parent->LeftChild, parent->LeftChild->data); else remove(ptr->RightChild, ptr->RightChild->data); } } }
4) Traversal
An in-order traversal of a binary search tree will always result in a sorted list of node items (numbers, strings or other comparable items).
An in-order traversal algorithm for C is given below.
void in_order_traversal(struct Node *n, void (*cb)(void*)) { struct Node *cur, *pre; if(!n) return; cur = n; while(cur) { if(!cur->left) { cb(cur->val); cur= cur->right; } else { pre = cur->left; while(pre->right && pre->right != cur) pre = pre->right; if (!pre->right) { pre->right = cur; cur = cur->left; } else { pre->right = NULL; cb(cur->val); cur = cur->right; } } } }
5) Sort: Similar to heapsort, we insert all the values we wish to sort into a new ordered data structure—in this case a binary search tree—and then traverse it in order, building our result.