Hashing
Checking for existence
- With array: Need
O(n)
- With hash map or set: Only need
O(1)
1. Two sum
Problem description - Easy
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.
| def two_sum(nums, target):
hash_map = {}
for i in range(len(nums)):
num = nums[i]
complement = target - num
if complement in hash_map: # This operation is O(1) for the hash map
return [i, hash_map[complement]]
hash_map[num] = i
return [-1, -1]
# Time: O(n)
# Space: O(n) - number of keys of the hash_map scales linearly with input size
|
2351. First letter to appear twice
Problem description - Easy
Given a string s
, return the first character to appear twice. It is guaranteed that the input will have a
duplicate character.
| def appear_twice(s):
seen = set()
for c in s:
if c in seen:
return c
seen.add(c)
# Time: O(n)
# Space: O(1) since number of characters are bounded by 26, constant. Or O(m) with m is number of allowed characters
|
Example 3 - find numbers
Problem description - Easy
Given an integer array nums
, find all the unique numbers x
in nums that satisfy the following: x + 1
is not
in nums
, and x - 1
is not in nums
| def find_nums(nums):
ans = []
nums = set(nums)
for num in nums:
if (num + 1) not in nums and (num - 1) not in nums:
ans.append(num)
return ans
# Time: O(n)
# Space: O(n)
|
1832. Check if the Sentence Is Pangram
Problem description - Easy
A pangram is a sentence where every letter of the English alphabet appears at least once.
Given a string sentence containing only lowercase English letters, return true if sentence is a pangram, or false
otherwise.
| def is_pangram(sentence):
return len(set(sentence)) == 26
# Time: O(n)
# Space: O(1) - bound by 26 characters
|
268. Missing Number
Problem description - Easy
Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that
is missing from the array.
| def missing_number(nums):
num_set = set(nums)
n = len(nums) + 1
for number in range(n):
if number not in num_set:
return number
# Time: O(n)
# Space: O(n)
|
1426. Counting elements
Problem description - Easy
Given an integer array arr, count how many elements x there are, such that x + 1 is also in arr. If there are
duplicates in arr, count them separately
| def count_elements(arr):
num_set = set(arr)
count = 0
for num in arr:
if num + 1 in arr:
count += 1
return count
|
Counting
Using hash map to map keys to integers to track the frequency of things.
Example 1 - longest substring with at most k distinct characters
Problem description
You are given a string s
and an integer k
. Find the length of the longest substring that contains at most k
distinct characters.
For example, given s = "eceba"
and k = 2
, return 3. The longest substring with at most 2 distinct characters is "ece"
| from collections import defaultdict
def longest_substring(s, k):
# Condition: count(distinct c) <= k
counts = defaultdict(int)
left = ans = 0
n = len(s)
for right in range(n):
counts[s[right]] += 1
while len(counts) > k:
counts[s[left]] -= 1
if counts[s[left]] == 0:
del counts[s[left]]
left += 1
ans = max(ans, right - left + 1)
return ans
# Time complexity of sliding window is `O(n)` if the work done in the for loop is amortized constant, which
# is the case here, since hash map having `O(1)` operations
# Space: The hash map will store at most k elements => O(k)
|
2248. Intersection of Multiple Arrays
Problem description - Easy
Given a 2D array nums that contains n
arrays of distinct integers, return a sorted array containing all the numbers
that appear in all n
arrays.
For example, given nums = [[3,1,2,4,5],[1,2,3,4],[3,4,5,6]]
, return [3, 4]
. 3 and 4 are the only numbers that
are in all arrays.
| from collections import defaultdict
def find_arrays_intersect(nums):
counts = defaultdict(int)
for arr in nums:
for num in arr:
counts[num] += 1
ans = [num for num in counts if counts[num] == len(nums)]
return sorted(ans)
# Time complexity: n lists, each list has m elements
# Counting: O(n * m); ans can has max m elements => sorting 0(m * log m) => Total O(m(n+logm))
# Space: If every element in the input is unique => hash map can have size of n*m => O(nm)
|
1941. Check if All Characters Have Equal Number of Occurrences
Problem description - Easy
Given a string s, determine if all characters have the same frequency.
For example, given s = "abacbc", return true. All characters appear twice. Given s = "aaabb", return false.
"a" appears 3 times, "b" appears 2 times. 3 != 2
| from collections import defaultdict
def check_character_same_freq(s):
counts = defaultdict(int)
for c in s:
counts[c] += 1
return len(set(counts.values())) == 1
# Time: O(n) to populate hash map; O(n) to convert hash map to set => O(n)
# Space: O(1) (bounded by 26 characters) or O(k) if k is the number of characters in the input (26)
|
Count the number of subarrays with an "exact" constraint
560. Subarray Sum Equals K
Problem description - Medium
Given an integer array nums and an integer k, find the number of subarrays whose sum is equal to k
| # Constraint metric is sum => curr to track sum
# Using prefix sum and counts to map the prefix to frequency
# Go through counts if seeing curr - k;
from collections import defaultdict
def subarray_exact_sum(nums, k):
counts = defaultdict(int)
counts[0] = 1 # since empty subarray has sum = 0
curr = ans = 0
for num in nums:
curr += num
# if we see curr - k in counts; and currently we are at curr prefix, meaning
# the current subarray has sum = k => increase ans by 1
if counts[curr - k]:
ans += 1
counts[curr] += 1
return ans
# Time: O(n)
# Space: O(n)
|
1248. Count Number of Nice Subarrays
Problem description - Medium
Given an array of integers nums and an integer k. A continuous subarray is called nice if there are k odd
numbers on it.
Return the number of nice sub-arrays.
| # Constraint metric is odd number count => curr to track odd number count
# At each element, if curr - k exists; it means the current subsarray has k odd numbers => increase
# ans by 1
from collections import defaultdict
def subarray_exact_sum(nums, k):
counts = defaultdict(int)
counts[0] = 1 # since empty subarray has number of odd number = 0
curr = ans = 0
for num in nums:
curr += num % 2
# if we see curr - k in counts; and currently we are at curr prefix, meaning
# the current subarray has sum = k => increase ans by 1
ans += counts[curr - k]
counts[curr] += 1
return ans
# Time: O(n)
# Space: O(n)
|
2225. Find Players With Zero or One Losses
Problem description - Medium
You are given an integer array matches where \(matches[i] = [winner_i, loser_i]\) indicates that the player \(winner_i\)
defeated player \(loser_i\) in a match.
Return a list answer of size 2 where:
answer[0]
is a list of all players that have not lost any matches.
answer[1]
is a list of all players that have lost exactly one match.
The values in the two lists should be returned in increasing order.
Note:
- You should only consider the players that have played at least one match.
- The testcases will be generated such that no two matches will have the same outcome.
| # Constraint metric is odd number count => curr to track odd number count
# At each element, if curr - k exists; it means the current subsarray has k odd numbers => increase
# ans by 1
from collections import defaultdict
def list_players(matches):
counts = {}
for winner, loser in matches:
counts[winner] = counts.get(winner, 0)
counts[loser] = counts.get(loser, 0) + 1
zero_loss = sorted([player for player in counts if counts.get(player) == 0])
one_loss = sorted([player for player in counts if counts.get(player) == 1])
return [zero_loss, one_loss]
# Time: O(n*logn)
# Space: O(n)
|
1133. Largest Unique Number
Problem description - Medium
Given an integer array nums, return the largest integer that only occurs once. If no integer occurs once, return -1
| from collections import defaultdict
def largest_unique_number(nums):
counts = defaultdict(int)
for num in nums:
counts[num] += 1
occur_once = [num for num in nums if counts[num] == 1]
if len(occur_once):
return max(occur_once)
else:
return -1
# Time: O(n)
# Space: O(n)
|
1189. Maximum Number of Balloons
Problem description - Easy
Given a string text, you want to use the characters of text to form as many instances of the word "balloon" as possible.
You can use each character in text at most once. Return the maximum number of instances that can be formed.
| from collections import defaultdict
def max_number_of_ballon(text):
counts = defaultdict(int)
for c in text:
counts[c] += 1
ans = min([2 * counts['a'], 2*counts['b'], 2*counts['n'], counts['l'], counts['o']])//2
return ans
# Time: O(n)
# Space: O(1) - bounded by 26 that is constant
|
525. Contiguous Array
Problem description - Easy
Given a binary array nums, return the maximum length of a contiguous subarray with an equal number of 0 and 1.
A contiguous subarray is a subarray of an array with a condition that the elements of the subarray should be
in exact sequence as the sequence of the elements in the array.
For example, array is [1,2,3,4,5] then [1,3,5] is a subarray of the array, but not a contiguous subarray since
the sequence does not match as the elements 2 and 4 are skipped. [1,2,3] will be one of the contiguous subarrays.
| def find_max_length(nums):
# Use hash map to store the relative number of ones and zeros: curr when traverse the list with corresponding index
# When see curr twice, meaning ones and zeros are equal between two indexes
counts = {0: -1}
ans = curr = 0
for i, num in enumerate(nums):
# Update curr (store relative number of ones and zeros): curr increase by 1 if see 1, else decrease by 1
curr += 1 if num == 1 else -1
# If we see curr twice, mean that number of ones and zeros are equal
if curr in counts:
# Update ans, max len will be the difference btw indexes
ans = max(ans, i - counts[curr])
else:
counts[curr] = i
return ans
# Time: O(n)
# Space: O(n)
|
Other hashing examples
49. Group Anagrams
Problem description - Medium
Given an array of strings strs, group the anagrams together.
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
For example, given strs = ["eat","tea","tan","ate","nat","bat"], return [["bat"],["nat","tan"],["ate","eat","tea"]].
| from collections import defaultdict
def group_anagrams(strs):
# two strings are anagrams of each other is if they are equal after both being sorted.
# Also, all strings in a group will be the same when sorted, so we can use the sorted version as a key.
groups = defaultdict(list)
for s in strs:
sorted_str = "".join(sorted(s))
groups[sorted_str].append(s)
return list(groups.values())
# Time: number of strings: n, average string length m
# Sorted m log m; in loop of n iters => n m log m
# Space: O(nm)
|
2260. Minimum Consecutive Cards to Pick Up
Problem description - Medium
You are given an integer array cards where cards[i] represents the value of the ith card. A pair of cards are
matching if the cards have the same value.
Return the minimum number of consecutive cards you have to pick up to have a pair of matching cards among the
picked cards. If it is impossible to have matching cards, return -1.
| def minimum_card_pickup(cards):
ans = float('inf')
seen = dict()
for right, card in enumerate(cards):
if card in seen:
ans = min(ans, right - seen[card] + 1)
seen[card] = right
return ans if ans != float('inf') else -1
# Time: O[n]
# Space: O[n]
|
2342. Max Sum of a Pair With Equal Sum of Digits
Problem description - Medium
Given an array of integers nums, find the maximum value of nums[i] + nums[j], where nums[i] and nums[j] have the
same digit sum (the sum of their individual digits). Return -1 if there is no pair of numbers with the same digit sum.
Solution 1
| from collections import defaultdict
def max_same_digit_sum(nums):
def get_digit_sum(num):
digit_sum = 0
while num:
digit_sum += num % 10
num //= 10
return digit_sum
hash_map = defaultdict(list)
for num in nums:
digit_sum = get_digit_sum(num)
hash_map[digit_sum].append(num)
ans = -1
for curr in hash_map.values():
if len(curr) > 1:
curr.sort(reverse=True)
ans = max(ans, curr[0] + curr[1])
return ans
# This is not efficient due to the sorting, could be potentially cost O(n*log(n)) if all number has same digit sum.
|
Solution 2: not store the whole list, only the max number for each digit sum
| from collections import defaultdict
def max_same_digit_sum(nums):
def get_digit_sum(num):
digit_sum = 0
while num:
digit_sum += num % 10
num //= 10
return digit_sum
hash_map = defaultdict(int)
ans = -1
for num in nums:
digit_sum = get_digit_sum(num)
if digit_sum in hash_map:
ans = max(ans, num + hash_map[digit_sum])
hash_map[digit_sum] = max(hash_map[digit_sum], num)
return ans
# Time O(n), space O(n)
|
2352. Equal Row and Column Pairs
Problem description - Medium
Given an n x n matrix grid, return the number of pairs (R, C) where R is a row and C is a column, and R and C
are equal if we consider them as 1D arrays.
| from collections import defaultdict
def number_of_pairs(grid):
# Use a hash map to count how many times each row occurs; another hash map to count columns
# Iterate over row hash map, check if the array appears as a column, if yes, add the product of appearances to ans
ans = 0
n = len(grid)
m = len(grid[0])
# Count row appearances
row_count = defaultdict(int)
for row in grid:
row_count[tuple(row)] += 1
# Count column appearances
column_count = defaultdict(int)
for j in range(m):
column = []
for i in range(n):
column.append(grid[i][j])
column_count[tuple(column)] += 1
# Iterate over row_count and calculate sum
for key in row_count:
ans += row_count[key] * column_count[key]
return ans
# Time: O[n^2]
# Space: O[n^2]
|