-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #767 from iking07/Trie
Add Important concepts In Trie section
- Loading branch information
Showing
6 changed files
with
234 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#define ALPHABET_SIZE 26 | ||
|
||
// Trie Node Structure | ||
struct TrieNode { | ||
struct TrieNode *children[ALPHABET_SIZE]; // Each node has 26 possible children (for each alphabet letter) | ||
int isEndOfWord; // 1 if this node marks the end of a word | ||
}; | ||
|
||
// Function to create a new Trie node | ||
struct TrieNode *createNode() { | ||
struct TrieNode *node = (struct TrieNode *)malloc(sizeof(struct TrieNode)); | ||
node->isEndOfWord = 0; | ||
for (int i = 0; i < ALPHABET_SIZE; i++) | ||
node->children[i] = NULL; // Initialize all children to NULL | ||
return node; | ||
} | ||
|
||
// Insert a word into the Trie | ||
void insert(struct TrieNode *root, const char *word) { | ||
struct TrieNode *current = root; | ||
while (*word) { | ||
int index = *word - 'a'; // Calculate index for each character (0 for 'a', 1 for 'b', etc.) | ||
if (!current->children[index]) | ||
current->children[index] = createNode(); // Create new node if it doesn't exist | ||
current = current->children[index]; | ||
word++; | ||
} | ||
current->isEndOfWord = 1; // Mark the end of a valid word | ||
} | ||
|
||
// Depth-First Search (DFS) to collect words in lexicographical order | ||
void dfs(struct TrieNode *root, char *currentWord, int level, int *k, char *result) { | ||
if (*k <= 0) return; // If we have found the k-th word, stop searching | ||
|
||
// If this node marks the end of a word, check if it's the k-th word | ||
if (root->isEndOfWord) { | ||
(*k)--; // Decrease k when a valid word is found | ||
if (*k == 0) { | ||
currentWord[level] = '\0'; // Null-terminate the word | ||
strcpy(result, currentWord); // Copy the word to result | ||
return; | ||
} | ||
} | ||
|
||
// Recursively search for children in alphabetical order | ||
for (int i = 0; i < ALPHABET_SIZE; i++) { | ||
if (root->children[i]) { | ||
currentWord[level] = 'a' + i; // Add the current letter to the word | ||
dfs(root->children[i], currentWord, level + 1, k, result); // Go deeper in the Trie | ||
} | ||
} | ||
} | ||
// Function to find the k-th lexicographical string | ||
void findKthWord(struct TrieNode *root, int k, char *result) { | ||
char currentWord[100]; // To store the current word during DFS | ||
dfs(root, currentWord, 0, &k, result); // Start DFS with level 0 | ||
} | ||
|
||
int main() { | ||
struct TrieNode *root = createNode(); // Create the root of the Trie | ||
char *words[] = {"banana", "apple", "grape", "mango", "orange"}; | ||
int n = 5, k = 3; // Example: 5 words and find the 3rd lexicographical word | ||
|
||
// Insert all words into the Trie | ||
for (int i = 0; i < n; i++) | ||
insert(root, words[i]); | ||
|
||
char result[100]; // To store the k-th lexicographical word | ||
findKthWord(root, k, result); // Find the k-th word | ||
|
||
printf("The %d-th lexicographical word is: %s\n", k, result); | ||
return 0; | ||
} | ||
// The 3-th lexicographical word is: grape |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
## Problem: K-th Lexicographical String in Trie | ||
|
||
### Problem Statement: | ||
Given a set of strings, your task is to find the **k-th lexicographical string** from this set. The lexicographical order is the same as dictionary order, where words are sorted alphabetically. To solve this problem efficiently, we can use a **Trie (Prefix Tree)**, which naturally maintains the order of characters and helps in finding lexicographical order. | ||
|
||
### Approach Overview: | ||
A **Trie (Prefix Tree)** allows us to store words in a hierarchical structure, with each node representing a character. By traversing this Trie in alphabetical order, we can collect the strings in lexicographical order and directly retrieve the k-th word. | ||
|
||
### Steps: | ||
|
||
1. **TrieNode Structure**: | ||
- Each node in the Trie has an array `children[26]` for the lowercase English alphabet (`'a'` to `'z'`). | ||
- `isEndOfWord` marks if the node is the end of a valid word. | ||
- Each node can have 26 possible children, corresponding to each letter. | ||
|
||
2. **Insertion**: | ||
- Each word from the input list is inserted into the Trie, character by character. | ||
- If a node for a character does not exist, a new node is created. | ||
- At the end of each word, mark the final node as an `end of word`. | ||
|
||
3. **Depth-First Search (DFS)** for K-th Lexicographical Word: | ||
- After inserting the words into the Trie, perform a DFS traversal. | ||
- Start at the root node and visit each child in lexicographical order (starting from `'a'` to `'z'`). | ||
- For each word ending node, decrement the counter `k`. | ||
- Once `k` reaches zero, the current string is the desired k-th lexicographical string. | ||
|
||
### Example: | ||
```c | ||
char *words[] = {"banana", "apple", "grape", "mango", "orange"}; | ||
int k = 3; | ||
``` | ||
1. Insert all the words into the Trie. | ||
2. Traverse the Trie using DFS and find the 3rd lexicographical word. | ||
**Output**: | ||
``` | ||
The 3rd lexicographical word is: grape | ||
``` | ||
### Key Points: | ||
- The Trie efficiently organizes words such that DFS traversal yields lexicographical order. | ||
- The traversal is designed to visit child nodes from `'a'` to `'z'`, ensuring that words are checked in alphabetical order. | ||
- The algorithm stops when the k-th lexicographical word is found. | ||
### Time Complexity: | ||
- **Insertion**: Inserting all words into the Trie takes O(n * m) time, where `n` is the number of words, and `m` is the average length of the words. | ||
- **Finding the k-th word**: Traversing the Trie in lexicographical order takes O(26 * m), where `m` is the length of the longest word. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,4 +115,4 @@ int main() | |
printf("Longest Common Prefix: %s\n", prefix); | ||
|
||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
#define BITS 31 // We consider 31 bits for non-negative integers | ||
|
||
// Trie Node Structure for Binary Trie | ||
struct TrieNode { | ||
struct TrieNode *children[2]; // Binary Trie has 2 children (for bit 0 and bit 1) | ||
}; | ||
|
||
// Function to create a new Trie node | ||
struct TrieNode *createBinaryNode() { | ||
struct TrieNode *node = (struct TrieNode *)malloc(sizeof(struct TrieNode)); | ||
node->children[0] = node->children[1] = NULL; // Initialize both children to NULL | ||
return node; | ||
} | ||
|
||
// Insert a number into the binary Trie | ||
void insertBinaryTrie(struct TrieNode *root, int num) { | ||
struct TrieNode *current = root; | ||
for (int i = BITS; i >= 0; i--) { | ||
int bit = (num >> i) & 1; // Get the i-th bit of the number | ||
if (!current->children[bit]) | ||
current->children[bit] = createBinaryNode(); // Create a new node for this bit | ||
current = current->children[bit]; // Move to the child corresponding to this bit | ||
} | ||
} | ||
|
||
// Find the maximum XOR with a given number in the Trie | ||
int findMaxXOR(struct TrieNode *root, int num) { | ||
struct TrieNode *current = root; | ||
int maxXOR = 0; | ||
for (int i = BITS; i >= 0; i--) { | ||
int bit = (num >> i) & 1; // Get the i-th bit of the number | ||
int oppositeBit = 1 - bit; // We want to find the opposite bit to maximize XOR | ||
if (current->children[oppositeBit]) { | ||
maxXOR = (maxXOR << 1) | 1; // If opposite bit exists, add 1 to maxXOR | ||
current = current->children[oppositeBit]; // Move to the opposite bit node | ||
} else { | ||
maxXOR = (maxXOR << 1); // If opposite bit doesn't exist, add 0 to maxXOR | ||
current = current->children[bit]; // Move to the same bit node | ||
} | ||
} | ||
return maxXOR; | ||
} | ||
|
||
// Function to find the maximum XOR of two numbers in an array | ||
int findMaximumXOR(int *nums, int n) { | ||
struct TrieNode *root = createBinaryNode(); // Create the root of the Trie | ||
insertBinaryTrie(root, nums[0]); // Insert the first number into the Trie | ||
|
||
int maxResult = 0; // Variable to store the maximum XOR result | ||
|
||
// Process the rest of the numbers | ||
for (int i = 1; i < n; i++) { | ||
// Find the maximum XOR for the current number and update maxResult | ||
maxResult = (maxResult > findMaxXOR(root, nums[i])) ? maxResult : findMaxXOR(root, nums[i]); | ||
insertBinaryTrie(root, nums[i]); // Insert the current number into the Trie | ||
} | ||
|
||
return maxResult; // Return the maximum XOR found | ||
} | ||
int main() { | ||
int nums[] = {3, 10, 5, 25, 2, 8}; | ||
int n = sizeof(nums) / sizeof(nums[0]); | ||
|
||
int maxXOR = findMaximumXOR(nums, n); // Find the maximum XOR | ||
printf("Maximum XOR is: %d\n", maxXOR); | ||
|
||
return 0; | ||
} | ||
// Maximum XOR is: 28 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
## Problem: Maximum XOR of Two Numbers | ||
|
||
### Problem Statement: | ||
Given an array of non-negative integers, find the maximum XOR of two numbers in the array. The XOR operation between two integers results in a number where each bit is `1` if the corresponding bits of the two numbers differ, and `0` if they are the same. | ||
|
||
### Approach Overview: | ||
To find the maximum XOR of two numbers in an array, we use a **Binary Trie** data structure. This allows us to efficiently compare bits of numbers and attempt to maximize the XOR value. | ||
|
||
### Steps: | ||
1. **Binary TrieNode Structure**: | ||
- Each node has two children: `children[0]` for the bit `0` and `children[1]` for the bit `1`. | ||
- We represent numbers in their binary form (using up to 31 bits). | ||
|
||
2. **Insertion**: | ||
- Each number in the array is inserted into the Trie bit by bit, starting from the most significant bit (MSB). | ||
- New nodes are created as necessary for each bit (`0` or `1`). | ||
|
||
3. **Finding Maximum XOR**: | ||
- For each number, we traverse the Trie and try to maximize the XOR by choosing the opposite bit at each step (e.g., if the current bit is `0`, try to go to `1`, and vice versa). | ||
- The XOR is maximized when we take the opposite bit at each level of the Trie. | ||
|
||
### Example: | ||
```c | ||
int nums[] = {3, 10, 5, 25, 2, 8}; | ||
``` | ||
1. Insert the numbers into the Trie. | ||
2. For each number, calculate the maximum XOR by traversing the Trie and choosing the path that gives the highest XOR. | ||
**Output**: | ||
``` | ||
Maximum XOR: 28 | ||
``` | ||
### Key Points: | ||
- The Binary Trie allows efficient lookup and insertion of binary representations of numbers. | ||
- The XOR is maximized by selecting the opposite bit at each level, ensuring we get the maximum possible value. |