Author: admin

  • Approximation Algorithms

    Approximation Algorithms

    Approximation algorithms are algorithms designed to solve problems that are not solvable in polynomial time for approximate solutions. These problems are known as NP complete problems. These problems are significantly effective to solve real world problems, therefore, it becomes important to solve them using a different approach.

    NP complete problems can still be solved in three cases: the input could be so small that the execution time is reduced, some problems can still be classified into problems that can be solved in polynomial time, or use approximation algorithms to find near-optima solutions for the problems.

    This leads to the concept of performance ratios of an approximation problem.

    Performance Ratios

    The main idea behind calculating the performance ratio of an approximation algorithm, which is also called as an approximation ratio, is to find how close the approximate solution is to the optimal solution.

    The approximate ratio is represented using (n) where n is the input size of the algorithm, C is the near-optimal solution obtained by the algorithm, C* is the optimal solution for the problem. The algorithm has an approximate ratio of (n) if and only if −

    max{CC∗,C∗C}≤ρ(n)

    The algorithm is then called a (n)-approximation algorithm. Approximation Algorithms can be applied on two types of optimization problems: minimization problems and maximization problems. If the optimal solution of the problem is to find the maximum cost, the problem is known as the maximization problem; and if the optimal solution of the problem is to find the minimum cost, then the problem is known as a minimization problem.

    For maximization problems, the approximation ratio is calculated by C*/C since 0 C C*. For minimization problems, the approximation ratio is calculated by C/C* since 0 C* C.

    Assuming that the costs of approximation algorithms are all positive, the performance ratio is well defined and will not be less than 1. If the value is 1, that means the approximate algorithm generates the exact optimal solution.

    Examples

    Few popular examples of the approximation algorithms are −

    • Vertex Cover Algorithm
    • Set Cover Problem
    • Travelling Salesman Problem (Approximation Approach)
    • The Subset Sum Problem
  • Bloom Filters

    A Bloom filter is defined as a data structure designed to identify of a elements presence in a set in a rapid and memory efficient manner.

    You can think of it as a probabilistic data structure. This data structure helps us to identify that an element is either present or absent in a set. It is not used to store the actual data, but to check whether the data is present or not.

    It is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set. False positive matches are possible, but false negatives are not. In other words, a query returns either “possibly in set” or “definitely not in set”.

    How Bloom Filter Works?

    Let’s understand how a Bloom filter works with the help of an example:

    Suppose we have a set of elements {A, B, C, D, E, F, G, H, I, J} and we want to check whether the element ‘X’ is present in the set or not.

    Here’s how a Bloom filter works:

    • Initially, we create a bit array of size ‘m’ and initialize all bits to 0.
    • We also create ‘k’ hash functions, each of which maps an element to one of the ‘m’ bits.
    • For each element in the set, we calculate the ‘k’ hash values and set the corresponding bits in the bit array to 1.
    • When we want to check whether an element ‘X’ is present in the set, we calculate the ‘k’ hash values for ‘X’ and check if all the corresponding bits are set to 1.
    • If all the bits are set to 1, we say that ‘X’ is possibly in the set. If any of the bits is 0, we say that ‘X’ is definitely not in the set.

    Implementation of Bloom Filter

    Here’s an example implementation of a Bloom filter C, C++, Java and Python:

    CC++JavaPython

    // C program to implement Bloom Filter#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#define SIZE 10
    
    bool bitArray[SIZE]={0};inthash1(int key){return key % SIZE;}voidinsert(int key){int h1 =hash1(key);
       bitArray[key]=1;}
    
    bool search(int key){int h1 =hash1(key);return bitArray[key];}intmain(){insert(3);insert(5);insert(7);insert(9);printf("%d\n",search(3));printf("%d\n",search(5));printf("%d\n",search(7));printf("%d\n",search(9));printf("%d\n",search(4));printf("%d\n",search(6));printf("%d\n",search(8));return0;}

    Output

    Following is the output of the above C program:

    1
    1
    1
    1
    0
    0
    0
    

    Features of Bloom Filter

    Some of the key features of Bloom filters include:

    • Space-efficient: Bloom filters use a small amount of memory compared to other data structures.
    • Fast: Bloom filters provide constant-time lookup and insertion operations.
    • Probabilistic: Bloom filters may return false positives, but never false negatives.
    • Scalable: Bloom filters can be easily scaled to handle large datasets.

    Applications of Bloom Filter

    Bloom filters are used in various applications, including:

    • Spell checkers
    • Network routers
    • Web browsers
    • Database systems
    • Anti-virus software
    • Big data processing
    • Content delivery networks
  • Bit Mask in Data Structures

    A bit mask is a powerful technique that is widely used in computer science and programming to manipulate bits. You can think of it as a way to turn on or off specific bits in a binary number.

    Bit masks are used for performing bitwise operations, such as ANDORXOR, and NOT, on binary numbers. They are commonly used in computer science for various applications, we will discuss about the bit mask in detail in this chapter.

    What is Bit Mask?

    Bitmask, also known as mask is a sequence of N-bits that encode the subset of our collection. The element of the mask can be either set or not set (i.e. 0 or 1). This denotes the availability of the chosen element in the bitmask.

    For example, an element i is available in the subset if the ith bit of mask is set. For the N element set, there can be a 2N mask each corresponding to a subset.

    So, for solving the problem, we will start for a mask i.e. a subset assigns it a value and then finds the values for further masks using the values of the previous mask. Using this we will finally be able to calculate the value of the main set.

    Bitwise Operations on Bit Mask

    Now, let’s discuss the bitwise operations that can be performed on a bit mask:

    • AND Operation: The AND operation is used to set a bit to 1 if both bits are 1, otherwise it will be set to 0.
    • OR Operation: The OR operation is used to set a bit to 1 if either of the bits is 1, otherwise it will be set to 0.
    • XOR Operation: The XOR operation is used to set a bit to 1 if the bits are different, otherwise it will be set to 0.
    • NOT Operation: The NOT operation is used to invert the bits, i.e. 1 will be converted to 0 and 0 will be converted to 1.

    Operations on Bit Mask

    There are mainly three operations that can be performed on a bit mask:

    • Set: Setting a bit to 1 in a binary number.
    • Clear: Clearing a bit to 0 in a binary number.
    • Toggle: Toggling a bit from 0 to 1 or 1 to 0 in a binary number.

    Set Operation on Bit Mask

    Setting a bit to 1 in a binary number is done by using the OR operation. The OR operation is used to set a bit to 1 if either of the bits is 1, otherwise it will be set to 0.

    Algorithm for Set Operation

    Following is the algorithm to set the ith bit to 1 in the binary number:

    1.Read the binary number and the position of the bit to set.
    2.Create a mask with a 1 at the ith position using left shift.
    3.Perform the OR operation between the binary number and the mask to set the ith bit.
    4.Output the modified binary number.
    

    Code for Set Operation

    Here is the code for performing the Set operation on a bit mask in different programming languages:

    CC++JavaPython

    //C Program to perform Set Operation on Bit Mask#include <stdio.h>intsetBit(int num,int pos){return num |(1<< pos);}intmain(){int num =5;int pos =1;
       num =setBit(num, pos);printf("Number after Set Operation: %d\n", num);return0;}

    Output

    Number after Set Operation: 7
    

    Clear Operation on Bit Mask

    Clearing a bit to 0 in a binary number is done by using the AND operation. The AND operation is used to set a bit to 1 if both bits are 1, otherwise it will be set to 0.

    Why do we need to clear a bit to 0?

    Sometimes, we need to clear a bit to 0 in a binary number to perform further operations on it. For example, if we want to set a bit to 1, we first clear the bit to 0 and then set it to 1.

    Algorithm for Clear Operation

    Following is the algorithm to clear the ith bit to 0 in the binary number:

    1.Read the binary number and the position of the bit to clear.
    2.Create a mask with a 1 at the ith position using left shift and then invert it.
    3.Perform the AND operation between the binary number and the mask to clear the ith bit.
    4.Output the modified binary number.
    

    Code for Clear Operation

    Here is the code for performing the Clear operation on a bit mask in different programming languages:

    CC++JavaPython

    //C Program to perform Clear Operation on Bit Mask#include <stdio.h>intclearBit(int num,int pos){return num &~(1<< pos);}intmain(){int num =7;int pos =1;
       num =clearBit(num, pos);printf("Number after Clear Operation: %d\n", num);return0;}

    Output

    Number after Clear Operation: 5
    

    Toggle Operation on Bit Mask

    Toggling a bit from 0 to 1 or 1 to 0 in a binary number is done by using the XOR operation. The XOR operation is used to set a bit to 1 if the bits are different, otherwise it will be set to 0.

    Algorithm for Toggle Operation

    Following is the algorithm to toggle the ith bit in the binary number:

    1.Read the binary number and the position of the bit to toggle.
    2.Toggle the ith bit in the binary number using XOR operation.
    3.Output the modified binary number.
    

    Code for Toggle Operation

    Here is the code for performing the Toggle operation on a bit mask in different programming languages:

    CC++JavaPython

    //C Program to perform Toggle Operation on Bit Mask#include <stdio.h>inttoggleBit(int num,int pos){return num ^(1<< pos);}intmain(){int num =5;int pos =1;
       num =toggleBit(num, pos);printf("Number after Toggle Operation: %d\n", num);return0;}

    Output

    Number after Toggle Operation: 7
    

    Applications of Bit Mask

    Bit masks are used in computer science for various applications, such as:

    • Setting and clearing bits in a binary number.
    • Performing bitwise operations on binary numbers.
    • Encoding and decoding data in a compact form.
    • Implementing data structures like bit arrays and bitmaps.
    • Optimizing algorithms and data structures for performance.

    Conclusion

    Bit masks are useful for manipulating bits in a binary number. They are commonly used in computer science and programming for performing bitwise operations on binary numbers.

    Bit masks can be used to setclear, and toggle bits in a binary number, as well as perform bitwise operations like ANDORXOR, and NOT. They are a powerful technique for optimizing algorithms and data structures for performance.

  • Treap (A Randomized Binary Search Tree)

    Treaps (pronounced as “trees” and “heaps”) are a data structure that is combination of Binary Search Tree and Heap. It is a binary search tree that is also a heap. We can call it a randomized data structure that maintains a dynamic set of elements, it ensures both the binary search order and the heap order.

    Imagine a scenario where you have to organize a bookshelf. You want these book to be in a sorted order, but you also want to keep the books that you read most often at the top. Treaps are the data structure that can help you in this scenario.

    Properties of Treaps

    Following are the properties of Treaps −

    • Combined with two major data structures: Binary Search Tree and Heap.
    • Hence it is BST, keys in left sub-tree are less than the root and keys in right subtree are greater than the root.
    • Has time complexity of O(log n) for search, insert and delete operations.
    • Randomized data structure.
    • It is a balanced binary search tree.
    • The tree height is O(log n) on average.

    Structure of Treaps

    Each node in a Treap has two fields:

    • Key: The key is the value of the node.
    • Priority: The priority is a random number that is assigned to the node.

    The key field is used to maintain the binary search tree property, and the priority field is used to maintain the heap property. The priority of a node is always greater than the priority of its children.

    Here is an example of a Treap:

            50
           /  \
         30    70
        /  \   / \
       20  40 60  80
    

    Operations on Treaps

    There are many operations that can be performed on Treaps. Some of the major operations are:

    • Search
    • Insert
    • Delete

    Implementation of Treaps

    Now we will see how to implement Treaps in C, C++, Java and Python.

    Algorithm for Inserting a Node in Treaps

    1. If the root is NULL, create a new node with the key and priority and return the node.
    2. If the key is less than the root, insert in the left subtree.
    3. If the key is greater than the root, insert in the right subtree.
    4. If the priority of the new node is greater than the priority of the root, rotate the new node with the root.
    5. Repeat the process until the new node is inserted.
    

    Code for Implementing Treaps

    Now, let’s understand the code for implementing Treaps in C, C++, Java and Python.

    For implementing treaps, we need to create a structure for the node and define the functions for inserting and rotating the nodes. After that we can insert the nodes and perform the operations on the treap.

    CC++JavaPython

    // C program to implement Treaps#include <stdio.h>#include <stdlib.h>structNode{int key;int priority;structNode*left,*right;};structNode*newNode(int key){structNode* temp =(structNode*)malloc(sizeof(structNode));
       temp->key = key;
       temp->priority =rand()%100;
       temp->left = temp->right =NULL;return temp;}voidrightRotate(structNode**y){structNode*x =(*y)->left;structNode*T2 = x->right;
    
       x->right =*y;(*y)->left = T2;*y = x;}voidleftRotate(structNode**x){structNode*y =(*x)->right;structNode*T2 = y->left;
    
       y->left =*x;(*x)->right = T2;*x = y;}structNode*insert(structNode* root,int key){if(root ==NULL)returnnewNode(key);if(key <= root->key){
          root->left =insert(root->left, key);if(root->left->priority > root->priority)rightRotate(&root);}else{
          root->right =insert(root->right, key);if(root->right->priority > root->priority)leftRotate(&root);}return root;}voidinorder(structNode* root){if(root){inorder(root->left);printf("%d ", root->key);inorder(root->right);}}intmain(){structNode* root =NULL;
       root =insert(root,50);
       root =insert(root,30);
       root =insert(root,20);
       root =insert(root,40);
       root =insert(root,70);
       root =insert(root,60);
       root =insert(root,80);printf("Inorder traversal of the given Treap: ");inorder(root);return0;}

    Output

    Following is the output of the above C program:

    Inorder traversal of the given Treap: 20 30 40 50 60 70 80
    

    Search Operation on Treaps

    Searching in a Treap is similar to searching in a Binary Search Tree. We start from the root and compare the key with the root. If the key is less than the root, we move to the left subtree. If the key is greater than the root, we move to the right subtree. We continue this process until we find the key or reach a NULL node.

    Algorithm for Searching in Treaps

    1. Start from the root.
    2. If the root is NULL, return NULL.
    3. If the key is equal to the root, return the root.
    4. If the key is less than the root, search in the left subtree.
    5. If the key is greater than the root, search in the right subtree.
    6. Repeat the process until the key is found or NULL is reached.
    

    Code for Searching in Treaps

    Now, let’s see the code for searching in Treaps in C, C++, Java and Python.

    CC++JavaPython

    // C program to implement Treaps#include <stdio.h>#include <stdlib.h>structNode{int key;int priority;structNode*left,*right;};structNode*newNode(int key){structNode* temp =(structNode*)malloc(sizeof(structNode));
       temp->key = key;
       temp->priority =rand()%100;
       temp->left = temp->right =NULL;return temp;}voidrightRotate(structNode**y){structNode*x =(*y)->left;structNode*T2 = x->right;
    
       x->right =*y;(*y)->left = T2;*y = x;}voidleftRotate(structNode**x){structNode*y =(*x)->right;structNode*T2 = y->left;
    
       y->left =*x;(*x)->right = T2;*x = y;}structNode*insert(structNode* root,int key){if(root ==NULL)returnnewNode(key);if(key <= root->key){
          root->left =insert(root->left, key);if(root->left->priority > root->priority)rightRotate(&root);}else{
          root->right =insert(root->right, key);if(root->right->priority > root->priority)leftRotate(&root);}return root;}structNode*search(structNode* root,int key){if(root ==NULL|| root->key == key)return root;if(root->key < key)returnsearch(root->right, key);returnsearch(root->left, key);}intmain(){structNode* root =NULL;
       root =insert(root,50);
       root =insert(root,30);
       root =insert(root,20);
       root =insert(root,40);
       root =insert(root,70);
       root =insert(root,60);
       root =insert(root,80);structNode* result =search(root,40);if(result !=NULL)printf("Key %d found\n", result->key);elseprintf("Key not found\n");return0;}

    Output

    Following is the output of the above C program:

    Key found 40
    

    Deletion Operation on Treaps

    Deletion in a Treap is similar to deletion in a Binary Search Tree. We first search for the key to be deleted. If the key is found, we delete the node and rearrange the tree to maintain the heap property. We can delete the node by replacing it with the node that has the smallest priority in the right subtree or the node that has the largest priority in the left subtree.

    Algorithm for Deleting a Node in Treaps

    1. Search for the key to be deleted.
    2. If the key is not found, return NULL.
    3. If the key is found, delete the node.
    4. If the node has no children, delete the node.
    5. If the node has one child, replace the node with the child.
    6. If the node has two children, replace the node with the node that has the smallest priority in the right subtree or the node that has the largest priority in the left subtree.
    7. Repeat the process until the node is deleted.
    

    Code for Deleting a Node in Treaps

    Now, let’s see the code for deleting a node in Treaps in C, C++, Java and Python.

    CC++JavaPython

    // C program to implement Treaps#include <stdio.h>#include <stdlib.h>structNode{int key;int priority;structNode*left,*right;};// Function to create a new nodestructNode*newNode(int key){structNode* temp =(structNode*)malloc(sizeof(structNode));
       temp->key = key;
       temp->priority =rand()%100;
       temp->left = temp->right =NULL;return temp;}// Right rotation functionstructNode*rightRotate(structNode*y){structNode*x = y->left;structNode*T2 = x->right;
    
       x->right = y;
       y->left = T2;return x;}// Left rotation functionstructNode*leftRotate(structNode*x){structNode*y = x->right;structNode*T2 = y->left;
    
       y->left = x;
       x->right = T2;return y;}// Insert functionstructNode*insert(structNode* root,int key){if(root ==NULL)returnnewNode(key);if(key <= root->key){
          root->left =insert(root->left, key);if(root->left->priority > root->priority)
             root =rightRotate(root);}else{
          root->right =insert(root->right, key);if(root->right->priority > root->priority)
             root =leftRotate(root);}return root;}// Inorder traversal functionvoidinorder(structNode* root){if(root !=NULL){inorder(root->left);printf("%d ", root->key);inorder(root->right);}}// Delete functionstructNode*deleteNode(structNode* root,int key){if(root ==NULL)return root;if(key < root->key)
          root->left =deleteNode(root->left, key);elseif(key > root->key)
          root->right =deleteNode(root->right, key);else{if(root->left ==NULL){structNode* temp = root->right;free(root);return temp;}elseif(root->right ==NULL){structNode* temp = root->left;free(root);return temp;}if(root->left->priority < root->right->priority){
             root =rightRotate(root);
             root->right =deleteNode(root->right, key);}else{
             root =leftRotate(root);
             root->left =deleteNode(root->left, key);}}return root;}intmain(){structNode* root =NULL;
       root =insert(root,50);
       root =insert(root,30);
       root =insert(root,20);
       root =insert(root,40);
       root =insert(root,70);
       root =insert(root,60);
       root =insert(root,80);printf("Inorder traversal of the given Treap: ");inorder(root);printf("\n");
    
       root =deleteNode(root,20);printf("Inorder traversal of the Treap after deletion: ");inorder(root);printf("\n");return0;}

    Output

    Following is the output of the above C program:

    Inorder traversal of the given Treap: 20 30 40 50 60 70 80
    Inorder traversal of the given Treap after deletion: 30 40 50 60 70 80
    

    Time Complexity of Treaps

    Following is the time complexity of Treaps:

    • Insertion: O(log n) on average.
    • Deletion: O(log n) on average.
    • Search: O(log n) on average.

    Applications of Treaps

    • It is used in the implementation of priority queues.
    • Also used in online algorithms.
    • Useful in dynamic optimization problems.
    • Helpful in interval scheduling.

    Conclusion

    In this chapter, we learned about Treaps, which are a combination of Binary Search Trees and Heaps. We saw the structure of Treaps, the operations that can be performed on Treaps, and the implementation of Treaps in C, C++, Java and Python. We also saw the time and space complexity of Treaps along with the applications of Treaps.

  • Suffix Tries

    A suffix trie is a type of trie mainly used for representing all the suffixes of a string. It is a tree where each node represents the suffix of a string. The root node is the entire string, and each child node is a suffix of the string that’s one character shorter than the parent node’s suffix.

    For example, consider the string “banana”. The suffix trie for this string would look like this:

                 root
               /  | \
              b   a  n
             /    |   \
             a    n    a
            /     |     \
            n     a      n
           /      |       \
          a       n        a
         /        |         \
        n         a          $
       /          |
      a           $
     /
    $
    

    Each path from the root to a leaf node represents a suffix of the string. The leaf nodes represent the end of a suffix, and the dollar sign ($) is used to indicate the end of the string.

    The children store entire word instead of characters.

    Features of Suffix Tries

    Following are the Features of Suffix Tries −

    Operations on Suffix Tries

    Some of the common operations on suffix tries include:

    • Insertion: Insert a new suffix into the trie.
    • Deletion: Remove a suffix from the trie.
    • Search: Find a specific suffix in the trie.

    Insertion Operation on Suffix Tries

    We can insert a new suffix into the trie by following these steps:

    1. Start at the root node.
    2. For each character in the suffix:
        a. If the character is not in the current node's children, create a new node for the same.
        b. Then move to the child node corresponding to the character and repeat the process.
    3. Repeat the process for all character in the suffix.
    4. Mark the last node as a leaf node to indicate the end of the suffix.
    

    Code for Insertion Operation

    For example, to insert the suffix “ana” into the trie, we would follow these steps:

    CC++JavaPython

    // C Program to insert a suffix into a trie#include <stdio.h>#include <stdlib.h>#define ALPHABET_SIZE 26// Structure for trie nodestructTrieNode{structTrieNode* children[ALPHABET_SIZE];int isEndOfWord;};// Function to create a new trie nodestructTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}// Function to insert a suffix into the trievoidinsert(structTrieNode* root,constchar* suffix){structTrieNode* current = root;for(int i =0; suffix[i]!='\0'; i++){int index = suffix[i]-'a';if(current->children[index]==NULL){
             current->children[index]=createNode();}
          current = current->children[index];}
       current->isEndOfWord =1;}// Display the trievoiddisplay(structTrieNode* root,char str[],int level){if(root->isEndOfWord){
          str[level]='\0';printf("%s\n", str);}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]!=NULL){
             str[level]= i +'a';display(root->children[i], str, level +1);}}}intmain(){structTrieNode* root =createNode();insert(root,"banana");insert(root,"ana");insert(root,"na");insert(root,"a");insert(root,"$");char str[20];// Allocating memory for string bufferdisplay(root, str,0);return0;}

    Output

    Following is the output of the above code:

    a
    ana
    banana
    na
    

    Deletion and Search Operations on Suffix Tries

    Deletion and search operations on suffix tries are similar to insertion. We can delete a suffix by removing the corresponding leaf node from the trie. To search for a specific suffix, we can traverse the trie from the root node to the leaf node corresponding to the suffix.

    Following are the steps to delete a suffix from the trie:

    1. Start at the root node.
    2. For each character in the suffix:
        a. Move to the child node corresponding to the character.
    3. Mark the last node as a non-leaf node to delete the suffix.
    

    Following are the steps to search for a specific suffix in the trie:

    1. Start at the root node.
    2. For each character in the suffix:
        a. Move to the child node corresponding to the character.
    3. Check if the last node is a leaf node to find the suffix.
    

    Code for Deletion and Search Operations

    Here is the code for deletion and search operations on suffix tries:

    CC++JavaPython

    // C Program to delete and search a suffix in a trie#include <stdio.h>#include <stdlib.h>#define ALPHABET_SIZE 26// Structure for trie nodestructTrieNode{structTrieNode* children[ALPHABET_SIZE];int isEndOfWord;};// Function to create a new trie nodestructTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}// Function to insert a suffix into the trievoidinsert(structTrieNode* root,constchar* suffix){structTrieNode* current = root;for(int i =0; suffix[i]!='\0'; i++){int index = suffix[i]-'a';if(current->children[index]==NULL){
             current->children[index]=createNode();}
          current = current->children[index];}
       current->isEndOfWord =1;}// Function to delete a suffix from the trievoiddelete(structTrieNode* root,constchar* suffix){structTrieNode* current = root;for(int i =0; suffix[i]!='\0'; i++){int index = suffix[i]-'a';if(current->children[index]==NULL){return;// Suffix not found}
          current = current->children[index];}
       current->isEndOfWord =0;}// Function to search for a suffix in the trieintsearch(structTrieNode* root,constchar* suffix){structTrieNode* current = root;for(int i =0; suffix[i]!='\0'; i++){int index = suffix[i]-'a';if(current->children[index]==NULL){return0;// Suffix not found}
          current = current->children[index];}return current->isEndOfWord;}intmain(){structTrieNode* root =createNode();insert(root,"banana");insert(root,"ana");insert(root,"na");insert(root,"a");insert(root,"$");printf("Search for 'ana': %s\n",search(root,"ana")?"Found":"Not found");delete(root,"ana");printf("After deletion:\n");printf("Search for 'ana': %s\n",search(root,"ana")?"Found":"Not found");printf("Search for 'na': %s\n",search(root,"na")?"Found":"Not found");return0;}

    Output

    Following is the output of the above code:

    Search for 'ana': Found
    After deletion:
    Search for 'ana': Not found
    Search for 'na': Found
    

    Time Complexity of Suffix Tries

    The time complexity of suffix tries depends on the operations performed:

    • Insertion: O(n) for inserting a suffix of length n into the trie.
    • Deletion: O(n)
    • Search: O(n)

    Applications of Suffix Tries

    Suffix tries are used in various applications, including:

    • Pattern matching and substring search algorithms
    • Text indexing and searching
    • Genomic sequence analysis
    • Spell checking and correction
    • Biological sequence analysis
    • Information retrieval
    • Compression algorithms

    Conclusion

    In this chapter, we discussed suffix tries, a type of trie used for representing all the suffixes of a string. We explored the structure of a suffix trie, its features, operations, and applications. Suffix tries are a powerful data structure for substring searches, pattern matching, and other string-related operations.

  • Compressed Tries

    Compressed Trie is also known as Patricia Trie or Radix Tree. It is a type of trie data structure that is used for storing and retrieving keys in a dataset, where the keys are strings.

    You can think of a compressed trie as a trie where the nodes with only one child are merged with their parent nodes. This helps in reducing the space complexity of the trie and makes it more efficient.

    The compression of redundant nodes helps in memory management. This type of trie is used with applications that need space management.

    Properties of Compressed Tries

    Following are some of the properties of compressed tries:

    • The nodes of compressed tries have at least two children.
    • The edges of the trie are labeled with characters.
    • The keys are stored in the leaves of the trie.

    Representation of Compressed Tries

    Now, let’s see how a compressed trie is represented:

    It is represented as a tree where each node has a label and a list of children. The edges of the tree are labeled with characters. The keys are stored in the leaves of the tree.

    Here is an example of a compressed trie:

        (root)
          |
         b
         |
        e, i
        |   \
      ar, ll  d
          \    \
           l     l
          /       \
        d, ll    bull
    

    In the above example, the keys “bear”, “bell”, “bid”, “bull” are stored in the leaves of the trie.

    Operations on Compressed Tries

    We can perform the following operations on compressed tries:

    • Insert: Inserting a new key into the trie.
    • Search: Searching for a key in the trie.
    • Delete: Deleting a key from the trie.

    Insert Operation on Compressed Tries

    For insertion of a new key into the compressed trie, we need to follow these steps:

    Algorithm for Insert Operation

    Following is the algorithm for inserting a new key into the compressed trie:

    Insert the new key into the trie.
    If the node has only one child, merge it with its parent.
    

    Code for Insert Operation

    Now, let’s implement the insert operation in a compressed trie. For this, first we need to define the structure of the trie node then implement the insert operation.

    CC++JavaPython

    //C Program to perform Insert Operation on Compressed Trie#include <stdio.h>#include <stdlib.h>#include <string.h>#define ALPHABET_SIZE 26// Structure for Trie NodestructTrieNode{structTrieNode* children[ALPHABET_SIZE];int isEndOfWord;};// Function to create a new Trie NodestructTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}// Function to insert a key into the Trievoidinsert(structTrieNode* root,char* key){structTrieNode* temp = root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(!temp->children[index]){
             temp->children[index]=createNode();}
          temp = temp->children[index];}
       temp->isEndOfWord =1;}voiddisplay(structTrieNode* root,char str[],int level){if(root->isEndOfWord){
          str[level]='\0';printf("%s\n", str);}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){
             str[level]= i +'a';display(root->children[i], str, level +1);}}}// Main Functionintmain(){structTrieNode* root =createNode();char keys[][8]={"bear","bell","bid","bull"};for(int i =0; i <4; i++){insert(root, keys[i]);}char str[20];display(root, str,0);return0;}

    Output

    Following is the output of the above code:

    bear
    bell
    bid
    bull
    

    Search Operation on Compressed Tries

    For searching a key in the compressed trie, we need to follow these steps:

    Algorithm for Search Operation

    Following is the algorithm for searching a key in the compressed trie:

    Search for the key in the trie.
    If the key is found, return true.
    Otherwise, return false.
    

    Code for Search Operation

    Now, let’s implement the search operation in a compressed trie. For this, we need to define the structure of the trie node and then implement the search operation.

    CC++JavaPython

    //C Program to perform Search Operation on Compressed Trie#include <stdio.h>#include <stdlib.h>#include <string.h>#define ALPHABET_SIZE 26// Structure for Trie NodestructTrieNode{structTrieNode* children[ALPHABET_SIZE];int isEndOfWord;};// Function to create a new Trie NodestructTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}// Function to insert a key into the Trievoidinsert(structTrieNode* root,char* key){structTrieNode* temp = root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(!temp->children[index]){
             temp->children[index]=createNode();}
          temp = temp->children[index];}
       temp->isEndOfWord =1;}// Function to search a key in the Trieintsearch(structTrieNode* root,char* key){structTrieNode* temp = root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(!temp->children[index]){return0;}
          temp = temp->children[index];}return(temp !=NULL&& temp->isEndOfWord);}voiddisplay(structTrieNode* root,char str[],int level){if(root->isEndOfWord){
          str[level]='\0';printf("%s\n", str);}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){
             str[level]= i +'a';display(root->children[i], str, level +1);}}}// Main Functionintmain(){structTrieNode* root =createNode();char keys[][8]={"bear","bell","bid","bull"};for(int i =0; i <4; i++){insert(root, keys[i]);}char str[20];display(root, str,0);char key[]="bell";if(search(root, key)){printf("%s is present in the trie.\n", key);}else{printf("%s is not present in the trie.\n", key);}return0;}

    Output

    Following is the output of the above code:

    bear
    bell
    bid
    bull
    bell is present in the trie.
    

    Delete Operation on Compressed Tries

    For deleting a key from the compressed trie, we need to follow these steps:

    Algorithm for Delete Operation

    Following is the algorithm for deleting a key from the compressed trie:

    Search for the key in the trie.
    If the key is found, mark the last node as not the end of the word.
    If the last node has no children, delete it.
    

    Code for Delete Operation

    Now, let’s implement the delete operation in a compressed trie. For this, we need to define the structure of the trie node and then implement the delete operation.

    CC++JavaPython

    //C Program to perform Delete Operation on Compressed Trie#include <stdio.h>#include <stdlib.h>#include <string.h>#define ALPHABET_SIZE 26// Structure for Trie NodestructTrieNode{structTrieNode* children[ALPHABET_SIZE];int isEndOfWord;};// Function to create a new Trie NodestructTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}// Function to insert a key into the Trievoidinsert(structTrieNode* root,char* key){structTrieNode* temp = root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(!temp->children[index]){
             temp->children[index]=createNode();}
          temp = temp->children[index];}
       temp->isEndOfWord =1;}// Function to search a key in the Trieintsearch(structTrieNode* root,char* key){structTrieNode* temp = root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(!temp->children[index]){return0;}
          temp = temp->children[index];}return(temp !=NULL&& temp->isEndOfWord);}// Function to delete a key from the Trievoiddelete(structTrieNode* root,char* key){if(!search(root, key)){return;}structTrieNode* temp = root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';
          temp = temp->children[index];}
       temp->isEndOfWord =0;}voiddisplay(structTrieNode* root,char str[],int level){if(root->isEndOfWord){
          str[level]='\0';printf("%s\n", str);}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){
             str[level]= i +'a';display(root->children[i], str, level +1);}}}// Main Functionintmain(){structTrieNode* root =createNode();char keys[][8]={"bear","bell","bid","bull"};for(int i =0; i <4; i++){insert(root, keys[i]);}char str[20];display(root, str,0);char key[]="bell";delete(root, key);printf("Trie after deletion:\n");display(root, str,0);if(search(root, key)){printf("%s is present in the trie.\n", key);}else{printf("%s is not present in the trie.\n", key);}return0;}

    Output

    Following is the output of the above code:

    bear
    bell
    bid
    bull
    bell is not present in the trie.
    

    Applications of Compressed Tries

    Compressed tries are used in various applications, such as:

    • Spell checkers: In spell checkers, compressed tries are used to store the dictionary of words.
    • Text editors
    • Routing tables in computer networks
    • Genome sequence analysis
    • String matching algorithms

    Conclusion

    In this chapter, we learned about compressed tries, which are used to store a large number of strings efficiently. We discussed the structure of a compressed trie, the insert operation, search operation, and delete operation on a compressed trie. We also implemented these operations in C, C++, Java, and Python programming languages.

  • Standard Tries

    Tries are tree-like data structures that are mainly used for storing dynamic set of strings or associative arrays where the keys are usually strings. The word “trie” comes from the word “retrieval”.

    We already discussed the basic concepts of tries in Tries chapter. In this chapter, we will discuss the standard tries in data structures.

    What is Standard Trie?

    Children of a node are stored in a certain order such as dictionary order (alphabetical) or ASCII order. The order des on the use cases. For example, if the trie is used for a dictionary, the children are stored in alphabetical order.

    Let’s understand with an example.

    We have an array of Strings.
    {cat, car, dog, doll}
    The structure of the trie for these strings would be
                       (root)
                 	    /\
                 	   /  \
                 	  /    \
                      /      \
                     c        d
                    / \      / \
                   a   a    o   o
                  /    \   /     \
                 t      r l       g
                       /
                      l
    

    The above example shows the standard trie structure for the strings {cat, car, dog, doll}. The root node is empty and the children are stored in alphabetical order.

    In the Standard Trie, a string can be traced from the root node to the node where the strings. The node is marked as a leaf node.

    Operations on Standard Trie

    There are mainly three operations that can be performed on a standard trie:

    • Insert: Inserting a new string in the trie.
    • Search: Searching a string in the trie.
    • Delete: Deleting a string from the trie.

    Implementation of Standard Trie

    For implementing the standard trie, we need to create a structure for the trie node. The trie node contains an array of pointers to the children nodes and a flag to mark the of the string.

    Now, let’s implement the standard trie, which supports the above operations.

    Insert Operation on Standard Trie

    Inserting a new string in the Standard Trie is done by inserting the characters of the string one by one. The characters are inserted in the trie in the order of the string. The last character of the string is marked as a leaf node.

    Algorithm for Insert Operation in Standard Trie

    Following is the algorithm for the insert operation in the standard trie:

    1.Traverse the trie from the root node.
    2.Insert the characters of the string one by one.
    3.Mark the last character of the string as a leaf node.
    

    Code for Insert Operation in Standard Trie

    Let’s see the code for the insert operation in the standard trie using C, C++, Java, and Python programming languages.

    CC++JavaPython

    // C Program to perform Insert Operation in Standard Trie#include <stdio.h>#include <stdlib.h>#define ALPHABET_SIZE 26// Structure for Trie NodestructTrieNode{structTrieNode*children[ALPHABET_SIZE];int iOfWord;};// Function to create a new Trie NodestructTrieNode*createNode(){structTrieNode*newNode =(structTrieNode*)malloc(sizeof(structTrieNode));
       newNode->iOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          newNode->children[i]=NULL;}return newNode;}// Function to insert a string in the Trievoidinsert(structTrieNode*root,char*key){structTrieNode*temp = root;for(int i =0; key[i]!='\0'; i++){int index = key[i]-'a';if(!temp->children[index]){
             temp->children[index]=createNode();}
          temp = temp->children[index];}
       temp->iOfWord =1;}voiddisplay(structTrieNode*root,char str[],int level){if(root->iOfWord){
          str[level]='\0';printf("%s\n", str);}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){
             str[level]= i +'a';display(root->children[i], str, level +1);}}}// Main functionintmain(){char*keys[]={"cat","car","dog","doll"};int n =sizeof(keys)/sizeof(keys[0]);structTrieNode*root =createNode();for(int i =0; i < n; i++){insert(root, keys[i]);}printf("Inserted Strings are:\n");char str[26];display(root, str,0);return0;}

    Output

    Following is the output obtained −

    Inserted Strings are:
    car
    cat
    dog
    doll
    

    Search Operation on Standard Trie

    Searching a string in the Standard Trie is done by traversing the trie from the root node to the node where the strings. If the string is found in the trie, then the search operation returns true; otherwise, it returns false.

    Algorithm for Search Operation in Standard Trie

    Following is the algorithm for the search operation in the Standard Trie:

    1.Traverse the trie from the root node.
    2.Search the characters of the string one by one.
    3.If the string is found, return true.
    4.If the string is not found, return false.
    

    Code for Search Operation in Standard Trie

    Following is the code for the search operation in the Standard Trie using C, C++, Java, and Python programming languages.

    CC++JavaPython

    // C Program to perform Search Operation in Standard Trie#include <stdio.h>#include <stdlib.h>#define ALPHABET_SIZE 26// Structure for Trie NodestructTrieNode{structTrieNode*children[ALPHABET_SIZE];int iOfWord;};// Function to create a new Trie NodestructTrieNode*createNode(){structTrieNode*newNode =(structTrieNode*)malloc(sizeof(structTrieNode));
       newNode->iOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          newNode->children[i]=NULL;}return newNode;}// Function to insert a string in the Standard Trievoidinsert(structTrieNode*root,char*key){structTrieNode*temp = root;for(int i =0; key[i]!='\0'; i++){int index = key[i]-'a';if(!temp->children[index]){
             temp->children[index]=createNode();}
          temp = temp->children[index];}
       temp->iOfWord =1;}// Function to search a string in the Standard Trieintsearch(structTrieNode*root,char*key){structTrieNode*temp = root;for(int i =0; key[i]!='\0'; i++){int index = key[i]-'a';if(!temp->children[index]){return0;}
          temp = temp->children[index];}return temp !=NULL&& temp->iOfWord;}intmain(){char*keys[]={"cat","car","dog","doll"};int n =sizeof(keys)/sizeof(keys[0]);structTrieNode*root =createNode();for(int i =0; i < n; i++){insert(root, keys[i]);}search(root,"cat")?printf("Found\n"):printf("Not Found\n");return0;}

    Output

    The output obtained is as follows −

    Found
    

    Delete Operation on Standard Trie

    Deleting a string from the Standard Trie is done by deleting the characters of the string one by one. The characters are deleted in the order of the string. The last character of the string is marked as a non-leaf node.

    Algorithm for Delete Operation in Standard Trie

    Following is the algorithm for the delete operation in the Standard Trie:

    1.Traverse the trie from the root node.
    2.Delete the characters of the string one by one.
    3.Mark the last character of the string as a non-leaf node.
    

    Code for Delete Operation in Standard Trie

    Let’s see the code for the delete operation in the Standard Trie using C, C++, Java, and Python programming languages.

    CC++JavaPython

    // C Program to perform Delete Operation in Standard Trie#include <stdio.h>#include <stdlib.h>#define ALPHABET_SIZE 26structTrieNode{structTrieNode*children[ALPHABET_SIZE];int iOfWord;};structTrieNode*createNode(){structTrieNode*newNode =(structTrieNode*)malloc(sizeof(structTrieNode));
       newNode->iOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
          newNode->children[i]=NULL;}return newNode;}voidinsert(structTrieNode*root,char*key){structTrieNode*temp = root;for(int i =0; key[i]!='\0'; i++){int index = key[i]-'a';if(!temp->children[index]){
             temp->children[index]=createNode();}
          temp = temp->children[index];}
       temp->iOfWord =1;}intdelete(structTrieNode*root,char*key,int depth){if(root ==NULL){return0;}if(key[depth]=='\0'){if(root->iOfWord){
             root->iOfWord =0;}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){return0;}}free(root);return1;}int index = key[depth]-'a';if(delete(root->children[index], key, depth +1)){
          root->children[index]=NULL;if(!root->iOfWord){for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){return0;}}free(root);return1;}}return0;}voiddisplay(structTrieNode*root,char str[],int level){if(root ==NULL){return;}if(root->iOfWord){
          str[level]='\0';printf("%s\n", str);}for(int i =0; i < ALPHABET_SIZE; i++){if(root->children[i]){
             str[level]= i +'a';display(root->children[i], str, level +1);}}}intmain(){char*keys[]={"cat","car","dog","doll"};int n =sizeof(keys)/sizeof(keys[0]);structTrieNode*root =createNode();for(int i =0; i < n; i++){insert(root, keys[i]);}printf("Before Deletion:\n");char str[20];display(root, str,0);delete(root,"car",0);printf("\nAfter Deletion:\n");display(root, str,0);return0;}

    Output

    The output obtained is as follows −

    Before Deletion:
    car
    cat
    dog
    doll
    
    After Deletion:
    cat
    dog
    doll
    

    Time Complexity of Standard Trie

    Following is the time complexity of the Standard Trie:

    • Insert Operation: O(n), where n is the length of the string.
    • Search Operation: O(n).
    • Delete Operation: O(n).

    Applications of Standard Trie

    Standard Trie is used in various applications such as:

    • Spell Checker
    • Auto-Complete
    • Dictionary
    • IP Routing
    • Network Routing
    • Prefix Matching

    These are some of the applications where Standard Trie is used.

    Conclusion

    In this chapter, we discussed the Standard Trie in data structures. The structure of the Standard Trie, the operations that can be performed on the Standard Trie, and the implementation of the Standard Trie. We also discussed the applications of the Standard Trie.

  • Tries Data Structure

    A trie is a type of a multi-way search tree, which is fundamentally used to retrieve specific keys from a string or a set of strings. It stores the data in an ordered efficient way since it uses pointers to every letter within the alphabet.

    The trie data structure works based on the common prefixes of strings. The root node can have any number of nodes considering the amount of strings present in the set. The root of a trie does not contain any value except the pointers to its child nodes.

    There are three types of trie data structures −

    • Standard Tries
    • Compressed Tries
    • Suffix Tries

    The real-world applications of trie include − autocorrect, text prediction, sentiment analysis and data sciences.

    trie data structure

    Basic Operations in Tries

    The trie data structures also perform the same operations that tree data structures perform. They are −

    • Insertion
    • Deletion
    • Search

    Insertion operation

    The insertion operation in a trie is a simple approach. The root in a trie does not hold any value and the insertion starts from the immediate child nodes of the root, which act like a key to their child nodes. However, we observe that each node in a trie represents a singlecharacter in the input string. Hence the characters are added into the tries one by one while the links in the trie act as pointers to the next level nodes.

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <string.h>#define ALPHABET_SIZE 26structTrieNode{structTrieNode* children[ALPHABET_SIZE];int isEndOfWord;};structTrie{structTrieNode* root;};structTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
        node->isEndOfWord =0;for(int i =0; i < ALPHABET_SIZE; i++){
            node->children[i]=NULL;}return node;}voidinsert(structTrie* trie,constchar* key){structTrieNode* curr = trie->root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(curr->children[index]==NULL){
                curr->children[index]=createNode();}
            curr = curr->children[index];}
        curr->isEndOfWord =1;}voidprintWords(structTrieNode* node,char* prefix){if(node->isEndOfWord){printf("%s\n", prefix);}for(int i =0; i < ALPHABET_SIZE; i++){if(node->children[i]!=NULL){char* newPrefix =(char*)malloc(strlen(prefix)+2);strcpy(newPrefix, prefix);
                newPrefix[strlen(prefix)]='A'+ i;
                newPrefix[strlen(prefix)+1]='\0';printWords(node->children[i], newPrefix);free(newPrefix);}}}intmain(){structTrie car;
        car.root =createNode();insert(&car,"lamborghini");insert(&car,"mercedes-Benz");insert(&car,"land Rover");insert(&car,"maruti Suzuki");printf("Trie elements are:\n");printWords(car.root,"");return0;}

    Output

    Trie elements are:
    LAMBORGHINI
    LANDNOVER
    MARUTIOUZUKI
    MERCEZENZ
    

    Deletion operation

    The deletion operation in a trie is performed using the bottom-up approach. The element is searched for in a trie and deleted, if found. However, there are some special scenarios that need to be kept in mind while performing the deletion operation.

    Case 1 − The key is unique − in this case, the entire key path is deleted from the node. (Unique key suggests that there is no other path that branches out from one path).

    Case 2 − The key is not unique − the leaf nodes are updated. For example, if the key to be deleted is see but it is a prefix of another key seethe; we delete the see and change the Boolean values of t, h and e as false.

    Case 3 − The key to be deleted already has a prefix − the values until the prefix are deleted and the prefix remains in the tree. For example, if the key to be deleted is heart but there is another key present he; so we delete a, r, and t until only he remains.

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    //C code for Deletion Operation of tries Algorithm#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#include <string.h>//Define size 26#define ALPHABET_SIZE 26structTrieNode{structTrieNode* children[ALPHABET_SIZE];
       bool isEndOfWord;};structTrie{structTrieNode* root;};structTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord = false;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}voidinsert(structTrie* trie,constchar* key){structTrieNode* curr = trie->root;for(int i =0; i <strlen(key); i++){int index = key[i]-'a';if(curr->children[index]==NULL){
                curr->children[index]=createNode();}
            curr = curr->children[index];}
        curr->isEndOfWord =1;}
    bool search(structTrieNode* root,char* word){structTrieNode* curr = root;for(int i =0; word[i]!='\0'; i++){int index = word[i]-'a';if(curr->children[index]==NULL){return false;}
          curr = curr->children[index];}return(curr !=NULL&& curr->isEndOfWord);}
    bool startsWith(structTrieNode* root,char* prefix){structTrieNode* curr = root;for(int i =0; prefix[i]!='\0'; i++){int index = prefix[i]-'a';if(curr->children[index]==NULL){return false;}
          curr = curr->children[index];}return true;}
    bool deleteWord(structTrieNode* root,char* word){structTrieNode* curr = root;structTrieNode* parent =NULL;int index;for(int i =0; word[i]!='\0'; i++){
          index = word[i]-'a';if(curr->children[index]==NULL){return false;// Word does not exist in the Trie}
          parent = curr;
          curr = curr->children[index];}if(!curr->isEndOfWord){return false;// Word does not exist in the Trie}
       curr->isEndOfWord = false;// Mark as deletedif(parent !=NULL){
          parent->children[index]=NULL;// Remove the child node}return true;}voidprintWords(structTrieNode* node,char* prefix){if(node->isEndOfWord){printf("%s\n", prefix);}for(int i =0; i < ALPHABET_SIZE; i++){if(node->children[i]!=NULL){char* newPrefix =(char*)malloc(strlen(prefix)+2);strcpy(newPrefix, prefix);
                newPrefix[strlen(prefix)]='a'+ i;
                newPrefix[strlen(prefix)+1]='\0';printWords(node->children[i], newPrefix);free(newPrefix);}}}intmain(){structTrie car;
        car.root =createNode();insert(&car,"lamborghini");insert(&car,"mercedes-Benz");insert(&car,"landrover");insert(&car,"maruti Suzuki");//Before Deletionprintf("Tries elements before deletion: \n");printWords(car.root,"");//Deleting the elementschar* s1  ="lamborghini";char* s2 ="landrover";printf("Elements to be deleted are: %s and %s", s1, s2);deleteWord(car.root, s1);deleteWord(car.root, s2);//After Deletionprintf("\nTries elements before deletion: \n");printWords(car.root,"");}

    Output

    Tries elements before deletion: 
    lamborghini
    landrover
    marutiouzuki
    mercezenz
    Elements to be deleted are: lamborghini and landrover
    Tries elements before deletion: 
    marutiouzuki
    mercezenz
    

    Searching in a trie is a rather straightforward approach. We can only move down the levels of trie based on the key node (the nodes where insertion operation starts at). Searching is done until the end of the path is reached. If the element is found, search is successful; otherwise, search is prompted unsuccessful.

    Example

    Following are the implementations of this operation in various programming languages −

    CC++JavaPython

    //C program for search operation of tries algorithm#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#define ALPHABET_SIZE 26structTrieNode{structTrieNode* children[ALPHABET_SIZE];
       bool isEndOfWord;};structTrieNode*createNode(){structTrieNode* node =(structTrieNode*)malloc(sizeof(structTrieNode));
       node->isEndOfWord = false;for(int i =0; i < ALPHABET_SIZE; i++){
          node->children[i]=NULL;}return node;}voidinsert(structTrieNode* root,char* word){structTrieNode* curr = root;for(int i =0; word[i]!='\0'; i++){int index = word[i]-'a';if(curr->children[index]==NULL){
             curr->children[index]=createNode();}  
          curr = curr->children[index];} 
       curr->isEndOfWord = true;}
    bool search(structTrieNode* root,char* word){structTrieNode* curr = root;for(int i =0; word[i]!='\0'; i++){int index = word[i]-'a';if(curr->children[index]==NULL){return false;}   
          curr = curr->children[index];}return(curr !=NULL&& curr->isEndOfWord);}
    bool startsWith(structTrieNode* root,char* prefix){structTrieNode* curr = root;for(int i =0; prefix[i]!='\0'; i++){int index = prefix[i]-'a';if(curr->children[index]==NULL){return false;} 
          curr = curr->children[index];}return true;}intmain(){structTrieNode* root =createNode();//inserting the elementsinsert(root,"Lamborghini");insert(root,"Mercedes-Benz");insert(root,"Land Rover");insert(root,"Maruti Suzuki");//Searching elementsprintf("Searching Cars\n");//Printing searched elementsprintf("Found? %d\n",search(root,"Lamborghini"));// Output: 1 (true)printf("Found? %d\n",search(root,"Mercedes-Benz"));// Output: 1 (true)printf("Found? %d\n",search(root,"Honda"));// Output: 0 (false)printf("Found? %d\n",search(root,"Land Rover"));// Output: 1 (true)printf("Found? %d\n",search(root,"BMW"));// Output: 0 (false)   //Searching the elements the name starts with?printf("Cars name starts with\n");//Printing the elementsprintf("Does car name starts with 'Lambo'? %d\n",startsWith(root,"Lambo"));// Output: 1 (true)printf("Does car name starts with 'Hon'? %d\n",startsWith(root,"Hon"));// Output: 0 (false)printf("Does car name starts with 'Hy'? %d\n",startsWith(root,"Hy"));// Output: 0 (false)printf("Does car name starts with 'Mar'? %d\n",startsWith(root,"Mar"));// Output: 1 (true)printf("Does car name starts with 'Land'? %d\n",startsWith(root,"Land"));// Output: 1 (true)   return0;}

    Output

    Searching Cars
    Found? 1
    Found? 1
    Found? 0
    Found? 1
    Found? 0
    Cars name starts with
    Does car name starts with 'Lambo'? 1
    Does car name starts with 'Hon'? 0
    Does car name starts with 'Hy'? 0
    Does car name starts with 'Mar'? 1
    Does car name starts with 'Land'? 1
  • Fibbonacci Heap

    Like Binomial heapsFibonacci heaps are collection of trees. They are loosely based on binomial heaps. Unlike trees within binomial heaps, trees within Fibonacci heaps are rooted but unordered.

    Each node x in Fibonacci heaps contains a pointer p[x] to its parent, and a pointer child[x] to any one of its children. The children of x are linked together in a circular doubly linked list known as the child list of x.

    Each child y in a child list has pointers left[y] and right[y] to point to the left and right siblings of y respectively. If node y is the only child, then left[y] = right[y] = y. The order in which siblings appear in a child list is arbitrary.

    Structure of Fibonacci Heap

    Each node x in Fibonacci heap contains the following fields:

    • key[x]: key of node x
    • degree[x]: number of children of x
    • p[x]: parent of x
    • child[x]: any one of the children of x
    • left[x]: left sibling of x in the child list of x
    • right[x]: right sibling of x in the child list of x
    • mark[x]: boolean value to indicate whether x has lost a child since the last time x was made the child of another node

    Each Fibonacci heap H is a set of rooted trees that are min-heap ordered. That is, each tree obeys the min-heap property: the key of a node is greater than or equal to the key of its parent.

    The root list of H is a circular doubly linked list of elements of H containing exactly one root from each tree in the root list. The min pointer in H points to the root of the tree containing the minimum key.

    Fibonacci Heap

    This Fibonacci Heap H consists of five Fibonacci Heaps and 16 nodes. The line with arrow head indicates the root list. Minimum node in the list is denoted by min[H] which is holding 4.

    The asymptotically fast algorithms for problems such as computing minimum spanning trees, finding single source of shortest paths etc. make essential use of Fibonacci heaps.

    Operations on Fibonacci Heap

    Following are the operations that can be performed on Fibonacci heap:

    • Make-Fib-Heap().
    • Fib-Heap-Insert(H, x).
    • Fib-Heap-Extract-Min(H).
    • Fib-Heap-Decrease-Key(H, x, k).
    • Fib-Heap-Delete(H, x).

    Insert Operation on Fibonacci Heap

    Adds a new node x to the heap H. Its super fast, just inserts without much rearranging.

    Algorithm for Insert Operation

    Let’s assume that we have a Fibonacci heap H and a node x. The algorithm to insert node x into Fibonacci heap H is as follows:

    1: Create a new Fibonacci Heap H' containing only x.
    2: Set x.left = x and x.right = x (circular doubly linked list).
    3: If H is empty, set H.min = x.
    4: Otherwise, Insert x into the root list of H and update H.min if x.key < H.min.key.
    5: Increase the total node count of H.
    

    Code for Insert Operation

    Let’s write a simple code to insert a node into Fibonacci heap.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <math.h>structNode{int key, degree;structNode*parent,*child,*left,*right;int mark;};structFibonacciHeap{structNode* min;int nodeCount;};structNode*createNode(int key){structNode* newNode =(structNode*)malloc(sizeof(structNode));
       newNode->key = key;
       newNode->degree =0;
       newNode->parent = newNode->child =NULL;
       newNode->mark =0;
       newNode->left = newNode->right = newNode;return newNode;}structFibonacciHeap*createHeap(){structFibonacciHeap* heap =(structFibonacciHeap*)malloc(sizeof(structFibonacciHeap));
       heap->min =NULL;
       heap->nodeCount =0;return heap;}voidinsert(structFibonacciHeap* H,int key){structNode* x =createNode(key);if(!H->min){
          H->min = x;}else{
          x->right = H->min->right;
          x->left = H->min;
          H->min->right->left = x;
          H->min->right = x;if(x->key < H->min->key){
             H->min = x;}}
       H->nodeCount++;}voidprintHeap(structFibonacciHeap* H){if(!H->min){printf("Heap is empty!\n");return;}structNode* temp = H->min;printf("Fibonacci Heap Root List: ");do{printf("%d ", temp->key);
          temp = temp->right;}while(temp != H->min);printf("\n");}intmain(){structFibonacciHeap* fibHeap =createHeap();insert(fibHeap,10);insert(fibHeap,3);insert(fibHeap,15);insert(fibHeap,7);printHeap(fibHeap);printf("Minimum value in heap: %d\n", fibHeap->min->key);return0;}

    Output

    Following is the output of the above code:

    Fibonacci Heap Root List: 3 7 10 15
    Minimum value in heap: 3
    

    Extract Minimum Operation on Fibonacci Heap

    Removes the node containing the minimum key from the heap H. Its a bit complex operation as it involves rearranging the tree structure.

    Algorithm

    Let’s assume that we have a Fibonacci heap H. The algorithm to extract minimum node from Fibonacci heap H is as follows:

    1: z = H.min
    2: If z != NULL
       1 - For each child x of z, add x to the root list of H.
       2 - Remove z from the root list of H.
       3 - If z = z.right, then H.min = NULL.
       4 - Else, H.min = z.right and Consolidate(H).
       5 - Decrease the total node count of H.
    3: Return z.
    

    Code for Extract Minimum Operation

    Let’s write a simple code to extract minimum node from Fibonacci heap.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>structNode{int key, degree;structNode*parent,*child,*left,*right;int mark;};structFibonacciHeap{structNode* min;int nodeCount;};structNode*createNode(int key){structNode* newNode =(structNode*)malloc(sizeof(structNode));
       newNode->key = key;
       newNode->degree =0;
       newNode->parent = newNode->child =NULL;
       newNode->mark =0;
       newNode->left = newNode->right = newNode;return newNode;}structFibonacciHeap*createHeap(){structFibonacciHeap* heap =(structFibonacciHeap*)malloc(sizeof(structFibonacciHeap));
       heap->min =NULL;
       heap->nodeCount =0;return heap;}voidinsert(structFibonacciHeap* H,int key){structNode* x =createNode(key);if(!H->min){
          H->min = x;}else{
          x->right = H->min->right;
          x->left = H->min;
          H->min->right->left = x;
          H->min->right = x;if(x->key < H->min->key){
             H->min = x;}}
       H->nodeCount++;}voidprintHeap(structFibonacciHeap* H){if(!H->min){printf("Heap is empty!\n");return;}structNode* temp = H->min;printf("Fibonacci Heap Root List: ");do{printf("%d ", temp->key);
          temp = temp->right;}while(temp != H->min);printf("\n");}structNode*extractMin(structFibonacciHeap* H){structNode* z = H->min;if(z){structNode* x = z->child;if(x){structNode* temp = x;do{structNode* next = temp->right;
                temp->left->right = temp->right;
                temp->right->left = temp->left;
                temp->left = H->min;
                temp->right = H->min->right;
                H->min->right->left = temp;
                H->min->right = temp;
                temp->parent =NULL;
                temp = next;}while(temp != x);}
          z->left->right = z->right;
          z->right->left = z->left;if(z == z->right){
             H->min =NULL;}else{
             H->min = z->right;// consolidate(H); }
          H->nodeCount--;}return z;}intmain(){structFibonacciHeap* fibHeap =createHeap();insert(fibHeap,10);insert(fibHeap,3);insert(fibHeap,15);insert(fibHeap,7);printHeap(fibHeap);structNode* minNode =extractMin(fibHeap);if(minNode){printf("Minimum value extracted: %d\n", minNode->key);free(minNode);}else{printf("Heap is empty!\n");}printHeap(fibHeap);return0;}

    Output

    Following is the output of the above code:

    Fibonacci Heap Root List: 3 7 10 15
    Minimum value extracted: 3
    Fibonacci Heap Root List: 7 10 15
    

    Decrease Key Operation on Fibonacci Heap

    Decreases the key of node x to the new key k. Its a bit complex operation as it involves rearranging the tree structure.

    Algorithm for Decrease Key Operation

    Let’s assume that we have a Fibonacci heap H, a node x and a new key k. The algorithm to decrease key of node x to k in Fibonacci heap H is as follows:

    1: x.key = k
    2: y = x.parent
    3: If y != NULL and x.key < y.key
       1 - Cut x from its parent y.
       2 - Cascading-Cut(H, y).
    4: If x.key < H.min.key, set H.min = x.
    

    Code for Decrease Key Operation

    Let’s write a simple code to decrease key of a node in Fibonacci heap.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>structNode{int key, degree;structNode*parent,*child,*left,*right;int mark;};structFibonacciHeap{structNode* min;int nodeCount;};structNode*createNode(int key){structNode* newNode =(structNode*)malloc(sizeof(structNode));
       newNode->key = key;
       newNode->degree =0;
       newNode->parent = newNode->child =NULL;
       newNode->mark =0;
       newNode->left = newNode->right = newNode;return newNode;}structFibonacciHeap*createHeap(){structFibonacciHeap* heap =(structFibonacciHeap*)malloc(sizeof(structFibonacciHeap));
       heap->min =NULL;
       heap->nodeCount =0;return heap;}voidinsert(structFibonacciHeap* H,structNode* x){if(!H->min){
          H->min = x;}else{
          x->right = H->min->right;
          x->left = H->min;
          H->min->right->left = x;
          H->min->right = x;if(x->key < H->min->key){
             H->min = x;}}
       H->nodeCount++;}voidcut(structFibonacciHeap* H,structNode* x,structNode* y){if(x->right == x){
          y->child =NULL;}else{
          x->right->left = x->left;
          x->left->right = x->right;if(y->child == x){
             y->child = x->right;}}
       y->degree--;
    
       x->left = H->min;
       x->right = H->min->right;
       H->min->right->left = x;
       H->min->right = x;
       x->parent =NULL;
       x->mark =0;}voidcascadingCut(structFibonacciHeap* H,structNode* y){structNode* z = y->parent;if(z){if(!y->mark){
             y->mark =1;}else{cut(H, y, z);cascadingCut(H, z);}}}voiddecreaseKey(structFibonacciHeap* H,structNode* x,int newKey){if(newKey > x->key){printf("New key is greater than current key!\n");return;}
       x->key = newKey;structNode* y = x->parent;if(y && x->key < y->key){cut(H, x, y);cascadingCut(H, y);}if(x->key < H->min->key){
          H->min = x;}}voidprintHeap(structFibonacciHeap* H){if(!H->min){printf("Heap is empty!\n");return;}structNode* temp = H->min;printf("Fibonacci Heap Root List: ");do{printf("%d ", temp->key);
          temp = temp->right;}while(temp != H->min);printf("\n");}intmain(){structFibonacciHeap* fibHeap =createHeap();structNode* n1 =createNode(10);structNode* n2 =createNode(3);structNode* n3 =createNode(15);structNode* n4 =createNode(7);insert(fibHeap, n1);insert(fibHeap, n2);insert(fibHeap, n3);insert(fibHeap, n4);printHeap(fibHeap);decreaseKey(fibHeap, n3,1);printf("After Decrease-Key:\n");printHeap(fibHeap);return0;}

    Output

    Following is the output of the above code:

    Fibonacci Heap Root List: 3 7 10 15
    Fibonacci Heap Root List: 1 10 7 3
    

    Delete Operation on Fibonacci Heap

    Deletes the node x from the heap H. Its a bit complex operation as it involves rearranging the tree structure.

    Algorithm for Delete Operation

    Let’s assume that we have a Fibonacci heap H and a node x. The algorithm to delete node x from Fibonacci heap H is as follows:

    1: Decrease key of node x to -.
    2: Extract minimum node from heap H.
    

    Code for Delete Operation

    Let’s write a simple code to delete a node from Fibonacci heap.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>typedefstructNode{int key, degree;structNode*parent,*child,*left,*right;int mark;} Node;typedefstructFibonacciHeap{
        Node *min;int nodeCount;} FibonacciHeap;
    
    Node*createNode(int key){
        Node* node =(Node*)malloc(sizeof(Node));
        node->key = key;
        node->degree =0;
        node->parent = node->child =NULL;
        node->mark =0;
        node->left = node->right = node;return node;}
    
    FibonacciHeap*createHeap(){
        FibonacciHeap* heap =(FibonacciHeap*)malloc(sizeof(FibonacciHeap));
        heap->min =NULL;
        heap->nodeCount =0;return heap;}voidinsert(FibonacciHeap* heap, Node* x){if(heap->min ==NULL){
            heap->min = x;}else{
            x->right = heap->min->right;
            x->left = heap->min;
            heap->min->right->left = x;
            heap->min->right = x;if(x->key < heap->min->key){
                heap->min = x;}}
        heap->nodeCount++;}voiddecreaseKey(FibonacciHeap* heap, Node* x,int newKey){if(newKey > x->key){printf("New key is greater than current key!\n");return;}
        x->key = newKey;
        Node* y = x->parent;if(y !=NULL&& x->key < y->key){// Implement cut and cascadingCut}if(x->key < heap->min->key){
            heap->min = x;}}
    
    Node*extractMin(FibonacciHeap* heap){
        Node* z = heap->min;if(z !=NULL){if(z->child !=NULL){// Merge child nodes into root list}
            z->left->right = z->right;
            z->right->left = z->left;if(z == z->right){
                heap->min =NULL;}else{
                heap->min = z->right;// Implement consolidate function}
            heap->nodeCount--;}return z;}voiddeleteNode(FibonacciHeap* heap, Node* x){decreaseKey(heap, x, INT_MIN);extractMin(heap);}voidprintHeap(FibonacciHeap* heap){if(heap->min ==NULL){printf("Heap is empty!\n");return;}
        Node* temp = heap->min;printf("Fibonacci Heap Root List: ");do{printf("%d ", temp->key);
            temp = temp->right;}while(temp != heap->min);printf("\n");}intmain(){
        FibonacciHeap* fibHeap =createHeap();
        Node* n1 =createNode(10);
        Node* n2 =createNode(3);
        Node* n3 =createNode(15);
        Node* n4 =createNode(7);insert(fibHeap, n1);insert(fibHeap, n2);insert(fibHeap, n3);insert(fibHeap, n4);printHeap(fibHeap);deleteNode(fibHeap, n3);printf("After Delete:\n");printHeap(fibHeap);return0;}

    Output

    Following is the output of the above code:

    Fibonacci Heap Root List: 3 7 10 15
    Minimum value extracted: 3
    Fibonacci Heap Root List: 7 10 15
    

    Time Complexity of Fibonacci Heap

    Let’s analyze the time complexity of various operations in Fibonacci heap:

    • Insert Operation: O(1)
    • Extract Minimum Operation: O(log n)
    • Decrease Key Operation: O(1)
    • Delete Operation: O(log n)

    Thus, the Fibonacci heap is a data structure that supports insertextract minimumdecrease key, and delete operations in amortized O(log n) time complexity.

    Applications of Fibonacci Heap

    Following are some of the applications of Fibonacci heap:

    • It is used in Dijkstra’s algorithm to find the shortest path in a graph.
    • Also, used in Prim’s algorithm to find the minimum spanning tree of a graph.
    • Used in network routing algorithms.

    Conclusion

    In this tutorial, we learned about Fibonacci heap, its operations, and its time complexity. We also implemented the Fibonacci heap in C, C++, Java, and Python programming languages. We also discussed the applications of Fibonacci heap in various algorithms.

  • Binomial Heaps

    binomial Heap is a collection of Binomial Trees. A binomial tree Bk is an ordered tree defined recursively. A binomial Tree B0 consists of a single node.

    A binomial tree is an ordered tree defined recursively. A binomial tree Bk consists of two binomial trees Bk-1 that are linked together. The root of one is the leftmost child of the root of the other.

    Binomial trees are defined recursively. A binomial tree Bk is defined as follows

    • B0 is a single node.
    • Bk is formed by linking two binomial trees Bk-1 such that the root of one is the leftmost child of the root of the other.

    Some properties of binomial trees are

    • Binomial tree with Bk has 2k nodes.
    • Height of the tree is k
    • There are exactly j! / (i! * (j-i)!) nodes at depth i for all i in range 0 to k.

    What is Binomial Heap?

    As mentioned above, a binomial heap is a collection of binomial trees. These binomial trees are linked together in a specific way. The binomial heap has the following properties

    • Each binomial tree in the heap follows the min-heap property.
    • No two binomial trees in the heap can have the same number of nodes.
    • There is at most one binomial tree of any order.

    Representation of Binomial Heap

    Following is a representation of binomial heap, where Bk is a binomial heap of order k.

    binomial_heap

    Some binomial heaps are like below

    binomial_heap

    Example of Binomial Heap

    This binomial Heap H consists of binomial trees B0, B2 and B3. Which have 1, 4 and 8 nodes respectively. And in total n = 13 nodes. The root of binomial trees are linked by a linked list in order of increasing degree

    binomial_heap

    Operations on Binomial Heap

    Following are the operations that can be performed on a binomial heap

    • Insert: As name suggests, it is used to insert a node into the heap.
    • Union: It is used to merge two binomial heaps into a single binomial heap.
    • Extract-Min: This operation removes the node with the smallest key from the heap.
    • Decrease-Key: This operation decreases the key of a node in the heap.
    • Delete: Simply put, it deletes a node from the heap.

    Implementation of Binomial Heap

    We can implement a binomial heap using a linked list of binomial trees. Each node in the linked list is a binomial tree. The root of each binomial tree is the head of the linked list. The linked list is ordered by the order of the binomial trees. The binomial heap is the head of the linked list.

    Implementation of binomial heap using following steps

    1. Create a new node with the given key.
    2. Merge the new node with the binomial heap.
    3. Adjust the binomial heap to maintain the heap property.
    4. Insert the new node into the binomial heap.
    5. Print the binomial heap.
    

    Code for Binomial Heap

    Following is the implementation of binomial heap in C, C++, Java and Python.

    In the following code, first we create a structure Node to represent a node in the binomial heap. The structure contains the data, degree, child, sibling, and parent of the node. We then define functions to create a new node, merge two binomial trees, union two binomial heaps, adjust the heap, and insert a new node into the heap. Finally, we print the binomial heap.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>structNode{int data, degree;structNode*child,*sibling,*parent;};structNode*newNode(int key){structNode*temp =(structNode*)malloc(sizeof(structNode));
       temp->data = key;
       temp->degree =0;
       temp->child = temp->parent = temp->sibling =NULL;return temp;}structNode*mergeBinomialTrees(structNode*b1,structNode*b2){if(b1->data > b2->data){structNode*temp = b1;
          b1 = b2;
          b2 = temp;}
       b2->parent = b1;
       b2->sibling = b1->child;
       b1->child = b2;
       b1->degree++;return b1;}structNode*unionBinomialHeap(structNode*h1,structNode*h2){if(!h1)return h2;if(!h2)return h1;structNode*newHeap =NULL,**pos =&newHeap;structNode*curr1 = h1,*curr2 = h2;while(curr1 && curr2){if(curr1->degree <= curr2->degree){*pos = curr1;
             curr1 = curr1->sibling;}else{*pos = curr2;
             curr2 = curr2->sibling;}
          pos =&((*pos)->sibling);}*pos =(curr1)? curr1 : curr2;return newHeap;}structNode*adjustHeap(structNode*head){if(!head)returnNULL;structNode*prev =NULL,*curr = head,*next = head->sibling;while(next){if(curr->degree != next->degree ||(next->sibling && next->sibling->degree == curr->degree)){
             prev = curr;
             curr = next;}else{if(curr->data <= next->data){
                curr->sibling = next->sibling;
                curr =mergeBinomialTrees(curr, next);}else{if(!prev) head = next;else prev->sibling = next;
                curr =mergeBinomialTrees(next, curr);}}
          next = curr->sibling;}return head;}structNode*insert(structNode*heap,int key){structNode*temp =newNode(key);returnadjustHeap(unionBinomialHeap(heap, temp));}voidprintTree(structNode*h){while(h){printf("%d ", h->data);printTree(h->child);
          h = h->sibling;}}voidprintHeap(structNode*h){printf("Binomial Heap: \n");while(h){printf("B%d: ", h->degree);printTree(h);printf("\n");
          h = h->sibling;}}intmain(){structNode*hp =NULL;
       hp =insert(hp,10);
       hp =insert(hp,20);
       hp =insert(hp,30);
       hp =insert(hp,40);printHeap(hp);return0;}

    Output

    Following is the output of the above C code

    10 20 30 40
    

    Extract-Min Operation in Binomial Heap

    The extract-min operation removes the node with the smallest key from the binomial heap. The extract-min operation is performed as follows

    1. Find the node with the smallest key in the binomial heap.
    2. Remove the node from the binomial heap.
    3. Now, Let's Merge the children of the node with the binomial heap.
    4. Adjust the binomial heap to maintain the heap property.
    5. Return the new binomial heap.
    

    Code for Extract-Min Operation

    Following is the implementation of extract-min operation in C, C++, Java and Python.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>structNode{int data, degree;structNode*child,*sibling,*parent;};structNode*newNode(int key){structNode*temp =(structNode*)malloc(sizeof(structNode));
       temp->data = key;
       temp->degree =0;
       temp->child = temp->parent = temp->sibling =NULL;return temp;}structNode*mergeBinomialTrees(structNode*b1,structNode*b2){if(b1->data > b2->data){structNode*temp = b1;
          b1 = b2;
          b2 = temp;}
       b2->parent = b1;
       b2->sibling = b1->child;
       b1->child = b2;
       b1->degree++;return b1;}structNode*unionBinomialHeap(structNode*h1,structNode*h2){if(!h1)return h2;if(!h2)return h1;structNode*newHeap =NULL,**pos =&newHeap;structNode*curr1 = h1,*curr2 = h2;while(curr1 && curr2){if(curr1->degree <= curr2->degree){*pos = curr1;
             curr1 = curr1->sibling;}else{*pos = curr2;
             curr2 = curr2->sibling;}
          pos =&((*pos)->sibling);}*pos =(curr1)? curr1 : curr2;return newHeap;}structNode*adjustHeap(structNode*head){if(!head)returnNULL;structNode*prev =NULL,*curr = head,*next = head->sibling;while(next){if(curr->degree != next->degree ||(next->sibling && next->sibling->degree == curr->degree)){
             prev = curr;
             curr = next;}else{if(curr->data <= next->data){
                curr->sibling = next->sibling;
                curr =mergeBinomialTrees(curr, next);}else{if(!prev) head = next;else prev->sibling = next;
                curr =mergeBinomialTrees(next, curr);}}
          next = curr->sibling;}return head;}structNode*insert(structNode*heap,int key){structNode*temp =newNode(key);returnadjustHeap(unionBinomialHeap(heap, temp));}structNode*getMin(structNode*heap){if(!heap)returnNULL;structNode*min = heap;structNode*curr = heap->sibling;while(curr){if(curr->data < min->data) min = curr;
          curr = curr->sibling;}return min;}structNode*extractMin(structNode*heap){if(!heap)returnNULL;structNode*min =getMin(heap);structNode*prev =NULL,*curr = heap;while(curr != min){
          prev = curr;
          curr = curr->sibling;}if(prev) prev->sibling = min->sibling;else heap = min->sibling;structNode*child = min->child;structNode*newHeap =NULL;while(child){structNode*next = child->sibling;
          child->sibling = newHeap;
          child->parent =NULL;
          newHeap = child;
          child = next;}
    
       newHeap =adjustHeap(newHeap);return newHeap;}voidprintTree(structNode*h){while(h){printf("%d ", h->data);printTree(h->child);
          h = h->sibling;}}voidprintHeap(structNode*h){printf("Binomial Heap: \n");while(h){printf("B%d: ", h->degree);printTree(h);printf("\n");
          h = h->sibling;}}intmain(){structNode*hp =NULL;
       hp =insert(hp,10);
       hp =insert(hp,20);
       hp =insert(hp,30);
       hp =insert(hp,40);printHeap(hp);structNode*min =getMin(hp);printf("Min: %d\n", min->data);
    
       hp =extractMin(hp);printHeap(hp);return0;}

    Output

    Following is the output of the above C code

    Binomial Heap:
    B0: 10
    B1: 20
    B2: 30
    B3: 40
    Min: 10
    Binomial Heap:
    B1: 20
    B2: 30
    B3: 40
    

    Decrease-Key Operation in Binomial Heap

    Decrease-key is a important operation in the binomial heap. It is used to decrease the value of a key in the binomial heap. The decrease-key operation is performed as follows

    Algorithm for Decrease-Key Operation

    Follow the steps below to decrease-key.

    1. Decrease the value of the key to a new value.
    2. If the new value is greater than the parent of the node, then return.
    3. If the new value is less than the parent of the node, then swap the values of the node and its parent.
    4. Repeat the above step until the node reaches the root or the parent of the node is less than the node.
    

    Code for Decrease-Key Operation in Binomial Heap

    Following is the implementation of decrease-key operation in C, C++, Java and Python.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>structNode{int data, degree;structNode*child,*sibling,*parent;};structNode*newNode(int key){structNode*temp =(structNode*)malloc(sizeof(structNode));
       temp->data = key;
       temp->degree =0;
       temp->child = temp->parent = temp->sibling =NULL;return temp;}structNode*mergeBinomialTrees(structNode*b1,structNode*b2){if(b1->data > b2->data){structNode*temp = b1;
          b1 = b2;
          b2 = temp;}
       b2->parent = b1;
       b2->sibling = b1->child;
       b1->child = b2;
       b1->degree++;return b1;}structNode*unionBinomialHeap(structNode*h1,structNode*h2){if(!h1)return h2;if(!h2)return h1;structNode*newHeap =NULL,**pos =&newHeap;structNode*curr1 = h1,*curr2 = h2;while(curr1 && curr2){if(curr1->degree <= curr2->degree){*pos = curr1;
             curr1 = curr1->sibling;}else{*pos = curr2;
             curr2 = curr2->sibling;}
          pos =&((*pos)->sibling);}*pos =(curr1)? curr1 : curr2;return newHeap;}structNode*adjustHeap(structNode*head){if(!head)returnNULL;structNode*prev =NULL,*curr = head,*next = head->sibling;while(next){if(curr->degree != next->degree ||(next->sibling && next->sibling->degree == curr->degree)){
             prev = curr;
             curr = next;}else{if(curr->data <= next->data){
                curr->sibling = next->sibling;
                curr =mergeBinomialTrees(curr, next);}else{if(!prev) head = next;else prev->sibling = next;
                curr =mergeBinomialTrees(next, curr);}}
          next = curr->sibling;}return head;}structNode*insert(structNode*heap,int key){structNode*temp =newNode(key);returnadjustHeap(unionBinomialHeap(heap, temp));}structNode*getMin(structNode*heap){if(!heap)returnNULL;structNode*min = heap;structNode*curr = heap->sibling;while(curr){if(curr->data < min->data) min = curr;
          curr = curr->sibling;}return min;}structNode*extractMin(structNode*heap){if(!heap)returnNULL;structNode*min =getMin(heap);structNode*prev =NULL,*curr = heap;while(curr != min){
          prev = curr;
          curr = curr->sibling;}if(prev) prev->sibling = min->sibling;else heap = min->sibling;structNode*child = min->child;structNode*newHeap =NULL;while(child){structNode*next = child->sibling;
          child->sibling = newHeap;
          child->parent =NULL;
          newHeap = child;
          child = next;}
    
       newHeap =adjustHeap(newHeap);return newHeap;}voiddecreaseKey(structNode*heap,structNode*node,int key){if(!heap ||!node)return;if(key > node->data)return;
    
       node->data = key;structNode*parent = node->parent;while(parent && node->data < parent->data){int temp = node->data;
          node->data = parent->data;
          parent->data = temp;
          node = parent;
          parent = node->parent;}}voidprintTree(structNode*h){while(h){printf("%d ", h->data);printTree(h->child);
          h = h->sibling;}}voidprintHeap(structNode*h){while(h){printf("B%d: ", h->degree);printTree(h);printf("\n");
          h = h->sibling;}}intmain(){structNode*hp =NULL;
       hp =insert(hp,10);
       hp =insert(hp,20);
       hp =insert(hp,30);
       hp =insert(hp,40);printf("Binomial Heap: \n");printHeap(hp);structNode*min =getMin(hp);printf("Min: %d\n", min->data);decreaseKey(hp, hp->sibling,5);printf("Binomial Heap after Decrease-Key operation\n");printHeap(hp);return0;}

    Output

    Following is the output of the above C code

    Binomial Heap: 
    B2: 10 30 40 20 
    Min: 10
    Binomial Heap after Decrease-Key operation
    B2: 10 30 40 20
    

    Delete Operation in Binomial Heap

    Delete operation is used for deleting a key from a binomial heap.

    Algorithm for Delete Operation

    1. Decrease the key value to minus infinity.
    2. Now extract the minimum value from the heap.
    

    Code for Delete Operation in Binomial Heap

    Following is the implementation of delete operation in C, C++, Java and Python.

    CC++JavaPython

    #include <stdio.h>#include <stdlib.h>#include <limits.h>structNode{int data, degree;structNode*child,*sibling,*parent;};structNode*newNode(int key){structNode*temp =(structNode*)malloc(sizeof(structNode));
       temp->data = key;
       temp->degree =0;
       temp->child = temp->parent = temp->sibling =NULL;return temp;}structNode*mergeBinomialTrees(structNode*b1,structNode*b2){if(b1->data > b2->data){structNode*temp = b1;
          b1 = b2;
          b2 = temp;}
       b2->parent = b1;
       b2->sibling = b1->child;
       b1->child = b2;
       b1->degree++;return b1;}structNode*unionBinomialHeap(structNode*h1,structNode*h2){if(!h1)return h2;if(!h2)return h1;structNode*newHeap =NULL,**pos =&newHeap;structNode*curr1 = h1,*curr2 = h2;while(curr1 && curr2){if(curr1->degree <= curr2->degree){*pos = curr1;
             curr1 = curr1->sibling;}else{*pos = curr2;
             curr2 = curr2->sibling;}
          pos =&((*pos)->sibling);}*pos =(curr1)? curr1 : curr2;return newHeap;}structNode*adjustHeap(structNode*head){if(!head)returnNULL;structNode*prev =NULL,*curr = head,*next = head->sibling;while(next){if(curr->degree != next->degree ||(next->sibling && next->sibling->degree == curr->degree)){
             prev = curr;
             curr = next;}else{if(curr->data <= next->data){
                curr->sibling = next->sibling;
                curr =mergeBinomialTrees(curr, next);}else{if(!prev) head = next;else prev->sibling = next;
                curr =mergeBinomialTrees(next, curr);}}
          next = curr->sibling;}return head;}structNode*insert(structNode*heap,int key){structNode*temp =newNode(key);returnadjustHeap(unionBinomialHeap(heap, temp));}structNode*getMin(structNode*heap){if(!heap)returnNULL;structNode*min = heap;structNode*curr = heap->sibling;while(curr){if(curr->data < min->data) min = curr;
          curr = curr->sibling;}return min;}structNode*extractMin(structNode*heap){if(!heap)returnNULL;structNode*min =getMin(heap);structNode*prev =NULL,*curr = heap;while(curr != min){
          prev = curr;
          curr = curr->sibling;}if(prev) prev->sibling = min->sibling;else heap = min->sibling;structNode*child = min->child;structNode*newHeap =NULL;while(child){structNode*next = child->sibling;
          child->sibling = newHeap;
          child->parent =NULL;
          newHeap = child;
          child = next;}
    
       newHeap =adjustHeap(newHeap);return newHeap;}voiddeleteKey(structNode**heap,structNode*node){if(!*heap ||!node)return;
    
       node->data = INT_MIN;structNode*parent = node->parent;while(parent && node->data < parent->data){int temp = node->data;
          node->data = parent->data;
          parent->data = temp;
          node = parent;
          parent = node->parent;}*heap =extractMin(*heap);}voidprintTree(structNode*h){while(h){printf("%d ", h->data);printTree(h->child);
          h = h->sibling;}}voidprintHeap(structNode*h){printf("Binomial Heap: \n");while(h){printf("B%d: ", h->degree);printTree(h);printf("\n");
          h = h->sibling;}}intmain(){structNode*hp =NULL;
       hp =insert(hp,10);
       hp =insert(hp,20);
       hp =insert(hp,30);
       hp =insert(hp,40);printHeap(hp);structNode*min =getMin(hp);printf("Min: %d\n", min->data);deleteKey(&hp, hp->sibling);printf("Binomial Heap after Delete operation\n");printHeap(hp);return0;}

    Output

    Following is the output of the above C code

    Binomial Heap:
    B0: 10
    B1: 20
    B2: 30
    B3: 40
    Min: 10
    Binomial Heap after Delete operation
    B0: 10
    B1: 30
    B2: 40
    

    Time Complexity of Binomial Heap

    Following are the time complexities of the Binomial Heap

    • Insertion: O(log n)
    • Deletion: O(log n)
    • Decrease-Key: O(log n)
    • Extract-Min: O(log n)
    • Get-Min: O(1)

    Applications of Binomial Heap

    Binomial heaps are used in plenty of applications. Some of them are listed below

    • Used in implementing priority queues.
    • Dijkstra’s shortest path algorithm.
    • Prim’s minimum spanning tree algorithm.
    • Used in garbage collection.

    Conclusion

    In this tutorial, we have learned about Binomial Heap, its operations, and its implementation in C, C++, Java, and Python. We also discussed the time complexity of the Binomial Heap and its applications.