Introduction to Hash Table

310 阅读7分钟
    # so far: 17

Hash Table : a data structure which organizes data using hash functions to support quick insertion and search.

The key idea of Hash Tables is to use a hash function to map keys to buckets.

to be specific:

  1. when insert a new key, the hash function will decide which bucket the key should be assigned and the key will be stored in the corresponding bucket

  2. when we want to search for a key, the hash table will use the same hash function to find the corresponding bucket and search only in the specific bucket.

(e.g. using y = x % 5 as the hash function)

in this case, 1987 and 2 collides in the same bucket, collision resolution:

for one bucket, if the size is constant and small, use array typically, if the size is variable or large use height-balanced binary tree

#705. Design HashSet

The trade off between the size of buckets and the number of buckets. for hash set we only use an bool array to decide whether the number is in the set. the trick is to initial with 100 capacity and use Arrays.copyOf(arrayname, new size) to extend the array when is out of size.

class MyHashSet {
public boolean[] set = new boolean[100];
/** Initialize your data structure here. */
public MyHashSet() {
}
public void add(int key) {
    if(key >= set.length) extend(key);
    set[key] = true;
}
public void remove(int key) {
    if(key >= set.length) return;
    set[key] = false;
}
/** Returns true if this set contains the specified element */
public boolean contains(int key) {
    if(key >= set.length) return false;
    return set[key];
}
public void extend(int key){
    set = Arrays.copyOf(set, key + 1);
}
}   

#706. Design HashMap

i tried a two array method (int[] and boolean[]) wasn't too good.

class MyHashMap {
class ListNode{
    ListNode next;
    int val, key;
    ListNode(int key, int val){
        this.key = key;
        this.val = val;
    }
}
public ListNode[] map = new ListNode[100];
/** Initialize your data structure here. */
public MyHashMap() {
    
}
public boolean containsKey(ListNode head, int target){
    while(head != null){
        if(head.key == target) return true;
        head = head.next;
    }
    return false;
}
/** value will always be non-negative. */
public void put(int key, int value) {
    int bucket = key % 100;
    if(map[bucket] == null){
        ListNode head = new ListNode(-1, -1);
        map[bucket] = head;
        head.next = new ListNode(key, value);
    }
    else if(containsKey(map[bucket], key)){
        ListNode curr = map[bucket];
        while(curr.key != key) curr = curr.next;
        curr.val = value;
    }
    else{
        ListNode next = new ListNode(key, value);
        ListNode curr = map[bucket];
        while(curr.next != null){
            curr = curr.next;
        }
        curr.next = next;
    }
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
public int get(int key) {
    int bucket = key % 100;
    ListNode cur = map[bucket];
    while(cur != null && cur.key != key) cur = cur.next;
    if(cur == null) return -1;
    return cur.val;
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
public void remove(int key) {
    int bucket = key % 100;
    ListNode cur = map[bucket], prev = null;
    while(cur != null && cur.key != key){
        prev = cur;
        cur = cur.next;
    }
    if(cur == null) return;
    else{
        if(cur.next == null) prev.next = null;
        else prev.next = cur.next;
    }
    
}
}

by reading a 20ms (100% beat speed) answer i realize the difference between efficiency could be the hash function.

#217. Contains Duplicate

1.hashset 2.sort & two pointer

class Solution {
public boolean containsDuplicate(int[] nums) {
    if(nums.length == 0 || nums.length == 1) return false;
    int i = 0, j = 1;
    Arrays.sort(nums);
    while(j < nums.length){
        if(nums[i++] == nums[j++]) return true;
    }
    return false;
}
}

#136. Single Number

hashMap:

class Solution{
    public int singleNumber(int[] nums){
        Map<Integer,Integer> hm = new HashMap<>();
        for(int num : nums) hm.put(num, hm.getOrDefault(num, 0) + 1);
        for(int num : nums) if(hm.get(num) == 1) return num;
        throw new IllegalArgumentException();
    }
}

xor cheat:

class Solution {
public int singleNumber(int[] nums) {
    int mask = 0;
    for(int num : nums) mask ^= num;
    return mask;
    
}
}

then i realize that it can be accomplished with hashset only: (remove method)

class Solution {
public int singleNumber(int[] nums) {
    int ans = Integer.MAX_VALUE;
    Set<Integer> set = new HashSet<>();
    for(int num : nums){
        if(!set.contains(num)) set.add(num);
        else set.remove(num);
    }
    for(int num : nums)
        if(set.contains(num)) return num;
    throw new IllegalArgumentException();
    
}
}

#349. Intersection of Two Arrays have to be careful dealing with this... few things to be cautious about: 1.the data type of list is Integer instead of int 2.it requires no duplicates in the answer

class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
    List<Integer> list = new ArrayList<>();
    Set<Integer> set = new HashSet<>();
    for(int num : nums1) set.add(num);
    for(int num : nums2){
        if(set.contains(num) && !list.contains(num)){
            list.add(num);
        }
    }
    int[] ans = new int[list.size()];
    int cnt = 0;
    for(Integer i : list) ans[cnt++] = i;
    
    return ans;
}
}

