哈希表算法

目录

一、哈希表是什么?

二、哈希表有什么用?

三、什么时候用哈希表?

四、哈希表适合解哪些题?

五、哈希表的优缺点

六、相关例题

1.俩数之和

1.题目描述

2.示例

3.解题思路

4.代码实现

2.判定是否互为字符重排

1.题目描述

2.示例

3.解题思路

4.代码实现

 3.存在重复元素

1.题目描述

2.示例

3.解题思路

4.代码实现

4. 存在重复元素II

 1.题目描述

2.示例

3.解题思路

4.代码实现


一、哈希表是什么?

        哈希表(Hash Table)是一种基于**键值对(Key-Value Pair)存储数据的数据结构。它通过哈希函数(Hash Function)**将键(Key)映射到一个固定的位置(索引),从而快速定位和访问对应的值(Value)。

核心特点:

  1. 快速查找:通过哈希函数,可以在平均 O(1) 的时间复杂度内查找、插入或删除数据。

  2. 键值对存储:每个键对应一个唯一的值。

  3. 冲突处理:当不同的键通过哈希函数映射到同一个位置时,需要处理冲突。常见的冲突解决方法有链地址法开放地址法


二、哈希表有什么用?

哈希表的主要作用是高效地存储和查找数据。它的常见用途包括:

  1. 快速查找:判断某个元素是否存在。

  2. 去重:存储唯一的值。

  3. 计数:统计元素出现的次数。

  4. 映射关系:建立键和值之间的映射关系。


三、什么时候用哈希表?

哈希表适合在以下场景中使用:

  1. 需要快速查找、插入或删除数据:比如判断一个元素是否存在于集合中。

  2. 需要统计频率或计数:比如统计数组中每个元素出现的次数。

  3. 需要建立映射关系:比如将字符映射到索引,或者将单词映射到定义。

  4. 需要去重:比如从一个列表中提取唯一元素。

  5. 快速、频繁


四、哈希表适合解哪些题?

哈希表在算法题中非常常见,尤其适合解决以下类型的问题:

  1. 两数之和(Two Sum)

    • 题目:给定一个数组和一个目标值,找出数组中两个数之和等于目标值的索引。

    • 解法:用哈希表存储每个数的补数(target - nums[i]),快速查找是否存在。

  2. 统计频率

    • 题目:统计数组中每个元素出现的次数。

    • 解法:用哈希表存储元素和对应的频率。

  3. 字符串中的字符统计

    • 题目:判断两个字符串是否是字母异位词(Anagram)。

    • 解法:用哈希表统计每个字符的出现次数,然后比较两个哈希表是否相同。

  4. 查找重复元素

    • 题目:判断数组中是否存在重复元素。

    • 解法:用哈希表存储元素,如果发现元素已经存在,则说明有重复。

  5. 滑动窗口问题

    • 题目:找到字符串中无重复字符的最长子串。

    • 解法:用哈希表记录字符的最后出现位置,配合滑动窗口快速更新。

  6. 缓存设计(LRU Cache)

    • 题目:设计一个 LRU 缓存。

    • 解法:用哈希表 + 双向链表实现快速查找和更新。

五、哈希表的优缺点

优点:

  1. 查找、插入、删除速度快:平均时间复杂度为 O(1)。

  2. 灵活性高:可以存储任意类型的键值对。

缺点:

  1. 空间开销大:哈希表需要额外的空间来存储哈希函数和冲突处理的数据。

  2. 哈希冲突:当哈希函数设计不好时,冲突会增加,导致性能下降。

  3. 无序性:哈希表中的元素是无序的(除非使用 std::map,但它基于红黑树,不是哈希表)

六、相关例题

1.俩数之和

1. 两数之和 - 力扣(LeetCode)

1.题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

2.示例

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

3.解题思路

  • 创建一个hash表来存储遍历过的数

  • 计算 target 与当前数字 nums[i] 的差值 x。如果 x 在哈希表中存在,说明之前已经遍历过这个数字,且 nums[i] + x = target,即找到了满足条件的两个数。

4.代码实现

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        unordered_map  hash;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            int x = target - nums[i];
            if(hash.count(x))   return {hash[x] , i};
            hash[nums[i]] = i;
        }  
        return {-1, -1};     
    }
};

2.判定是否互为字符重排

面试题 01.02. 判定是否互为字符重排 - 力扣(LeetCode)

1.题目描述

给定两个由小写字母组成的字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串

2.示例

示例 1:

输入: s1= "abc", s2 = "bca"输出: true 

示例 2:

输入: s1 = "abc", s2 = "bad"输出: false

3.解题思路

1.使用哈希数组,hash[26]来记录字符串1中字符出现的个数

