哈希表经典题目深度解析 - 从理论到实践

哈希表经典题目深度解析 - 从理论到实践

哈希表理论基础回顾

哈希表的本质

哈希表是一种在时间和空间上做权衡的数据结构,通过哈希函数将键映射到数组索引,实现O(1)平均时间复杂度的查找、插入、删除操作。

哈希表的三种经典结构

  1. 数组 - 简单直接的哈希表
  2. set - 集合型哈希表
  3. map - 键值对哈希表

选择指南

  • 数组:数据范围有限且连续(如小写字母、固定范围整数)
  • set:需要去重,不需要记录额外信息
  • map:需要记录键值对关系(如元素及其下标、出现次数等)

大厂面试高频题型分类

1. 字符统计类题目

  • 字母异位词、赎金信、字符频率统计
  • 考察点:数组哈希表的应用,字符编码转换

2. 数组交集/并集类题目

  • 两个数组的交集、并集、差集
  • 考察点:set的使用,去重逻辑

3. 查找配对类题目

  • 两数之和、三数之和、四数之和
  • 考察点:map的应用,分治思想

4. 循环检测类题目

  • 快乐数、链表环检测
  • 考察点:哈希表检测重复,快慢指针优化

5. 计数统计类题目

  • 元素出现次数、频率统计
  • 考察点:map计数,排序优化

6. 设计类题目

  • 设计哈希集合、LRU缓存
  • 考察点:哈希表底层实现,数据结构设计

题目一:242. 有效的字母异位词

题目描述

给定两个字符串 s 和 t,判断 t 是否是 s 的字母异位词。

核心思想

数组就是最简单的哈希表,当数据范围有限且连续时,数组是最优选择。

解法分析

解法一:数组哈希表(最优解)
class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        
        // 统计s中字符出现次数
        for (char c : s) {
            record[c - 'a']++;
        }
        
        // 减去t中字符出现次数
        for (char c : t) {
            record[c - 'a']--;
        }
        
        // 检查是否所有字符都平衡
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                return false;
            }
        }
        return true;
    }
};
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        record = [0] * 26
        
        # 统计s中字符出现次数
        for c in s:
            record[ord(c) - ord('a')] += 1
        
        # 减去t中字符出现次数
        for c in t:
            record[ord(c) - ord('a')] -= 1
        
        # 检查是否所有字符都平衡
        return all(count == 0 for count in record)

优势分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1) - 固定大小数组
  • 实现简单,性能最优
  • 适合小写字母这种固定范围数据
解法二:排序比较
class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.length() != t.length()) return false;
        
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());
        
        return s == t;
    }
};
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        return sorted(s) == sorted(t)

优势分析

  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)(原地排序)
  • 代码简洁,易于理解
  • 适合面试快速写出
解法三:哈希表(通用解法)
class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.length() != t.length()) return false;
        
        unordered_map<char, int> count;
        
        for (char c : s) {
            count[c]++;
        }
        
        for (char c : t) {
            count[c]--;
            if (count[c] < 0) return false;
        }
        
        return true;
    }
};
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False
        
        count = {}
        
        for c in s:
            count[c] = count.get(c, 0) + 1
        
        for c in t:
            count[c] = count.get(c, 0) - 1
            if count[c] < 0:
                return False
        
        return True

优势分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(k) - k为字符集大小
  • 通用性强,适用于任意字符集
  • 适合面试展示对哈希表的理解

总结

  • 最优选择:数组哈希表(数据范围固定)
  • 面试选择:排序比较(快速实现)
  • 通用选择:哈希表(任意字符集)

题目二:349. 两个数组的交集

题目描述

给定两个数组,计算它们的交集。

核心思想

当数据范围不固定时,需要使用set或map作为哈希表。

解法分析

解法一:set哈希表(推荐)
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        unordered_set<int> result_set;
        
        for (int num : nums2) {
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        
        return vector<int>(result_set.begin(), result_set.end());
    }
};
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 使用集合的交集操作
        return list(set(nums1) & set(nums2))

优势分析

  • 时间复杂度:O(n + m)
  • 空间复杂度:O(n)
  • 自动去重
  • 代码简洁
解法二:数组哈希表(数据范围固定时)
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        int hash[1001] = {0};  // 假设数据范围在1000以内
        
        for (int num : nums1) {
            hash[num] = 1;
        }
        
        for (int num : nums2) {
            if (hash[num] == 1) {
                result_set.insert(num);
            }
        }
        
        return vector<int>(result_set.begin(), result_set.end());
    }
};
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        # 假设数据范围在1000以内
        hash_set = set()
        hash_array = [0] * 1001
        
        for num in nums1:
            hash_array[num] = 1
        
        for num in nums2:
            if hash_array[num] == 1:
                hash_set.add(num)
        
        return list(hash_set)