#202. Happy Number

cheat sheet: the non-happynumber goes into the loop of: 4-16-37-58-89-145-42-20-4, so i set 4 (or anyone of this loop) as terminated condition

class Solution {
Set<Integer> set = new HashSet<>();
public boolean isHappy(int n) {
    
    List<Integer> digits = new ArrayList<>();
    while(n != 0){
        digits.add(n % 10);
        n /= 10;
    }
    int next = 0;
    for(Integer i : digits) next += i * i;
    if(set.contains(next) || next == 1) return true;
    if(next == 4) return false;
    return isHappy(next);
}
}

#1. Two Sum

class Solution {
public int[] twoSum(int[] nums, int target) {
    Map<Integer,Integer> hm = new HashMap<>();
    for(int i = 0; i < nums.length; i++){
        if(hm.containsKey(target - nums[i])) return new int[] {hm.get(target - nums[i]), i};
        hm.put(nums[i], i);
    }
    throw new IllegalArgumentException();
}
}

#205. Isomorphic Strings

class Solution {
public boolean isIsomorphic(String s, String t) {
    return compare(s,t) && compare(t,s);
}
public boolean compare(String s, String t){
    Map<Character, Character> hm = new HashMap<>();
    for(int i = 0; i < s.length(); i++){
        if(!hm.containsKey(s.charAt(i))) hm.put(s.charAt(i), t.charAt(i));
        else{
            if(hm.get(s.charAt(i)) != t.charAt(i)) return false;
        }
    }
    return true;
}
}

#599. Minimum Index Sum of Two Lists

class Solution {
public String[] findRestaurant(String[] list1, String[] list2) {
    List<String> res = new ArrayList<>();
    Map<String, Integer> hm = new HashMap<>();
    int min_index_sum = Integer.MAX_VALUE;
    for(int i = 0; i < list1.length; i++){
        hm.put(list1[i], i);
    }
    for(int i = 0; i < list2.length; i++){
        if(hm.containsKey(list2[i])){
            int sum = i + hm.get(list2[i]);
            if(sum < min_index_sum){
                res.removeAll(res);
                min_index_sum = sum;
                res.add(list2[i]);
            }
            else if(sum == min_index_sum) res.add(list2[i]);
        }
    }
    int cnt = 0;
    String[] ans = new String[res.size()];
    for(String s : res) ans[cnt++] = s;
    return ans;
}
}

#387. First Unique Character in a String

class Solution {
public int firstUniqChar(String s) {
    if(s == null) return -1;
    if(s.length() == 1) return 0;
    Map<Character,Integer> hm = new HashMap<>();
    for(int i = 0; i < s.length(); i++) hm.put(s.charAt(i), hm.getOrDefault(s.charAt(i), 0) + 1);
    for(int i = 0; i < s.length(); i++){
        if(hm.get(s.charAt(i)) == 1) return i;
    }
    return -1;
}
}

#350. Intersection of Two Arrays II

class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
    List<Integer> list = new ArrayList<>();
    Map<Integer,Integer> hm = new HashMap<>();
    for(int num : nums1) hm.put(num, hm.getOrDefault(num, 0) + 1);
    for(int num : nums2){
        if(hm.containsKey(num) && hm.get(num) > 0){
            list.add(num);
            hm.put(num, hm.get(num) - 1);
        }
    }
    int[] ans = new int[list.size()];
    int cnt = 0;
    for(int num : list) ans[cnt++] = num;
    return ans;
}   
}

#219. Contains Duplicate II this is the most valuable one so far for a couple reasons:

1.hashmap feature: have more than element, also maintain other information 2.though only one additional slot in for each element, you can get more 3.the way to get more is to compare the "information" during one linear iteration for a array/list/string

class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
    Map<Integer,Integer> hm = new HashMap<>();
    for(int i = 0; i < nums.length; i++){
        if(hm.containsKey(nums[i]) && Math.abs(hm.get(nums[i]) - i) <= k){
            return true;            
        }
        hm.put(nums[i], i);
    }
    return false;
}
}

#49. Group Anagrams designing the key: as the goal of this problem, set the sorted string of each group of anagrams as the key, set the list of all anagrams as the value, compare sorted string for each word and whether add/put.

class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
    Map<String,List<String>> hm = new HashMap<>();
    for(String word : strs){
        char[] chars = word.toCharArray();
        Arrays.sort(chars);
        String sorted = String.valueOf(chars);
        if(hm.containsKey(sorted)){
            hm.get(sorted).add(word);
        }
        else{
            List<String> cur = new ArrayList<>();
            cur.add(word);
            hm.put(sorted, cur);
        }
    }
    List<List<String>> ans = new ArrayList(hm.values());
    return ans;
}
}

