Hash Table

A summary of hash table related problems from LeetCode with solutions in C++.

Problems:
242. Valid Anagram
349. Intersection of Two Arrays
202. Happy Number
1. Two Sum
454. 4Sum II
383. Ransom Note
15. 3Sum
18. 4Sum

1. 242. Valid Anagram

Problem: Given two strings s and t, return true if t is an anagram of s, and false otherwise. An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Case 1: Input: s = “anagram”, t = “nagaram”, Output: true
Case 2: Input: s = “rat”, t = “car”, Output: false

Hint:

// C++
class Solution {
public:
    bool isAnagram(string s, string t) {
        int hashRecord[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            // Record frequency of each letter in string s
            hashRecord[s[i]-'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            // Deduct frequency of each letter in string t
            hashRecord[t[i]-'a']--;
        } 
        for (int i = 0; i < 26; i++) {
            if (hashRecord[i] != 0) {
                return false;
            }
        }
        return true;
    }
};

Related problems
49. Group Anagrams
438. Find All Anagrams in a String

2. 349. Intersection of Two Arrays

Problem: Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order.

Case 1: Input: nums1 = [1,2,2,1], nums2 = [2,2], Output: [2]
Case 2: Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4], Output: [9,4] or [4,9]

Hint:

Credit to Carl

// C++
class Solution1 {
public:
    // Unordered set method
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // The num of nums2 found in nums_set
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

class Solution2 {
public:
    // Array method, both nums1 and nums2 are not large size
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        int hash[1001] = {0};
        for(int num : nums1) {
            hash[num] = 1;
        }
        for(int num : nums2) {
            if(hash[num] == 1) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

Related problems
350. Intersection of Two Arrays II

3. 202. Happy Number

Problem: Write an algorithm to determine if a number n is happy.
A happy number is a number defined by the following process:
(1) Starting with any positive integer, replace the number by the sum of the squares of its digits.
(2) Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
(3) Those numbers for which this process ends in 1 are happy.
Return true if n is a happy number, and false if not.

Case 1: Input: n = 19, Output: true, Explanation: 12 + 92 = 82, 82 + 22 = 68, 62 + 82 = 100, 12 + 02 + 02 = 1
Case 2: Input: n = 2, Output: false

// C++
class Solution {
public:
    // Get the sum of the squares of every digit
    int getSumSquare(int n) {
        int sumSquare = 0;
        while(n) {
            sumSquare += (n % 10) * (n % 10);
            n /= 10;
        }
        return sumSquare;
    }
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1) {
            int sumSquare = getSumSquare(n);
            if(sumSquare == 1) {
                return true;
            }
            // Check whether sumSquare is already in the set,
            // namely infinite loop, return false
            if(set.find(sumSquare) != set.end()) {
                return false;
            } else {
                set.insert(sumSquare);
            }
            n = sumSquare;
        }
    }
};

4. 1. Two Sum

Problem: Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice. You can return the answer in any order.

Case 1: Input: nums = [2,7,11,15], target = 9, Output: [0,1], Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].
Case 2: Input: nums = [3,2,4], target = 6, Output: [1,2]
Case 3: Input: nums = [3,3], target = 6, Output: [0,1]

Hint:

Credit to Carl

// C++
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // key: num, val: index
        std::unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            // Traverse current num and find whether there 
            // is a matched key (target-num) in the map
            auto iter = map.find(target - nums[i]);
            if (iter != map.end()) {
                return {iter->second, i};
            }
            // There is no matched key (target-num), then
            // add this num to map
            map.insert(pair<int,int>(nums[i], i));
        }
        return {};
    }
};

5. 454. 4Sum II

Problem: Given four integer arrays nums1, nums2, nums3, and nums4 all of length n, return the number of tuples (i, j, k, l) such that:
(1) 0 <= i, j, k, l < n
(2) nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

Case 1: Input: nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2], Output: 2,
Explanation: The two tuples are:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
Case 2: Input: nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0], Output: 1

// C++
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        // key: a+b, val: frequency of a+b
        unordered_map<int,int> umap;
        for (int a : nums1) {
            for (int b : nums2) {
                umap[a+b]++;
            }
        }
        // Get numbers of a+b+c+d=0
        int count = 0;
        for (int c : nums3) {
            for (int d : nums4) {
                if (umap.find(0-(c+d)) != umap.end()) {
                    count += umap[0-(c+d)];
                }
            }
        }
        return count;
    }
};

