
Hashing | Maps | Time Complexity | Collisions | Division Rule of Hashing | Strivers A2Z DSA Course
take U forward
Overview
This video introduces hashing as a technique to efficiently store and retrieve data, significantly improving upon brute-force methods. It covers basic hashing concepts, including frequency counting for numbers and characters using arrays. The limitations of array-based hashing for large numbers are discussed, leading to an introduction of `map` and `unordered_map` data structures in C++ STL. The video also touches upon the division method for hashing and the concept of collisions, explaining why `unordered_map` offers better average-case performance.
Save this permanently with flashcards, quizzes, and AI chat
Chapters
- Many interview problems require efficient data retrieval, and brute-force methods (like linear search) can be too slow.
- A common problem is counting the frequency of elements in an array.
- The brute-force approach involves iterating through the array for each query, resulting in O(N) time complexity per query.
- For Q queries and an array of size N, the total time complexity becomes O(Q*N), which is inefficient for large datasets.
- Hashing allows pre-storing and fast fetching of data.
- For numbers within a known, limited range, an auxiliary array (hash array or frequency array) can be used.
- The value of the number itself serves as the index in the hash array.
- Pre-computation involves iterating through the input array and incrementing the count at the corresponding index in the hash array.
- Fetching the frequency of a number then becomes an O(1) operation by directly accessing the hash array.
- Array-based hashing is impractical when numbers are very large (e.g., 10^9 or 10^12) due to memory constraints (max array size is around 10^6 to 10^7).
- Character hashing is feasible using arrays because the number of possible characters (ASCII) is limited (256).
- Lowercase characters can be mapped to indices 0-25 using the formula `character - 'a'`.
- For any character, its ASCII value can be used directly as an index in a 256-sized array.
- When array indices become too large or keys are not simple integers/characters, `map` and `unordered_map` (hash maps) are used.
- A `map` stores key-value pairs and keeps keys sorted, offering O(log N) time complexity for insertion and retrieval.
- An `unordered_map` also stores key-value pairs but does not maintain order, providing an average time complexity of O(1) for insertion and retrieval.
- The worst-case time complexity for `unordered_map` is O(N), which occurs due to internal collisions.
- Hashing aims to map keys to array indices, but multiple keys can sometimes map to the same index, causing a collision.
- The division method is a common technique where a key is modulated by the hash table size (e.g., `key % table_size`) to determine its index.
- Collisions are handled using techniques like separate chaining (using linked lists at each index) or open addressing.
- The worst-case scenario for hash maps occurs when all keys hash to the same index, degrading performance to O(N).
Key takeaways
- Hashing is a technique that maps keys to values for efficient data lookup, often achieving O(1) average time complexity.
- Array-based hashing is efficient for data within a known, limited range but fails for very large numbers due to memory limits.
- Character hashing is effectively done using arrays by mapping characters to their ASCII values or relative positions (e.g., `char - 'a'`).
- `std::map` (or Java's `TreeMap`) provides ordered key-value storage with O(log N) complexity, suitable when order matters or for complex keys.
- `std::unordered_map` (or Java's `HashMap`) offers average O(1) complexity for key-value operations by using hashing internally, making it ideal for most frequency counting and lookup tasks.
- Collisions are inevitable in hashing and can degrade `unordered_map` performance to O(N) in the worst case, though this is rare.
- The choice between `map` and `unordered_map` depends on whether sorted order is required or if average O(1) performance is prioritized.
Key terms
Test your understanding
- What is the primary advantage of using hashing over a brute-force approach for frequency counting?
- Why is array-based hashing not suitable for numbers as large as 10^9?
- How can you efficiently hash characters using an array, and what is the formula for lowercase English letters?
- What is the difference in time complexity and ordering between `std::map` and `std::unordered_map`?
- Explain what a hash collision is and how it can impact the performance of an `unordered_map`.