Basic Hashing
1. Introduction to Hashing
- Hashing is a technique used to quickly find, store, and manage data.
- It converts an input (e.g., a number or string) into a fixed-size value called a hash.
- The hash points to the location of the data in a hash table.
- Goal: Make data retrieval fast, even with large datasets.
2. Number Hashing
2.1 Method 1: Basic Traversal and Counting
int[] arr = {5, 6, 5, 6, 9, 6};
int count = 0;
for (int num : arr) {
if (num == 6) {
count++;
}
}
System.out.println(count); // Output: 3
- Time Complexity: O(N)
- Traverses the array once.
- Space Complexity: O(1)
- Uses a single variable for counting.
- Use Case: Simple counting for small datasets.
2.2 Method 2: Hashing for Efficient Counting
int[] arr = {5, 6, 5, 6, 9, 6};
int[] hashTable = new int[10];
for (int num : arr) {
hashTable[num]++;
}
System.out.println(hashTable[6]); // Output: 3
- Time Complexity: O(N)
- Traverses the array once.
- Space Complexity: O(M)
- M is the size of the hash table (10 in this case).
- Use Case: Efficient counting for larger datasets with known value ranges.
3. Character Hashing
3.1 Understanding ASCII Values
- ASCII assigns numerical values to characters:
- Lowercase letters:
'a'
(97) to'z'
(122).
- Lowercase letters:
- Example:
hash['a']++
translates tohash[97]++
.
3.2 Hashing for Lowercase Alphabets
- Optimize by indexing relative to
'a'
:- Example:
hash['b' - 'a'] = hash[1]
.
- Example:
- Advantages:
- Reduces space to 26 indices (0–25).
- Simplifies storage and retrieval.
4. Hashing Implementations
- HashMap:
- Average time complexity: O(1).
- Worst-case time complexity: O(N) (due to collisions).
- TreeMap:
- Time complexity: O(log N).
- Ensures consistent performance.
5. Problems
5.1 Highest Occurring Element in an Array
import java.util.*;
class Solution {
public int mostFrequentElement(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
int maxCount = 0, element = Integer.MAX_VALUE;
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
maxCount = Math.max(map.get(num), maxCount);
}
for (int num : nums) {
if (map.get(num) == maxCount) {
element = Math.min(element, num);
}
}
return element;
}
}
- Time Complexity: O(N)
- Traverses the array twice.
- Space Complexity: O(N)
- Uses a HashMap to store frequencies.
5.2 Second Highest Occurring Element
import java.util.*;
class Solution {
public int secondMostFrequentElement(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
int maxCount = 0, secondMaxCount = 0, element = Integer.MAX_VALUE;
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
for (int count : map.values()) {
if (count > maxCount) {
secondMaxCount = maxCount;
maxCount = count;
} else if (count > secondMaxCount && count < maxCount) {
secondMaxCount = count;
}
}
if (secondMaxCount == 0) return -1;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getValue() == secondMaxCount) {
element = Math.min(element, entry.getKey());
}
}
return element;
}
}
- Time Complexity: O(N)
- Traverses the array and map.
- Space Complexity: O(N)
- Uses a HashMap to store frequencies.
5.3 Sum of Highest and Lowest Frequency
import java.util.*;
class Solution {
public int sumHighestAndLowestFrequency(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
int maxCount = Integer.MIN_VALUE, minCount = Integer.MAX_VALUE;
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
for (int value : map.values()) {
maxCount = Math.max(value, maxCount);
minCount = Math.min(value, minCount);
}
return maxCount + minCount;
}
}
- Time Complexity: O(N)
- Traverses the array and map.
- Space Complexity: O(N)
- Uses a HashMap to store frequencies.
General Notes on Hashing
- Collisions: Occur when two inputs produce the same hash. Handled using techniques like chaining or open addressing.
- Applications: Databases, caching, password storage, and more.
- Trade-offs: Space vs. time complexity.