2.再对字符串2进行遍历,让找到的字符数量减减

3.如果hash[ch - 'a']小于0的时候就是找不到

4.代码实现

class Solution {
public:
    bool CheckPermutation(string s1, string s2) {
        //当俩个字符串的长度不相等的时候,肯定不能实现重排
        int n1 = s1.size(), n2 = s2.size();
        if(n1 != n2)    return false;
        int hash[26] = { 0 };
        for(auto ch : s1)
            hash[ch - 'a']++;
        for(auto ch : s2)
        {
            hash[ch - 'a']--;
            if(hash[ch - 'a'] < 0)  return false;
        }
        return true;
    }
};

 3.存在重复元素

217. 存在重复元素 - 力扣(LeetCode)

1.题目描述

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

2.示例

示例 1:

输入:nums = [1,2,3,1]

输出:true

解释:

元素 1 在下标 0 和 3 出现。

示例 2:

输入:nums = [1,2,3,4]

输出:false

解释:

所有元素都不同。

示例 3:

输入:nums = [1,1,1,3,3,4,3,2,4,2]

输出:true

3.解题思路

法一:使用哈希来解决问题,先将数字存入哈希表中,然后判断出现的次数,如果出现的次数大于1.返回true

法二:分析⼀下题⽬,出现「⾄少两次」的意思就是数组中存在着重复的元素,因此我们可以⽆需统计元素出现的数⽬。仅需在遍历数组的过程中,检查当前元素「是否在之前已经出现过」即可。

        因此我们可以利⽤哈希表,仅需存储数「组内的元素」。在遍历数组的时候,⼀边检查哈希表中是否 已经出现过当前元素,⼀边将元素加⼊到哈希表中。

4.代码实现

class Solution {
public:
    bool containsDuplicate(vector& nums) {
        unordered_map hash;
        for(auto num : nums)
            hash[num]++;
        for(auto& pair : hash)
        {
            if(pair.second > 1)   return true;
        }
        return false;
        
    }
};
class Solution {
public:
    bool containsDuplicate(vector& nums) {
        unordered_set  hash;
        for(auto x : nums)
        {
            if(hash.count(x))  return true;
            else    hash.insert(x);
        }
        return false;
    }
};

4. 存在重复元素II

 219. 存在重复元素 II - 力扣(LeetCode)

 1.题目描述

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。

2.示例

示例 1:

输入:nums = [1,2,3,1], k = 3
输出:true

示例 2:

输入:nums = [1,0,1,1], k = 1
输出:true

示例 3:

输入:nums = [1,2,3,1,2,3], k = 2
输出:false

3.解题思路

1.解决该问题需要我们快速定位到两个信息:

• 两个相同的元素;

• 这两个相同元素的下标

2.因此,我们可以使⽤「哈希表」,令数组内的元素做key 值,该元素所对应的下标做val 值,将 「数组元素」和「下标」绑定在⼀起,存⼊到「哈希表」中

3.我们按照下标「从⼩到⼤」的顺序遍历数组,当遇到两个元素相同,并且⽐较它们的下标时,这两个 下标⼀定是距离最近的,因为:

• 如果当前判断符合条件直接返回true ,⽆需继续往后查找。

 • 如果不符合条件,那么前⼀个下标⼀定不可能与后续相同元素的下标匹配(因为下标在逐渐变 ⼤),那么我们可以⼤胆舍去前⼀个存储的下标,转⽽将其换成新的下标,继续匹配。

4.代码实现

class Solution {
public:
    bool containsNearbyDuplicate(vector& nums, int k) {
        unordered_map  hash;
        
        for(int i = 0; i < nums.size(); i++)
        {
            if(hash.count(nums[i]))
            {
                if(i - hash[nums[i]] <= k)  return true;
            }
            hash[nums[i]] = i;
        }
        return false;
    }
};

5.字母异位词分组

49. 字母异位词分组 - 力扣(LeetCode)

1.题目描述

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

2.示例

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]

输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]

输出: [[""]]

示例 3:

输入: strs = ["a"]

输出: [["a"]]

3.解题思路

根据这道题,深刻得认识容器还可以存储一个串

哈希表算法_第1张图片

4.代码实现

class Solution {
public:
    vector> groupAnagrams(vector& strs) {
        unordered_map> hash;
        //把所有字母分组
        
        for(auto& s : strs)
        {
            string tmp = s;
            sort(tmp.begin() , tmp.end());
            hash[tmp].push_back(s);
        }
        vector> ret;
        for(auto& [i , j] : hash)
        {
            ret.push_back(j);
        }
        return ret;
    }
};

你可能感兴趣的:(算法,哈希算法)