6. 383. Ransom Note

Problem: Given two strings ransomNote and magazine, return true if ransomNote can be constructed by using the letters from magazine and false otherwise. Each letter in magazine can only be used once in ransomNote.

Case 1: Input: ransomNote = “a”, magazine = “b”, Output: false
Case 2: Input: ransomNote = “aa”, magazine = “ab”, Output: false
Case 3: Input: ransomNote = “aa”, magazine = “aab”, Output: true

// C++
class Solution1 {
public:
    // Brute force
    // Time complexity: O(n^2)
    // Space complexity: O(1) 
    bool canConstruct(string ransomNote, string magazine) {
        for (int i = 0; i < magazine.length(); i++) {
            for (int j = 0; j < ransomNote.length(); j++) {
                if (magazine[i] == ransomNote[j]) {
                    // Delete the letter in ransomNote
                    ransomNote.erase(ransomNote.begin() + j);
                    break;
                }
            }
        }
        // If ransomNote is empty, return true
        if (ransomNote.length() == 0) {
            return true;
        }
        return false;
    }
};

class Solution2 {
public:
    // Hash table method with array
    // Time complexity: O(n)
    // Space complexity: O(1) 
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};
        // Add
        if (ransomNote.size() > magazine.size()) {
            return false;
        }
        for (int i = 0; i < magazine.length(); i++) {
            // Record frequency of each letter in magazine string
            record[magazine[i]-'a']++;
        }
        for (int j = 0; j < ransomNote.length(); j++) {
            // Traverse ransomNote string with each letter
            record[ransomNote[j]-'a']--;
            // Check if ransomNote has the related letter
            if (record[ransomNote[j]-'a'] < 0) {
                return false;
            }
        }
        return true;
    }
};

7. 15. 3Sum

Problem: Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. Notice that the solution set must not contain duplicate triplets.

Case 1: Input: nums = [-1,0,1,2,-1,-4], Output: [[-1,-1,2],[-1,0,1]],
Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0
The distinct triplets are [-1,0,1] and [-1,-1,2]
Notice that the order of the output and the order of the triplets does not matter.
Case 2: Input: nums = [0,1,1], Output: [], Explanation: The only possible triplet does not sum up to 0.
Case 3: Input: nums = [0,0,0], Output: [[0,0,0]], Explanation: The only possible triplet sums up to 0.

// C++
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // Find a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // Return if the smallest value > 0
            if (nums[i] > 0) {
                return result;
            }
            // Remove redundant a correctly
            if (i > 0 && nums[i] == nums[i-1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // right > left not right >= left for "0, 0, 0" case
                if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                } else if (nums[i] + nums[left] + nums[right] < 0) {
                    left++;
                } else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // Remove redundant b and c
                    while (right > left && nums[right] == nums[right-1]) {right--;}
                    while (right > left && nums[left] == nums[left+1]) {left++;}
                    // Update pointers when a solution is found
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

8. 18. 4Sum

Problem: Given an array nums of n integers, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that:
(1) 0 <= a, b, c, d < n
(2) a, b, c, and d are distinct
(3) nums[a] + nums[b] + nums[c] + nums[d] == target
You may return the answer in any order.

Case 1: Input: nums = [1,0,-1,0,-2,2], target = 0, Output: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
Case 2: Input: nums = [2,2,2,2,2], target = 8, Output: [[2,2,2,2]]

// C++
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // Find a + b + c + d = target
        // a = nums[k], b = nums[i], c = nums[left], d = nums[right]
        for (int k = 0; k < nums.size(); k++) {
            // Trim 
            if (nums[k] > target && nums[k] >= 0) {
                break;
            }
            // Remove redundant nums[k]
            if (k > 0 && nums[k] == nums[k-1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                // Trim
                if ((nums[k] + nums[i]) > target && nums[k] + nums[i] >= 0) {
                    break;
                }
                // Remove redundant nums[i]
                if (i > k + 1 && nums[i] == nums[i-1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
                        right--;
                    } else if ((long) nums[k] + nums[i] + nums[left] + nums[right]  < target) {
                        left++;
                    } else {
                        result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
                        // Remove redundant nums[left] and nums[right]
                        while (right > left && nums[right] == nums[right-1]) {right--;}
                        while (right > left && nums[left] == nums[left+1]) {left++;}
                        // Update pointers
                        right--;
                        left++;
                    }
                }
            }
        }
        return result;
    }
};

Back to top of page