Binary Search Tree
Data Structures: Binary Search Tree Data Structure
The Definition of a Binary Search Tree
A Binary Search Tree (BST) is a binary tree in which each node contains only one value and has two child nodes, where:
- The value in the left child node is less than the value in the parent node.
- The value in the right child node is greater than the value in the parent node.
Binary Search Tree Creation Instructions
Define Node Structure:
- Create a class called
Node
that will represent an individual node in the BST. - This class should have three properties:
value
: to store the value of the node.left
: to store a reference to the left child node.right
: to store a reference to the right child node.
- Create a class called
Define Binary Search Tree Structure:
- Create a class called
BinarySearchTree
. - Initialize a property
root
within the class set tonull
. This will represent the root node of the BST.
- Create a class called
Add Methods to the BinarySearchTree:
insert(value):
- This method adds a new value to the BST.
- Create a new
Node
with the given value. - If the BST is empty (i.e.,
root
isnull
), assign this new node to theroot
. - Otherwise, use a private helper method, typically called
_insertNode(root, newNode)
, to find the correct location to insert the new node.
search(value):
- This method checks if a value exists in the BST.
- If the BST is empty, return
false
. - Otherwise, use a private helper method, typically called
_searchNode(root, value)
, to check if the value exists.
remove(value):
- This method removes a value from the BST.
- If the BST is empty, return
null
or a message indicating the value doesn't exist. - Use a private helper method, typically called
_removeNode(root, value)
, to locate the node and remove it.
inOrderTraversal(callback):
- This method traverses the BST in in-order fashion (left, root, right) and applies a callback function on each node.
- Use a private helper method, typically called
_inOrderTraversal(node, callback)
, to perform the traversal.
preOrderTraversal(callback), postOrderTraversal(callback):
- Similarly, add methods for pre-order (root, left, right) and post-order (left, right, root) traversals.
findMinNode() and findMaxNode():
- These methods return the nodes with the minimum and maximum values, respectively.
Consider Adding Additional Utility Methods (Optional):
- Depending on your needs, you might add methods to find the height of the tree, find the depth of a particular node, check if the BST is balanced, etc.
Remember BST Properties:
- Ensure that the BST properties are maintained throughout. When inserting, the value should go to the left if it's less than the current node and to the right if it's greater.
Balancing (Advanced):
- A vanilla BST can become skewed or unbalanced, which can deteriorate its performance. Advanced versions like AVL trees or Red-Black trees can automatically balance themselves.
By following these instructions, you can create a basic Binary Search Tree in a structured and organized manner.
Code Example
Here's a simple implementation of a Binary Search Tree (BST) in JavaScript:
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(value) {
let newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
} else {
this._insertNode(this.root, newNode);
}
}
_insertNode(node, newNode) {
if (newNode.value < node.value) {
if (node.left === null) {
node.left = newNode;
} else {
this._insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this._insertNode(node.right, newNode);
}
}
}
search(value) {
return this._searchNode(this.root, value);
}
_searchNode(node, value) {
if (node === null) {
return false;
}
if (value < node.value) {
return this._searchNode(node.left, value);
} else if (value > node.value) {
return this._searchNode(node.right, value);
} else {
return true; // value is equal to node.value
}
}
// Additional methods like remove, inOrderTraversal, etc., can be added
// For demonstration, let's add inOrderTraversal:
inOrderTraversal(callback) {
this._inOrderTraversal(this.root, callback);
}
_inOrderTraversal(node, callback) {
if (node !== null) {
this._inOrderTraversal(node.left, callback);
callback(node.value);
this._inOrderTraversal(node.right, callback);
}
}
}
// Usage:
const bst = new BinarySearchTree();
bst.insert(5);
bst.insert(3);
bst.insert(8);
bst.insert(1);
bst.insert(4);
bst.insert(7);
bst.insert(9);
console.log(bst.search(4)); // true
console.log(bst.search(10)); // false
bst.inOrderTraversal(value => console.log(value)); // 1, 3, 4, 5, 7, 8, 9
This is a basic implementation and doesn't include all BST functionalities. It serves as a foundation upon which other functionalities (like remove
, findMinNode
, etc.) can be added.
Complexity
Here's a table that outlines the time and space complexities for common operations performed on a basic Binary Search Tree (BST). It's worth noting that these complexities can vary depending on the balance of the tree:
Operation | Average Time Complexity | Worst Case Time Complexity | Space Complexity |
---|---|---|---|
Insertion | O(log n) | O(n) | O(log n) |
Deletion | O(log n) | O(n) | O(log n) |
Search | O(log n) | O(n) | O(log n) |
Minimum/Maximum | O(log n) | O(n) | O(log n) |
Predecessor/Successor | O(log n) | O(n) | O(log n) |
Notes:
Insertion, Deletion, and Search: For a balanced BST (e.g., AVL, Red-Black Tree), the time complexities for these operations are O(log n). However, for an unbalanced BST (e.g., when items are inserted in sorted order), these operations can degrade to O(n).
Minimum/Maximum: For a balanced tree, finding the minimum or maximum takes O(log n) since it involves traversing down the height of the tree. In an unbalanced tree, this operation could take O(n) in the worst case.
Predecessor/Successor: Similar to the above, finding the predecessor or successor in a balanced BST is O(log n) on average. In the worst case (unbalanced tree), it can take O(n).
For Space Complexity, each operation, especially those involving recursion or a call stack (like search or insertion), will take up space proportional to the height of the tree. In a balanced BST, this is O(log n). In an unbalanced tree, it can be O(n).
It's essential to understand that these complexities, especially the worst-case scenarios, highlight the importance of keeping a BST balanced. Self-balancing trees like AVL or Red-Black Trees address this by ensuring the tree remains approximately balanced after every insert or delete operation.