#771. Jewels and Stones

class Solution {
public int numJewelsInStones(String J, String S) {
    if(J == null || S == null) return 0;
    int ans = 0;
    Set<Character> hs = new HashSet<>();
    for(int i = 0; i < J.length(); i++){
        hs.add(J.charAt(i));
    }
    for(int i = 0; i < S.length(); i++){
        if(hs.contains(S.charAt(i))) ans++;
    }
    return ans;
}
}

#36. Valid Sudoku

class Solution {
public boolean isValidSudoku(char[][] board) {
    Set[] row = new HashSet[9];
    Set[] col = new HashSet[9];
    Set[] box = new HashSet[9];
    
    for(int i = 0; i < 9; i++){
        row[i] = new HashSet<Character>();
        col[i] = new HashSet<Character>();
        box[i] = new HashSet<Character>();

    }
    
    int k = 0;
    for(int i = 0; i < 9; i++){
        for(int j = 0; j < 9; j++){
            char cur = board[i][j];
            if(cur != '.'){
                k = i / 3 * 3 + j / 3;
                if(col[j].contains(cur) || row[i].contains(cur) || box[k].contains(cur)) 
                    return false;
                row[i].add(cur);
                col[j].add(cur);
                box[k].add(cur);
            }
        }
    }
    return true;
}
}

Arrays.fill fails on this case

#652. Find Duplicate Subtrees

try solutions:

solution1:

class Solution{
    Map<String, Integer> count;
    List<TreeNode> ans;
    public List<TreeNode> findDuplicateSubtrees(TreeNode root){
        count = new HashMao();
        ans = new ArrayList();
        collect(root);
        return ans;
    }
    public String collect(TreeNode node){
        if(node == null) return "#";
        String serial = node.val + "," + collect(node.left) + "," + collect(node.right);
        count.put(serial, count.getOrDefault(serial, 0) + 1);
    }
}

solution2:

computeIfAbsent() as says, using:

HashMap<> hm = new HashMap<>();

hm.computeIfAbsent(key_name, function) function usually be lambda expression.

such as: hm.computeIfAbsent(keyname, x -> t++)

lambda expression:

(parameters) -> expression

or

(parameters) -> {statements;}

e.g:

() -> 5 // no parameters and return 5

x -> 2 * x // receives x and return its twice

(x, y) -> x + y //

(int x, int y) -> x + y // returns sum of two int

(String s) -> System.out.print(s) //not return

class Solution{
    int t;
    Map<String, Integer> trees;
    Map<Integer, Integer> count;
    List<TreeNode> ans;
    
    public List<TreeNode> findDuplicateSubtrees(TreeNode root){
        t = 1;
        trees = new HashMap();
        count = new HashMap();
        ans = new ArrayList();
        lookup(root);
        return ans;
    }
    
    public int lookup(TreeNode node){
        if(node == null) return 0;
        String serial = node.val + "," + lookup(node.left) + "," + lookup(node.right);
        int uid = trees.computeIfAbsent(serial, x-> t++);
        count.put(uid, count.getOrDefault(uid, 0) + 1);
        if(count.get(uid) == 2) ans.add(node);
        return uid;
    }
}

#3. Longest Substring Without Repeating Characters

solution 1 : brute force

class Solution{
    public int lengthOfLongestSubstring(String s){
        if(s == null || s.length() == 1) return s.length();
        int ans = 0, n = s.length();
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j <= n; j++){
                if(allUnique(s.substring(i, j)) ans = Math.max(j - i, ans);
            }
        }
        return ans;
    }
    public boolean allUnique(String substring){
        Set<Character> hs = new HashSet<>();
        for(int i = 0; i < substring.length(); i++){
            if(hs.contains(s.charAt(i)) return false;
            hs.add(s.charAt(i));
        }
        return true;
    }
}

all test cases but one longest passed, time complexity O(n^3)

solution 2 : sliding window

class Solution{
    public int lengthOfLongestSubstring(String s){
        if(s == null || s.length() == 1) return s.length();
        int n = s.length(), i = 0, j = 0, ans = 0;
        Set<Character> hs = new HashSet<>();
        while(i < n && j < n){
            if(!hs.contains(s.charAt(j)){
                hs.add(s.charAt(j++));
                ans = Math.max(ans, j - i);
            }
            else{
                hs.remove(s.charAt(i++);
            }
        }
        return ans;
    }
}

#454. 4Sum II

class Solution{
    public int fourSumCount(int[] a, int[] b, int[] c, int[] d){
        Map<Integer, Integer> hm = new HashMap<>();
        int n = a.length, ans = 0;
        
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                int sum = a[i] + b[j];
                hm.put(sum, hm.getOrDefault(sum, 0) + 1);
            }
        }
        
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                int sum = -1 * (c[i] + d[j]);
                ans += hm.getOrDefault(sum, 0);
            }
        }
        
        return ans;
    }
}