优势分析

  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1) - 固定大小数组
  • 性能最优(数据范围固定时)

总结

  • 通用选择:set哈希表
  • 优化选择:数组哈希表(数据范围固定)

题目三:202. 快乐数

题目描述

判断一个数是否为快乐数(各位数字平方和最终等于1)。

核心思想

使用哈希表检测循环,避免无限循环。

解法分析

解法一:set检测循环(推荐)
class Solution {
public:
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    
    bool isHappy(int n) {
        unordered_set<int> seen;
        
        while (n != 1) {
            n = getSum(n);
            if (seen.find(n) != seen.end()) {
                return false;  // 检测到循环
            }
            seen.insert(n);
        }
        return true;
    }
};
class Solution:
    def isHappy(self, n: int) -> bool:
        seen = set()
        
        while n != 1:
            n = sum(int(digit) ** 2 for digit in str(n))
            if n in seen:
                return False  # 检测到循环
            seen.add(n)
        
        return True

优势分析

  • 时间复杂度:O(log n)
  • 空间复杂度:O(log n)
  • 思路清晰,易于理解
解法二:快慢指针(空间优化)
class Solution {
public:
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    
    bool isHappy(int n) {
        int slow = n, fast = n;
        
        do {
            slow = getSum(slow);
            fast = getSum(getSum(fast));
        } while (slow != fast);
        
        return slow == 1;
    }
};
class Solution:
    def isHappy(self, n: int) -> bool:
        def get_sum(num):
            return sum(int(digit) ** 2 for digit in str(num))
        
        slow = fast = n
        
        while True:
            slow = get_sum(slow)
            fast = get_sum(get_sum(fast))
            
            if slow == fast:
                break
        
        return slow == 1

优势分析

  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)
  • 空间最优解

总结

  • 推荐选择:set检测循环
  • 优化选择:快慢指针(空间敏感)

题目四:1. 两数之和

题目描述

在数组中找出两个数,使它们的和等于目标值。

核心思想

使用map记录已遍历的元素及其下标,实现一次遍历解决。

解法分析

解法一:map哈希表(最优解)
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;
        
        for (int i = 0; i < nums.size(); i++) {
            int complement = target - nums[i];
            
            if (map.find(complement) != map.end()) {
                return {map[complement], i};
            }
            
            map[nums[i]] = i;
        }
        
        return {};
    }
};
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        seen = {}
        
        for i, num in enumerate(nums):
            complement = target - num
            if complement in seen:
                return [seen[complement], i]
            seen[num] = i
        
        return []

优势分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
  • 一次遍历解决
  • 经典哈希表应用
解法二:暴力解法
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); i++) {
            for (int j = i + 1; j < nums.size(); j++) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {};
    }
};
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                if nums[i] + nums[j] == target:
                    return [i, j]
        return []

优势分析

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 实现简单
  • 适合小数据量

总结

  • 最优选择:map哈希表
  • 简单选择:暴力解法(小数据量)

题目五:454. 四数相加II

题目描述

给定四个数组,计算有多少个元组使A[i] + B[j] + C[k] + D[l] = 0。

核心思想

将四数之和转化为两数之和,使用map记录A+B的和及其出现次数。

解法分析

解法一:map哈希表(最优解)
class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        unordered_map<int, int> umap;
        
        // 统计A+B的和及其出现次数
        for (int a : A) {
            for (int b : B) {
                umap[a + b]++;
            }
        }
        
        int count = 0;
        // 查找-(C+D)是否在map中
        for (int c : C) {
            for (int d : D) {
                if (umap.find(-(c + d)) != umap.end()) {
                    count += umap[-(c + d)];
                }
            }
        }
        
        return count;
    }
};
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], 
                     nums3: List[int], nums4: List[int]) -> int:
        from collections import defaultdict
        
        count_map = defaultdict(int)
        
        # 统计nums1+nums2的和及其出现次数
        for n1 in nums1:
            for n2 in nums2:
                count_map[n1 + n2] += 1
        
        count = 0
        # 查找-(nums3+nums4)是否在map中
        for n3 in nums3:
   

你可能感兴趣的:(哈希表经典题目深度解析 - 从理论到实践)