雜湊對映用法及演算法例題

卻把清梅嗅發表於2020-05-07

本文為博主演算法學習過程中的學習筆記,主要內容來源於其他平臺或書籍,出處請參考下方 參考&感謝 一節。

用法

雜湊對映 是用於儲存 (key, value) 鍵值對的一種實現。

使用雜湊對映的第一個場景是,我們 需要更多的資訊,而不僅僅是鍵。然後通過雜湊對映 建立金鑰與資訊之間的對映關係

另一個常見的場景是 按鍵聚合所有資訊。我們也可以使用雜湊對映來實現這一目標。

例題

1、兩數之和

題目描述

給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們的陣列下標。

你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個陣列中同樣的元素。

示例:

給定 nums = [2, 7, 11, 15], target = 9

因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解題思路及實現

HashMap入門的經典題。

簡單的方式是使用雙層迴圈進行暴力破解,這種演算法時間複雜度為O(N^2)

而使用HashMap則可以將問題在一層迴圈中進行解決,時間複雜度僅O(N)

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

2、同構字串

題目描述

給定兩個字串 s 和 t,判斷它們是否是同構的。

如果 s 中的字元可以被替換得到 t ,那麼這兩個字串是同構的。

所有出現的字元都必須用另一個字元替換,同時保留字元的順序。兩個字元不能對映到同一個字元上,但字元可以對映自己本身。

你可以假設 s 和 t 具有相同的長度

示例:

輸入: s = “egg”, t = “add”
輸出: true
輸入: s = “foo”, t = “bar”
輸出: false

解題思路及實現

通過2個HashMap儲存各自字元對應最初的索引,然後進行比較,當字元對應的最初索引不同時,說明2個字串不是同構的:

class Solution {
    public boolean isIsomorphic(String s, String t) {
        char[] char1 = s.toCharArray();
        char[] char2 = t.toCharArray();
        HashMap<Character, Integer> map1 = new HashMap<>();
        HashMap<Character, Integer> map2 = new HashMap<>();
        for (int i = 0; i < char1.length; i++) {
            char c1 = char1[i];
            char c2 = char2[i];
            if (!map1.containsKey(c1))
                map1.put(c1, i);
            if (!map2.containsKey(c2))
                map2.put(c2, i);

            if (map1.get(c1) != map2.get(c2))
                return false;
        }
        return true;
    }
}

3、兩個列表的最小索引總和

題目描述

https://leetcode-cn.com/problems/minimum-index-sum-of-two-lists/

解題思路及實現

這道題的難點是發現對映關係,即 最愛餐廳 -> 索引值 的對映關係。

通過遍歷將該對映關係儲存到HashMap中後,再次通過一次遍歷找出索引值和最小的餐廳,因為索引值最小的情況可能不止一個,因此用一個List進行維護,最終將答案遍歷輸出:

class Solution {
    public String[] findRestaurant(String[] list1, String[] list2) {
        HashMap<String, Integer> map = new HashMap<>();
        List<Integer> posList = new ArrayList<>();
        int minPosNum = Integer.MAX_VALUE;

        for (int i = 0; i < list1.length; i++) {
            map.put(list1[i], i);
        }
        for (int i = 0; i < list2.length; i++) {
            String favorite = list2[i];
            Integer position = map.get(favorite);
            if (position != null) {
                int posNum = i + position;
                if (minPosNum > posNum) {
                    minPosNum = posNum;
                    posList.clear();
                    posList.add(i);
                } else if (minPosNum == posNum) {
                    posList.add(i);
                }
            }
        }
        String[] result = new String[posList.size()];
        for (int i = 0; i < posList.size(); i++)
            result[i] = list2[posList.get(i)];
        return result;
    }
}

4、字串中的第一個唯一字元

題目描述

給定一個字串,找到它的第一個不重複的字元,並返回它的索引。如果不存在,則返回 -1。

示例:

s = “leetcode”
返回 0.
s = “loveleetcode”
返回 2.

您可以假定該字串只包含小寫字母。

解題思路及實現

比較簡單,通過2次遍歷,第一次遍歷將每個字元對應出現的次數進行儲存,第二次遍歷每個字元的出現次數,當出現次數為1,返回該字元的索引即可。

class Solution {
    public int firstUniqChar(String s) {
        char[] arr = s.toCharArray();
        HashMap<Character, Integer> map = new HashMap<>();
        // 遍歷將所有字元的頻率輸出到Map中
        for (int i = 0; i < arr.length; i++) {
            char c = arr[i];
            if (map.containsKey(c)) {
                map.put(c, map.get(c) + 1);
            } else {
                map.put(c, 1);
            }
        }
        for (int i = 0; i < arr.length; i++) {
            char c = arr[i];
            if (map.get(c) == 1)
                return i;
        }
        return -1;
    }
}

5、兩個陣列的交集 II

題目描述

給定兩個陣列,編寫一個函式來計算它們的交集。

輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2,2]
輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [4,9]

輸出結果中每個元素出現的次數,應與元素在兩個陣列中出現的次數一致。
我們可以不考慮輸出結果的順序。

解題思路及實現

仍然是通過2次遍歷,第一次遍歷將nums1[]每個數字對應出現的次數進行儲存,第二次遍歷nums2[],然後和之前的結果進行匹配,最終返回結果。

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        HashMap<Integer, Integer> map = new HashMap<>();

        // 遍歷陣列將所有元素和其數量存入map中
        for (int i = 0; i < nums1.length; i++) {
            int key = nums1[i];
            if (map.containsKey(key)) {
                map.put(key, map.get(key) + 1);
            } else {
                map.put(key, 1);
            }
        }

        int[] result = new int[nums2.length];
        int pos = 0;
        for (int i = 0; i < nums2.length; i++) {
            int key = nums2[i];
            if (map.containsKey(key) && map.get(key) >= 1) {
                result[pos] = key;
                pos++;
                map.put(key, map.get(key) - 1);
            }
        }

        return Arrays.copyOf(result, pos);
    }
}

6、存在重複元素 II

題目描述

給定一個整數陣列和一個整數 k,判斷陣列中是否存在兩個不同的索引 i 和 j,使得 nums [i] = nums [j],並且 i 和 j 的差的絕對值最大為 k。

輸入: nums = [1,2,3,1], k = 3
輸出: true
輸入: nums = [1,0,1,1], k = 1
輸出: true
輸入: nums = [1,2,3,1,2,3], k = 2
輸出: false

解題思路及實現

題目理解了就好做了,關鍵是陣列內相同的數字最小距離是否不大於k,做一次遍歷,每次都將數字作為key,其索引作為value進行儲存。

遍歷結束前,只要有一個滿足要求就可以返回true,否則最終返回false即可。

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

參考 & 感謝

文章絕大部分內容節選自LeetCode,概述:

  • https://leetcode-cn.com/explore/learn/card/hash-table/205/practical-application-hash-map/809/

例題:

  • https://leetcode-cn.com/problems/two-sum/
  • https://leetcode-cn.com/problems/isomorphic-strings/
  • https://leetcode-cn.com/problems/minimum-index-sum-of-two-lists/
  • https://leetcode-cn.com/problems/first-unique-character-in-a-string/
  • https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/
  • https://leetcode-cn.com/problems/contains-duplicate-ii/

關於我

Hello,我是 卻把清梅嗅 ,如果您覺得文章對您有價值,歡迎 ❤️,也歡迎關注我的 部落格 或者 GitHub

如果您覺得文章還差了那麼點東西,也請通過關注督促我寫出更好的文章——萬一哪天我進步了呢?

相關文章