Efficient Searching Techniques (Hashing, Binary Search Trees)

When it comes to competitive programming, efficient searching techniques play a crucial role in optimizing algorithms and reducing the time complexity of solutions. Two widely used efficient searching techniques are Hashing and Binary Search Trees (BST). In this article, we will explore these techniques and understand how they can be leveraged to improve the efficiency of your code.

Hashing

Hashing is a technique that allows us to store and retrieve data in constant time, making it an ideal choice for fast retrieval of information. It works by mapping data to an array index through a hash function. The hash function takes an input (data) and produces a fixed-size numerical value known as a hash code. This hash code is then used as an index to store the data in an array called a hash table.

Benefits of Hashing:

  • Fast retrieval: Hashing allows us to retrieve data in constant time, irrespective of the input size. This is especially useful when dealing with large datasets.

  • Reduced time complexity: Hashing can significantly reduce the time complexity of certain algorithms by providing efficient data lookup and retrieval.

Implementing Hashing in C++:

In C++, one can implement hashing using the unordered_map container provided by the Standard Template Library (STL). This container uses a hash table internally for efficient storage and retrieval of data.

Here's a simple example that demonstrates the usage of unordered_map for hashing:

#include <unordered_map>
#include <iostream>

int main() {
    std::unordered_map<int, std::string> hashTable;

    // Inserting data into the hash table
    hashTable[1] = "Alice";
    hashTable[2] = "Bob";
    hashTable[3] = "Charlie";

    // Accessing data using keys
    std::cout << hashTable[2] << std::endl; // Output: Bob

    return 0;
}

In the above example, we create an unordered_map called hashTable and insert key-value pairs into it. We can then access the values using their respective keys in constant time.

Binary Search Trees

Binary Search Trees (BST) offer an efficient way to store and search for values in a sorted manner. In a binary search tree, each node has two children (left and right), and the values in the left subtree are smaller than the values in the root, while the values in the right subtree are greater.

Benefits of Binary Search Trees:

  • Efficient searching: Binary search trees allow for efficient searching of values by narrowing down the search space on each comparison. This results in a time complexity of O(log N), where N is the number of elements.

  • Ordered traversal: Binary search trees provide the ability to traverse the tree in sorted order, making it easier to perform tasks such as finding the next or previous element.

Implementing Binary Search Trees in C++:

C++ does not provide a built-in container for binary search trees. However, you can implement a binary search tree using custom data structures and pointers.

#include <iostream>

struct Node {
    int value;
    Node* left;
    Node* right;
};

Node* createNode(int value) {
    Node* newNode = new Node();
    if (newNode) {
        newNode->value = value;
        newNode->left = newNode->right = nullptr;
    }
    return newNode;
}

Node* insertNode(Node* root, int value) {
    if (root == nullptr) {
        return createNode(value);
    }
    if (value < root->value) {
        root->left = insertNode(root->left, value);
    }
    else if (value > root->value) {
        root->right = insertNode(root->right, value);
    }
    return root;
}

void inorderTraversal(Node* root) {
    if (root == nullptr) {
        return;
    }
    inorderTraversal(root->left);
    std::cout << root->value << " ";
    inorderTraversal(root->right);
}

int main() {
    Node* root = nullptr;
    root = insertNode(root, 50);
    insertNode(root, 30);
    insertNode(root, 20);
    insertNode(root, 40);
    insertNode(root, 70);

    std::cout << "Inorder Traversal: ";
    inorderTraversal(root); // Output: 20 30 40 50 70

    return 0;
}

In the above example, we create a binary search tree from scratch. We provide functions for creating new nodes, inserting elements, and performing an inorder traversal of the tree. The inorder traversal displays the elements in sorted order.

Conclusion

Efficient searching techniques such as hashing and binary search trees are essential tools in competitive programming. Hashing enables us to retrieve data in constant time, while binary search trees provide efficient searching and ordered traversal. By understanding and leveraging these techniques, you can optimize your algorithms and improve the efficiency of your code, leading to better performance in competitive programming challenges.


noob to master © copyleft