
Data Structure
Networking
RDBMS
Operating System
Java
MS Excel
iOS
HTML
CSS
Android
Python
C Programming
C++
C#
MongoDB
MySQL
Javascript
PHP
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
- Who is Who
Efficient Data Structure Design
In order to design efficient data structures for specific operations, the time and space complexity of the given operations for the data structure created is important. Looking into some basic operations and how they can be efficiently optimized
-
insert() Inserts an element to the data structure
Dynamic Arrays, Hash Tables, Binary Search Trees and Balanced Search Trees like AVL Trees or Red-Black Trees are the most efficient choice of data structures providing O(1) complexity for insertions operation.
-
delete() Deletes an element from the data structure
Hash tables approach the deletion process in O(1) time while Binary Search Trees and Balanced Search Trees like AVL Trees or Red-Black Trees take O(logN) time for the deletion operation.
-
search() Searches for a given element in the data structure and returns if it is present or not
Hash tables and HashMap with Separate Chaining take O(1) time for searching while Binary Search Trees and Balanced Search Trees like AVL Trees or Red-Black Trees take O(logN) time for the searching operation.
-
getRandom() Returns a random element stored in the data structure
Dynamic Array with random shuffling and HashMap offer O(1) complexity to get a random element from the data structure while Balanced Search Trees like AVL Trees or Red-Black Trees take o(logN) time for the same operation.
Problem Statement
Design an efficient data structure with minimal time and space complexity for the following operations
insert()
remove()
search()
getRandom()
removeRandom()
size()
Sample Example
Input
insert(2)
insert(3)
insert(4)
search(6)
getRandom()
remove(2)
size()
removeRandom()
Output
-
-
-
False
3
-
2
4
Explanation
Data structure on initialization: A ={}
On insertion of 2: A={2}
On insertion of 3: A={2,3}
On insertion of 4: A={2,3,4}
On searching 6 in A: False
Getting a random element from A: 3
Removing 2 from A: A={3,4}
The size of the data structure is 2
Deleting a random element from A: A={3} (4 is deleted and returned)
Solution Approach
Use the following for defining our data structure
Unordered Map It stores the elements of the data structure as keys and their indexes in a dynamic array. It is used to allow efficient access and deletion of elements.
Dynamic Array It stored the elements of the data structure in the order they were inserted. It allows the efficient working of random functions.
Random number generator
Algorithm
Initialize an empty set (element) to store the values in the order elements are added.
Initialize a hasp map with keys as an element and values as the index of the elements stored in the set.
Initialize a random number generator.
-
Implement the following methods
insert(ele) add an element to the set if it is not present and add the index along with the element to the hashmap.
search(ele) searches for the element by searching the hashmap.
getRandom() the random number generates a random index and the element at that index is returned.
removeRandom() the random number generator generates a random index and the element at that index is deleted from both the set and hashmap.
remove() removing a specific element by getting its index from the hashmap and deleting the element from both set and hashmap.
size() returns the size of the set.
Example
#include <iostream> #include <vector> #include <unordered_map> #include <stdexcept> #include <random> #include <ctime> #include <algorithm> template <typename T> class RandomizedSet { private: std::unordered_map<T, int> elementIndexes; std::vector<T> element; std::mt19937 rand; public: RandomizedSet() { elementIndexes.clear(); element.clear(); rand = std::mt19937(std::time(0)); } void insert(T ele) { if (!search(ele)) { int lastIndex = element.size(); elementIndexes[ele] = lastIndex; element.push_back(ele); } } bool search(T ele) { return elementIndexes.find(ele) != elementIndexes.end(); } T getRandom() { if (elementIndexes.empty()) { throw std::out_of_range("Empty set, cannot get a random element."); } std::uniform_int_distribution<int> dist(0, element.size() - 1); int randomIndex = dist(rand); return element[randomIndex]; } T removeRandom() { if (elementIndexes.empty()) { throw std::out_of_range("Empty set, cannot remove a random element."); } std::uniform_int_distribution<int> dist(0, element.size() - 1); int randomIndex = dist(rand); return removeElement(randomIndex); // Corrected function name } T remove(T ele) { if (!search(ele)) { throw std::out_of_range("Element not found in the set."); } int index = elementIndexes[ele]; return removeElement(index); // Corrected function name } int size() { if (element.size() != elementIndexes.size()) { throw std::runtime_error("Inconsistent set size, internal error."); } return element.size(); } private: T removeElement(int currentIndex) { // Corrected function name T currentElement = element[currentIndex]; int lastIndex = element.size() - 1; T lastVal = element[lastIndex]; std::swap(element[currentIndex], element[lastIndex]); element.pop_back(); elementIndexes[lastVal] = currentIndex; elementIndexes.erase(currentElement); return currentElement; } }; #include <iostream> int main() { // Create an instance of the RandomizedSet for integers RandomizedSet<int> mySet; // Insert elements mySet.insert(42); mySet.insert(99); // Check if an element exists bool exists = mySet.search(42); // Remove a specific element mySet.remove(42); // Get a random element int randomElement = mySet.getRandom(); // Get the size of the set int setSize = mySet.size(); // Output results std::cout << "Element 42 exists: " << exists << std::endl; std::cout << "Random element: " << randomElement << std::endl; std::cout << "Set size: " << setSize << std::endl; return 0; }
Output
Element 42 exists: 1 Random element: 99 Set size: 1
Time Complexity
insert() = O(1)
search() = O(1)
getRandom() = O(1)
removeRandom() = O(1)
remove() = O(1)
size() = O(1)
Space Complexity
element[] array = O(n)
elementIndexes hashmap = O(n)
Conclusion
In conclusion, the RandomizedSet structure provides an efficient approach to perform functions with O(1) complexity. The operations included insert, search, getRandom, removeRandom, remove and size.