leetcode -- hash table column

Prerequisite knowledge

Theoretical basis of hash table C + + version

Topic overview

242. Valid acronyms
349. Intersection of two arrays
202. Happy number
1. Sum of two numbers
454. Adding four numbers II
383. Ransom letter
15. Sum of three
18. Sum of four numbers

Detailed explanation of the topic

242. Valid acronyms

Overall thinking
Method 1: traverse two strings, establish two hash tables, store the number of each character, and compare once. Very slow

    public static boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) return false;
        HashMap<Character, Integer> map1 = new HashMap<>();
        HashMap<Character, Integer> map2 = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            map1.put(s.charAt(i), map1.getOrDefault(s.charAt(i), 0) + 1);
            map2.put(t.charAt(i), map2.getOrDefault(t.charAt(i), 0) + 1);
        }
        for (Character a : map1.keySet()){
            if (!map1.get(a).equals(map2.get(a))){
                return false;
            }
        }
        return true;
    }

However, the above time complexity and spatial complexity are very high. In terms of spatial complexity, only one map can be used. When characters are encountered in string 1, it will increase by 1, and when characters are encountered in string 2, it will decrease by 1.

    public static boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) return false;
        HashMap<Character, Integer> map1 = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            map1.put(s.charAt(i), map1.getOrDefault(s.charAt(i), 0) + 1);
            map1.put(t.charAt(i), map1.getOrDefault(t.charAt(i), 0) - 1);
        }
        for (Character a : map1.keySet()){
            if (!map1.get(a).equals(0)){
                return false;
            }
        }
        return true;
    }

This will reduce the spatial complexity.
Method 2: the answer is to use the array, because the hash table query is similar to O(1) and has its own internal adjustment time, while the array is standard O(1) and all lowercase letters, which can be used only 26 bytes.

class Solution {
    public boolean isAnagram(String s, String t) {

        int[] record = new int[26];
        for (char c : s.toCharArray()) {
            record[c - 'a'] += 1;
        }
        for (char c : t.toCharArray()) {
            record[c - 'a'] -= 1;
        }
        for (int i : record) {
            if (i != 0) {
                return false;
            }
        }
        return true;
    }
}

be careful:
After our own measurement, it is converted into s.toCharArray, which is faster than direct chatAt()

349. Intersection of two arrays

Overall thinking
Use a hash table to store the characters in an array and traverse another array. If any exists in the hash table, it will be stored in the set set to remove duplicates
ps: in the following version, the map can be directly set

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashMap<Integer, Integer> map1 = new HashMap<>();
        for (int i : nums1){
            map1.put(i, map1.getOrDefault(i, 0) + 1);
        }
        Set<Integer> set = new HashSet<>();
        for (int i : nums2){
            if (map1.containsKey(i)){
                set.add(i);
            }
        }
        int[] res = new int[set.size()];
        int count = 0;
        for (int i : set){
            res[count] = i;
            count++;
        }
        return res;
    }
}

202. Happy number

Basic idea:
1. Create a function to update n at any time
2. Create a Set without repetition. If n appears in it for the second time, it enters the dead cycle
code:

    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<>();
        while (n != 1 && !set.contains(n)){
            set.add(n);
            n = getNewNumber(n);
        }
        return n == 1;
    }

    public int getNewNumber(int n){
        int res= 0;
        while (n > 0){
            int temp = n % 10;
            res += temp * temp;
            n = n / 10;
        }
        return res;
    }

1. Sum of two numbers

Basic idea:
Method 1: traversal
Method 2: when you do it, you use two for loops. One is to save the value and index into the map, and the other is to traverse the array to find out whether there is a corresponding target - value in the map. But in fact, it is OK to use one cycle, because if the conditions are met in the later stage, the value of target late stage has been saved in advance

    public static int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int res[] = new int[2];
        for (int i = 0; i < nums.length; i++){
            map.put(nums[i],i);
        }
        for (int i = 0; i < nums.length; i++){
            if (map.containsKey(target - nums[i]) && i != map.get(target - nums[i])){
                res[0] = i;
                res[1] = map.get(target - nums[i]);
                break;
            }
        }
        return res;
    }

Method 3: just use one cycle, because if the conditions are met in the later stage, the value of target later stage has been saved in advance

    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int res[] = new int[2];
        for (int i = 0; i < nums.length; i++){
            if (map.containsKey(target - nums[i])){
                res[0] = i;
                res[1] = map.get(target - nums[i]);
                break;
            }
            map.put(nums[i], i);
        }
        return res;
    }

454. Adding four numbers II

Basic idea:

  1. First define an unordered_map, key puts the sum of a and b, and value puts the number of occurrences of the sum of a and b.
  2. Traverse the large A and B arrays, count the sum of the two array elements and the number of occurrences, and put them into the map.
  3. Define the int variable count to count the number of occurrences of a+b+c+d = 0.
  4. After traversing the large C and large D arrays, if 0-(c+d) has appeared in the map, count the value corresponding to the key in the map, that is, the number of occurrences.
  5. Finally, just return the statistical value count
    code:
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        //Record the sum of the first two arrays a+b and the number of occurrences of this sum
        Map<Integer, Integer> map = new HashMap<>();
        int res = 0;
        int n = nums1.length;
        for (int a : nums1){
            for (int b : nums2){
                map.put((a + b), map.getOrDefault((a + b), 0) + 1);
            }
        }
        //In the latter two arrays, c+d calculates the sum to see if it is consistent with the sum of the first two arrays a+b
        for (int c : nums3) {
            for (int d : nums4) {
                if (map.containsKey(0 - (c +d))){
                    res += map.get(0 - (c + d));
                }
            }
        }
        return res;
    }

