Trees
By Notes Vandar
6.1 Concept and definitions
Trees are hierarchical data structures that consist of nodes connected by edges. They are widely used in computer science for various applications, such as databases, file systems, and artificial intelligence. Below are some fundamental concepts and definitions associated with trees.
Basic Concepts
- Node:
- The basic unit of a tree, which contains data and may link to other nodes. Each node can be seen as a separate entity.
- Root:
- The topmost node in a tree. It is the only node in the tree that has no parent. All other nodes are descendants of the root.
- Leaf Node:
- A node that does not have any children. It is located at the bottom of the tree.
- Edge:
- A connection between two nodes in the tree. It represents the relationship between the parent and child nodes.
- Parent and Child:
- A node that is one level higher than another node is called the parent node, and the node that is one level lower is called the child node.
- Subtree:
- A tree formed by a node and all of its descendants. Any node can be considered as the root of its own subtree.
- Height of a Tree:
- The length of the longest path from the root node to a leaf node. It can also be defined as the number of edges on the longest downward path from the root to a leaf.
- Depth of a Node:
- The length of the path from the root to that node. The depth of the root is zero, and it increases as you move down the tree.
- Degree of a Node:
- The number of children a node has. A leaf node has a degree of zero.
- Level of a Node:
- The level of a node is defined as the number of edges in the path from the root to that node. The root node is at level 0.
Types of Trees
- Binary Tree:
- A tree where each node has at most two children, referred to as the left child and the right child.
- Binary Search Tree (BST):
- A binary tree where for each node, all the values in the left subtree are less than the node’s value, and all the values in the right subtree are greater.
- Balanced Trees:
- Trees that maintain a balanced height to ensure efficient operations. Examples include AVL trees and Red-Black trees.
- N-ary Tree:
- A tree where a node can have at most N children.
- Trie:
- A specialized tree used for storing strings, where each node represents a character of a string.
Common Operations on Trees
- Insertion: Adding a new node to the tree while maintaining the tree structure.
- Deletion: Removing a node from the tree and restructuring it as necessary.
- Traversal: Visiting all nodes in a specific order. Common traversal methods include:
- Inorder Traversal: Left, Root, Right (used in BSTs to get sorted order).
- Preorder Traversal: Root, Left, Right.
- Postorder Traversal: Left, Right, Root.
- Level Order Traversal: Visiting nodes level by level.
Applications of Trees
- Hierarchical Data Representation: Such as file systems, organizational structures, etc.
- Searching and Sorting: Data structures like binary search trees and heaps are used for efficient searching and sorting.
- Databases: B-trees and their variations are used in database indexing.
- Networking: Used in routing algorithms and to represent network topologies.
6.2 Basic operations in binary tree
A binary tree is a tree data structure in which each node has at most two children, referred to as the left child and the right child. Basic operations performed on a binary tree include insertion, deletion, traversal, searching, and various utility functions. Here’s a detailed look at these operations:
1. Insertion
Inserting a new node into a binary tree can be done in various ways, depending on the type of binary tree (e.g., complete binary tree, binary search tree).
- Inserting into a Binary Search Tree (BST):
- Start at the root.
- If the tree is empty, the new node becomes the root.
- If the new value is less than the current node, traverse to the left child.
- If the new value is greater than or equal to the current node, traverse to the right child.
- Repeat this process until an appropriate null position is found.
if (root == NULL) {
return createNode(data);
}
if (data < root->data) {
root->left = insert(root->left, data);
} else {
root->right = insert(root->right, data);
}
return root;
}
2. Deletion
Deleting a node from a binary tree involves three main cases:
- Node with no children: Simply remove the node.
- Node with one child: Remove the node and connect its parent to its child.
- Node with two children: Find the inorder successor (smallest node in the right subtree) or inorder predecessor (largest node in the left subtree), replace the node’s value with it, and then delete the successor/predecessor.
struct Node* deleteNode(struct Node* root, int key) {
if (root == NULL) return root;
if (key < root->data) {
root->left = deleteNode(root->left, key);
} else if (key > root->data) {
root->right = deleteNode(root->right, key);
} else {
// Node with one child or no child
if (root->left == NULL) {
struct Node* temp = root->right;
free(root);
return temp;
} else if (root->right == NULL) {
struct Node* temp = root->left;
free(root);
return temp;
}
// Node with two children: Get the inorder successor
struct Node* temp = minValueNode(root->right);
root->data = temp->data; // Copy the inorder successor’s content to this node
root->right = deleteNode(root->right, temp->data); // Delete the inorder successor
}
return root;
}
3. Traversal
Traversal refers to the process of visiting all the nodes in a binary tree. Common traversal methods include:
- Inorder Traversal: Visit left child, then the node itself, then the right child.
- For a binary search tree, this results in sorted order.
if (root != NULL) {
inorderTraversal(root->left);
printf(“%d “, root->data);
inorderTraversal(root->right);
}
}
- Preorder Traversal: Visit the node first, then the left child, then the right child.
if (root != NULL) {
printf(“%d “, root->data);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
}
- Postorder Traversal: Visit the left child, then the right child, then the node itself.
if (root != NULL) {
postorderTraversal(root->left);
postorderTraversal(root->right);
printf(“%d “, root->data);
}
}
- Level Order Traversal: Visit all nodes at the present depth level before moving on to nodes at the next depth level. This can be implemented using a queue.
if (root == NULL) return;
struct Queue* q = createQueue(); // Assuming a queue implementation
enqueue(q, root);
while (!isEmpty(q)) {
struct Node* current = dequeue(q);
printf(“%d “, current->data);
if (current->left) enqueue(q, current->left);
if (current->right) enqueue(q, current->right);
}
}
4. Searching
Searching for a node in a binary search tree can be done efficiently by comparing the target value with the current node’s value:
if (root == NULL || root->data == key) {
return root;
}
if (key < root->data) {
return search(root->left, key);
}
return search(root->right, key);
}
5. Utility Functions
- Finding Minimum and Maximum:
- To find the minimum value in a binary search tree, traverse to the leftmost node.
- To find the maximum value, traverse to the rightmost node.
struct Node* minValueNode(struct Node* node) {
struct Node* current = node;
while (current && current->left != NULL) {
current = current->left;
}
return current;
}
struct Node* maxValueNode(struct Node* node) {
struct Node* current = node;
while (current && current->right != NULL) {
current = current->right;
}
return current;
}
6.3 Tree height, level and depth
Understanding the concepts of height, level, and depth in a tree is essential for analyzing its structure and performance characteristics. Below is a detailed explanation of each term:
1. Tree Height
- Definition: The height of a tree is the length of the longest path from the root node to a leaf node. It is measured by the number of edges on the longest downward path from the root to a leaf.
- Calculation:
- If a tree has no nodes (an empty tree), its height is typically defined as -1.
- If the tree has only one node (the root), its height is 0.
- For any other node, the height is calculated as: Height=1+max(Height of left subtree,Height of right subtree)\text{Height} = 1 + \max(\text{Height of left subtree}, \text{Height of right subtree})
- Example:
- For the following tree:
A
/ \
B C
/ \
D E - The height of this tree is 2, as the longest path from the root (A) to a leaf (D or E) contains 2 edges.
- For the following tree:
2. Level
- Definition: The level of a node is defined as the number of edges in the path from the root node to that particular node. The root node is at level 0, its children are at level 1, and so on.
- Calculation:
- The level of a node can be determined based on its depth.
- The level can be calculated as: Level=Depth of the node\text{Level} = \text{Depth of the node}
- Example:
- In the previous tree:
A (Level 0)
/ \
B C (Level 1)
/ \
D E (Level 2) - Here, the node A is at level 0, nodes B and C are at level 1, and nodes D and E are at level 2.
- In the previous tree:
3. Depth
- Definition: The depth of a node is the number of edges from the root node to that node. It measures how far down the tree the node is located.
- Calculation:
- The depth of a node can be calculated by counting the number of edges from the root to that node. Alternatively, it can also be defined in terms of levels: Depth=Level of the node\text{Depth} = \text{Level of the node}
- Example:
- Using the same tree:
A
/ \
B C
/ \
D E - The depth of:
- Node A is 0 (0 edges from root).
- Nodes B and C are 1 (1 edge from root).
- Nodes D and E are 2 (2 edges from root).
- Using the same tree:
6.4 Binary Search Tree
A Binary Search Tree (BST) is a specialized type of binary tree that maintains a sorted order of its elements. It allows for efficient searching, insertion, and deletion operations, making it a widely used data structure in computer science.
Properties of Binary Search Tree
- Node Structure:
- Each node contains a key (value), a left child, and a right child.
- Ordering Property:
- For any given node:
- All values in the left subtree are less than the node’s value.
- All values in the right subtree are greater than the node’s value.
- For any given node:
- No Duplicate Nodes:
- A standard BST does not allow duplicate values. Each value must be unique.
Operations on Binary Search Tree
- Insertion:
- To insert a new value into the BST:
- Start at the root and compare the value to be inserted with the current node’s value.
- If the new value is less than the current node’s value, move to the left child; if it’s greater, move to the right child.
- Repeat this process until a null position is found, then insert the new node.
C Code for Insertion:
struct Node {
int data;
struct Node* left;
struct Node* right;
};struct Node* insert(struct Node* root, int data) {
if (root == NULL) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
if (data < root->data) {
root->left = insert(root->left, data);
} else {
root->right = insert(root->right, data);
}
return root;
} - To insert a new value into the BST:
- Searching:
- To search for a value in the BST:
- Start at the root and compare the target value with the current node’s value.
- If the target value matches, return the node.
- If it’s less, move to the left child; if greater, move to the right child.
- Repeat this until the value is found or a null position is reached.
C Code for Searching:
struct Node* search(struct Node* root, int key) {
if (root == NULL || root->data == key) {
return root;
}
if (key < root->data) {
return search(root->left, key);
}
return search(root->right, key);
} - To search for a value in the BST:
- Deletion:
- To delete a node from the BST, there are three main cases:
- Node with no children: Simply remove the node.
- Node with one child: Remove the node and link its parent to its child.
- Node with two children: Find the inorder successor (smallest in the right subtree) or inorder predecessor (largest in the left subtree), replace the node’s value with that of the successor/predecessor, and delete the successor/predecessor.
C Code for Deletion:
struct Node* minValueNode(struct Node* node) {
struct Node* current = node;
while (current && current->left != NULL) {
current = current->left;
}
return current;
}struct Node* deleteNode(struct Node* root, int key) {
if (root == NULL) return root;if (key < root->data) {
root->left = deleteNode(root->left, key);
} else if (key > root->data) {
root->right = deleteNode(root->right, key);
} else {
// Node with one child or no child
if (root->left == NULL) {
struct Node* temp = root->right;
free(root);
return temp;
} else if (root->right == NULL) {
struct Node* temp = root->left;
free(root);
return temp;
}// Node with two children: Get the inorder successor
struct Node* temp = minValueNode(root->right);
root->data = temp->data; // Copy the inorder successor’s content to this node
root->right = deleteNode(root->right, temp->data); // Delete the inorder successor
}
return root;
} - To delete a node from the BST, there are three main cases:
- Traversal:
- Common traversal methods include:
- Inorder Traversal: Visits nodes in sorted order (Left, Root, Right).
- Preorder Traversal: Useful for creating a copy of the tree (Root, Left, Right).
- Postorder Traversal: Useful for deleting a tree (Left, Right, Root).
C Code for Inorder Traversal:
void inorderTraversal(struct Node* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf(“%d “, root->data);
inorderTraversal(root->right);
}
} - Common traversal methods include:
Advantages of Binary Search Tree
- Efficiency: Operations like search, insert, and delete can be performed in O(log n) time on average if the tree is balanced.
- Sorted Data: Inorder traversal of a BST yields sorted data.
Disadvantages of Binary Search Tree
- Unbalanced Trees: In the worst case (e.g., inserting sorted data), the tree can become unbalanced, leading to O(n) time complexity for operations.
- Complexity of Balancing: Maintaining balance (e.g., using AVL trees or Red-Black trees) adds complexity to implementation.
6.5 Insertion, Deletion, Traversals (pre-order, post-order and in-order ), Search in BST
In this section, we will discuss the key operations of a Binary Search Tree (BST), including insertion, deletion, traversals (pre-order, post-order, and in-order), and search. Each operation is explained with accompanying C code examples.
1. Insertion in BST
Insertion in a BST involves placing a new node in the correct position according to the BST properties.
C Code for Insertion:
#include <stdio.h>
#include <stdlib.h>
// Definition of a node in the BST
struct Node {
int data;
struct Node* left;
struct Node* right;
};
// Function to create a new node
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// Function to insert a new node into the BST
struct Node* insert(struct Node* root, int data) {
if (root == NULL) {
return createNode(data);
}
if (data < root->data) {
root->left = insert(root->left, data);
} else {
root->right = insert(root->right, data);
}
return root;
}
2. Deletion in BST
Deletion from a BST can be handled in three cases:
- Node with no children (leaf node)
- Node with one child
- Node with two children
C Code for Deletion:
// Function to find the minimum value node in a BST
struct Node* minValueNode(struct Node* node) {
struct Node* current = node;
while (current && current->left != NULL) {
current = current->left;
}
return current;
}
// Function to delete a node from the BST
struct Node* deleteNode(struct Node* root, int key) {
if (root == NULL) return root;
if (key < root->data) {
root->left = deleteNode(root->left, key);
} else if (key > root->data) {
root->right = deleteNode(root->right, key);
} else {
// Node with one child or no child
if (root->left == NULL) {
struct Node* temp = root->right;
free(root);
return temp;
} else if (root->right == NULL) {
struct Node* temp = root->left;
free(root);
return temp;
}
// Node with two children: Get the inorder successor
struct Node* temp = minValueNode(root->right);
root->data = temp->data; // Copy the inorder successor’s content to this node
root->right = deleteNode(root->right, temp->data); // Delete the inorder successor
}
return root;
}
3. Traversals in BST
Traversing a BST can be done in different orders: Pre-order, In-order, and Post-order.
- In-order Traversal: Left, Root, Right (produces sorted order).
- Pre-order Traversal: Root, Left, Right.
- Post-order Traversal: Left, Right, Root.
C Code for Traversals:
// In-order Traversal
void inorderTraversal(struct Node* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf(“%d “, root->data);
inorderTraversal(root->right);
}
}
// Pre-order Traversal
void preorderTraversal(struct Node* root) {
if (root != NULL) {
printf(“%d “, root->data);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
}
// Post-order Traversal
void postorderTraversal(struct Node* root) {
if (root != NULL) {
postorderTraversal(root->left);
postorderTraversal(root->right);
printf(“%d “, root->data);
}
}
4. Search in BST
Searching for a value in a BST involves comparing the target value to the current node’s value and recursively traversing left or right based on the comparison.
C Code for Search:
// Function to search for a node with a given key
struct Node* search(struct Node* root, int key) {
if (root == NULL || root->data == key) {
return root;
}
if (key < root->data) {
return search(root->left, key);
}
return search(root->right, key);
}
Example Usage
Here’s how to use the above functions in a complete program:
#include <stdio.h>
#include <stdlib.h>
// Node structure and function declarations (as shown above)
// Main function
int main() {
struct Node* root = NULL;
// Inserting values into the BST
root = insert(root, 50);
insert(root, 30);
insert(root, 20);
insert(root, 40);
insert(root, 70);
insert(root, 60);
insert(root, 80);
// Traversals
printf(“In-order Traversal: “);
inorderTraversal(root);
printf(“\n”);
printf(“Pre-order Traversal: “);
preorderTraversal(root);
printf(“\n”);
printf(“Post-order Traversal: “);
postorderTraversal(root);
printf(“\n”);
// Searching for a value
int key = 40;
struct Node* foundNode = search(root, key);
if (foundNode != NULL) {
printf(“Node with key %d found.\n”, key);
} else {
printf(“Node with key %d not found.\n”, key);
}
// Deleting a node
printf(“Deleting node with key 20.\n”);
root = deleteNode(root, 20);
printf(“In-order Traversal after deletion: “);
inorderTraversal(root);
printf(“\n”);
return 0;
}
6.6 AVL tree and Balancing algorithm
An AVL tree is a self-balancing Binary Search Tree (BST) where the difference in heights between the left and right subtrees of any node (the balance factor) is at most 1. This property ensures that the tree remains approximately balanced, leading to O(log n) time complexity for insertion, deletion, and search operations.
1. Properties of AVL Trees
- Height Balance Property: For every node in the AVL tree, the balance factor is defined as:
Balance Factor=Height of left subtree−Height of right subtree\text{Balance Factor} = \text{Height of left subtree} – \text{Height of right subtree}The balance factor must be one of -1, 0, or +1 for the tree to be considered balanced.
- Height: The height of an AVL tree is always O(log n), where n is the number of nodes.
2. Rotations
To maintain the balance of an AVL tree after insertions or deletions, rotations are performed. There are four types of rotations:
- Right Rotation:
- Used when a node becomes unbalanced due to an insertion in the left subtree of its left child (Left-Left case).
- Before Rotation:
y
/
x
/
z- After Right Rotation:
x
/ \
z y - Left Rotation:
- Used when a node becomes unbalanced due to an insertion in the right subtree of its right child (Right-Right case).
- Before Rotation:
x
\
y
\
z- After Left Rotation:
y
/ \
x z - Left-Right Rotation:
- Used when a node becomes unbalanced due to an insertion in the right subtree of its left child (Left-Right case).
- Before Rotation:
z
/
x
\
y- After Left-Right Rotation:
y
/ \
x z - Right-Left Rotation:
- Used when a node becomes unbalanced due to an insertion in the left subtree of its right child (Right-Left case).
- Before Rotation:
x
\
z
/
y- After Right-Left Rotation:
y
/ \
x z
3. Insertion in AVL Tree
Insertion in an AVL tree is similar to insertion in a regular BST, followed by the adjustment of the tree through rotations to maintain the balance property.
C Code for Insertion:
#include <stdio.h>
#include <stdlib.h>
// Definition of a node in the AVL tree
struct Node {
int data;
struct Node* left;
struct Node* right;
int height;
};
// Function to get the height of a node
int getHeight(struct Node* node) {
if (node == NULL) return 0;
return node->height;
}
// Function to get the balance factor of a node
int getBalanceFactor(struct Node* node) {
if (node == NULL) return 0;
return getHeight(node->left) – getHeight(node->right);
}
// Function to create a new node
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
newNode->height = 1; // New node is initially added at leaf
return newNode;
}
// Right rotation
struct Node* rightRotate(struct Node* y) {
struct Node* x = y->left;
struct Node* T2 = x->right;
// Perform rotation
x->right = y;
y->left = T2;
// Update heights
y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));
x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));
return x; // Return new root
}
// Left rotation
struct Node* leftRotate(struct Node* x) {
struct Node* y = x->right;
struct Node* T2 = y->left;
// Perform rotation
y->left = x;
x->right = T2;
// Update heights
x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));
y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));
return y; // Return new root
}
// Function to insert a node into the AVL tree
struct Node* insert(struct Node* root, int data) {
// 1. Perform the normal BST insert
if (root == NULL) return createNode(data);
if (data < root->data) {
root->left = insert(root->left, data);
} else if (data > root->data) {
root->right = insert(root->right, data);
} else {
return root; // Duplicate keys are not allowed
}
// 2. Update height of this ancestor node
root->height = 1 + (getHeight(root->left) > getHeight(root->right) ? getHeight(root->left) : getHeight(root->right));
// 3. Get the balance factor of this ancestor node to check whether this node became unbalanced
int balanceFactor = getBalanceFactor(root);
// If this node becomes unbalanced, then there are 4 cases
// Left Left Case
if (balanceFactor > 1 && data < root->left->data) {
return rightRotate(root);
}
// Right Right Case
if (balanceFactor < -1 && data > root->right->data) {
return leftRotate(root);
}
// Left Right Case
if (balanceFactor > 1 && data > root->left->data) {
root->left = leftRotate(root->left);
return rightRotate(root);
}
// Right Left Case
if (balanceFactor < -1 && data < root->right->data) {
root->right = rightRotate(root->right);
return leftRotate(root);
}
// Return the (unchanged) node pointer
return root;
}
4. Deletion in AVL Tree
Deletion in an AVL tree is similar to deletion in a regular BST, followed by the adjustment of the tree through rotations to maintain the balance property.
C Code for Deletion:
// Function to delete a node from the AVL tree
struct Node* deleteNode(struct Node* root, int data) {
// STEP 1: PERFORM STANDARD BST DELETE
if (root == NULL) return root;
if (data < root->data) {
root->left = deleteNode(root->left, data);
} else if (data > root->data) {
root->right = deleteNode(root->right, data);
} else {
// Node with one child or no child
if ((root->left == NULL) || (root->right == NULL)) {
struct Node* temp = root->left ? root->left : root->right;
// No child case
if (temp == NULL) {
temp = root;
root = NULL;
} else { // One child case
*root = *temp; // Copy the contents of the non-empty child
}
free(temp);
} else {
// Node with two children: Get the inorder successor (smallest in the right subtree)
struct Node* temp = minValueNode(root->right);
root->data = temp->data; // Copy the inorder successor’s data to this node
root->right = deleteNode(root->right, temp->data); // Delete the inorder successor
}
}
// If the tree had only one node then return
if (root == NULL) return root;
// STEP 2: UPDATE HEIGHT OF THE CURRENT NODE
root->height = 1 + (getHeight(root->left) > getHeight(root->right) ? getHeight(root->left) : getHeight(root->right));
// STEP 3: GET THE BALANCE FACTOR OF THIS NODE (to check whether this node became unbalanced)
int balance = getBalanceFactor(root);
// If this node becomes unbalanced, then there are 4 cases
// Left Left Case
if (balance > 1 && getBalanceFactor(root->left) >= 0) {
return rightRotate(root);
}
// Left Right Case
if (balance > 1 && getBalanceFactor(root->left) < 0) {
root->left = leftRotate(root->left);
return rightRotate(root);
}
// Right Right Case
if (balance < -1 && getBalanceFactor(root->right) <= 0) {
return leftRotate(root);
}
// Right Left Case
6.7 Applications of tree
Trees are fundamental data structures in computer science, providing a hierarchical representation of data. They are widely used in various applications due to their efficient management of sorted data, quick search, insert, and delete operations, and their ability to represent relationships naturally. Here are some key applications of trees:
1. Binary Search Trees (BSTs)
- Searching: BSTs allow for efficient searching of elements in O(log n) time complexity.
- Dynamic Sets: They can be used to maintain a dynamic set of elements, allowing for quick insertion and deletion.
2. AVL Trees
- Self-Balancing: AVL trees are a type of BST that maintains balance, ensuring O(log n) time complexity for operations. They are used in scenarios requiring frequent insertions and deletions while maintaining sorted order.
3. Red-Black Trees
- Balanced Trees: Like AVL trees, red-black trees maintain balance but with different properties. They are used in the implementation of associative arrays, such as in the C++ Standard Template Library (STL) map and set.
4. B-Trees and B+ Trees
- Database Systems: B-trees are used in database indexing and file systems due to their ability to maintain sorted data and allow searches, sequential access, insertions, and deletions in logarithmic time. B+ trees are often preferred as they store all values at the leaf level and provide efficient range queries.
5. Trie (Prefix Tree)
- String Searching: Tries are used for storing dynamic sets of strings, allowing for fast retrieval and prefix matching. They are commonly used in applications like autocomplete systems and spell checkers.
6. Segment Trees
- Range Queries: Segment trees are used for answering range queries on arrays, such as range sum and range minimum queries, efficiently. They allow for quick updates and queries in logarithmic time.
7. Fenwick Tree (Binary Indexed Tree)
- Cumulative Frequency Tables: Fenwick trees are used for maintaining prefix sums and allowing updates to the array efficiently, particularly useful in computational geometry and data analysis.
8. Heap
- Priority Queues: Heaps are binary trees that maintain a specific order (max-heap or min-heap) and are used to implement priority queues. They are widely used in algorithms like Dijkstra’s for shortest path and heapsort for sorting.
9. Expression Trees
- Mathematical Expressions: Expression trees represent expressions where internal nodes are operators and leaf nodes are operands. They are used in compilers and interpreters for parsing and evaluating expressions.
10. Decision Trees
- Machine Learning: Decision trees are used in classification and regression tasks. They model decisions based on feature values and are interpretable models that can be visualized.
11. File Systems
- Hierarchical Data Storage: File systems use tree structures to represent directories and files hierarchically, enabling efficient file storage, retrieval, and organization.
12. XML and JSON Parsing
- Hierarchical Data Representation: Trees are used to represent hierarchical data structures such as XML and JSON, facilitating easy traversal and manipulation of data.
13. Network Routing Algorithms
- Routing Tables: Trees are used in network routing algorithms to maintain routing tables and efficiently determine the shortest paths.