算法----哈希表

70 阅读3分钟

哈希表的应用场景:判断某个元素,在集合中是否出现过

有效字母异位词(leetcode.242)

class Solution {
public:
    bool isAnagram(string s, string t) {
        int ans[26] = {0};  // 创建一个大小为 26 的整数数组 ans,用于记录每个字母出现的次数
        for (int i = 0; i < s.length(); i++)  
            ans[s[i] - 'a']++;  
        for (int i = 0; i < t.length(); i++)  
            ans[t[i] - 'a']--;  
        for (int i = 0; i < 26; i++) 
            if (ans[i] != 0) 
                return false;
        return true; 
    }
};

注意点:

  • 用数组实现hash表

两个数组的交集(leetcode.349)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1(nums1.begin(), nums1.end());  // 将nums1中的元素插入set1
        unordered_set<int> resultSet;  // 用于存储交集结果

        // 遍历nums2,检查其元素是否在set1中
        for (const auto& num : nums2) {
            if (set1.count(num)) {  // 如果存在
                resultSet.insert(num);  // 插入到结果集中
            }
        }

        // 将结果集转换为vector并返回
        return vector<int>(resultSet.begin(), resultSet.end());
    }
};

注意点:

  • 实现hash的三种基本数据结构:数组、map、set,这里使用set,因为题目只要求返回交集,且这个交集只需要记录一个元素即可,所以是无重复的
  • 使用unorded_set性能更高

两数之和(leetcode.1)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

注意点:

  • 为什么要用哈希表,因为两数之和要判断当前位置所需要的另一个加数是否在之前遍历过的元素集合之中
  • 遍历的元素值作为键,下标作为值存入哈希表中,因为哈希表就是找键的性能高
  • 使用unordered_map作为哈希表

四数相加(leetcode.454)

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        unordered_map<int, int> countAB;
        for (int a: A) {
            for (int b: B) {
                // 将元素和作为键存入map,对应的值用作计数
                ++countAB[a + b];
            }
        }
        int ans = 0;
        for (int c: C) {
            for (int d: D) {
                if (countAB.count(0 - (c + d))) {
                    ans += countAB[0 - (c + d)];
                }
            }
        }
        return ans;
    }
};

没什么好说的,四个数组分成两组,每组的时间复杂度为n方,实际上就是两数之和的拓展

三数之和(leetcode.15)

代码随想录 (programmercarl.com):双指针

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());
        for(int i = 0; i < nums.size(); i++){
            if(nums[i] > 0){
                return result;
            }
            // a去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while(left < right){
                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]});
                    //b去重
                    while(left < right && nums[right] == nums[right-1]){
                        right--;
                    }
                    //c去重
                    while(left < right && nums[left] == nums[left + 1]){
                        left++;
                    }

                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

注意点:

  • a、b、c的去重逻辑一定是上面这样

四数之和(leetcode.18)

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for (int k = 0; k < nums.size(); k++) {
            // 剪枝处理
            if (nums[k] > target && nums[k] >= 0) {
            	break; // 这里使用break,统一通过最后的return返回
            }
            // 对nums[k]去重
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                // 2级剪枝处理
                if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
                    break;
                }

                // 对nums[i]去重
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
                        right--;
                    // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
                    } 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]});
                        // 对nums[left]和nums[right]去重
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }

            }
        }
        return result;
    }
};