383. Ransom letter

Basic idea:

  1. If the length of ran is larger than mag, false is returned directly
  2. Because it is lowercase, you can use the array directly
  3. Record the number of occurrences of each letter in the mag and the corresponding array position++
  4. Traverse ran and encounter the corresponding array position –
  5. If the value of one position is less than 0, false is returned
    code:
    public static boolean canConstruct(String ransomNote, String magazine) {
        int len1 = ransomNote.length();
        int len2 = magazine.length();
        if (len1 > len2) return false;
        int[] find = new int[26];
        char[] mag = magazine.toCharArray();
        char[] ran = ransomNote.toCharArray();
        for (char a : mag){
            find[a - 'a']++;
        }
        for (char b : ran){
            find[b - 'a']--;
            if (find[b - 'a'] < 0){
                return false;
            }
        }
        return true;
    }

15. Sum of three

Basic idea:
In fact, the hash method is not very suitable for this topic, because there are many details to pay attention to in the de duplication operation. It is difficult to write bug free code directly in the interview.

Moreover, when using the hash method and using the two-layer for loop, the pruning operation that can be done is very limited. Although the time complexity is O(n^2) and can be passed on leetcode, the execution time of the program is still relatively long.

Use another solution: Double finger acupuncture, which is more efficient than Hashi's method

  1. Take this nums array as an example. First sort the array, and then there is a for loop. i starts from the subscript 0. At the same time, define a subscript left at the position of i+1 and a subscript right at the end of the array.

  2. We still find abc in the array so that a + b +c =0, which is equivalent to a = nums[i] b = nums[left] c = nums[right].

  3. Next, how to move left and right? If num [i] + num [left] + num [right] > 0, it means that the sum of the three numbers is large. Because the array is sorted, the right subscript should be moved to the left, so as to make the sum of the three numbers smaller.

  4. If num [i] + num [left] + num [right] < 0, it means that the sum of the three numbers is small at this time, and the left moves to the right to make the sum of the three numbers larger until left meets right.

code:

    public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++){
            //Because the order has been arranged, when it is greater than 0 at the beginning, the current result can be returned directly
            if (nums[i] > 0) return res;
            //Do not remove the weight. If num [i] = = num [i + 1], it will leave - 1, - 1,2
            if (i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
            while (right > left){
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0){
                    right--;
                }else if (sum < 0){
                    left++;
                }else {
                    //Join first and then remove
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    //De duplication, encounter the original same value movement
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    right--;
                    left++;
                }
            }
        }
        return res;
    }

18. Sum of four numbers

Basic idea:
The sum of four numbers and 15. The sum of three numbers (opens new window) is an idea, which uses the double finger needle method. The basic solution is to set a for loop on the basis of 15. The sum of three numbers (opens new window).

The double pointer solution of the sum of four numbers is a two-layer for loop. Num [k] + num [i] is the determined value. There are still left and right in the loop. The following table is used as the double pointer. Find out the case where num [k] + num [i] + num [left] + num [right] = = target. The time complexity of the sum of three numbers is O(n^2),
The time complexity of the sum of four numbers is O(n^3).

Then the same reason, the sum of five numbers, the sum of six numbers and so on all use this solution.
code:

    public static List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            //The following needs to be cancelled. For example, if - 5 - 2 - 1 target is - 8, it will be cancelled as soon as it comes up,
            //The main reason is that the target may be a negative value. The greater the positive number, the smaller the negative number
            /*
            if (nums[i] > target) {
                System.out.println(nums[i]);
                return res;
            }
             */
            //duplicate removal
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for (int j = i + 1; j < nums.length; j++) {
                //duplicate removal
                if (j > i + 1 && nums[j] == nums[j - 1]){
                    continue;
                }
                int left = j + 1;
                int right = nums.length - 1;
                while (right > left) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        //Join first and then remove
                        res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        //De duplication, encounter the original same value movement
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;
                        right--;
                        left++;
                    }
                }
            }
        }
        return res;
    }

summary

Array as hash table:
If the title contains only lowercase letters, it is most appropriate to use an array for hashing. In this case, the space consumption of using map is larger than that of array, because map needs to maintain red black tree or symbol table, and also needs to perform hash function operation. So arrays are simpler, more direct and more efficient.

set as hash table:
The size of the array is limited by the system stack space (not the stack of the data structure).
If the array space is large enough, but the hash value is small, especially scattered and the span is very large, using the array will cause a great waste of space.

map as hash table:
The size of the array is limited, and if there are few elements and the hash value is too large, it will cause a waste of memory space.
Set is a set, and the element in it can only be a key. For example, for the question of the sum of two numbers, we should not only judge whether y exists, but also record the subscript position of Y, because we need to return the subscripts of x and y. So set doesn't work.
Map is a < key, value > structure. You can use key to save values, and use value in the following table where the values are saved. Therefore, it is most appropriate to use map.

Tags: Algorithm leetcode

Posted on Sat, 04 Dec 2021 19:12:16 -0500 by Alffallen