当前位置: 首页 > news >正文

[Lc10_hash] 总结 | 两数之和 | 字符重排 | 存在重复元素 i ii | 字母异位词分组

目录

1.介绍

2.两数之和

题解

3.面试题 01.02. 判定是否互为字符重排

题解

4.存在重复元素

题解

5.存在重复元素 II

题解

⭕6.字母异位词分组

题解


1.介绍

哈希表是什么?

  • 存储数据的容器
  • 前文:[C++_] set | map | unordered_map

有什么用呢?

  • “快速” 查找某个元素。 时间复杂度O(1)

什么时候用哈希表?

频繁的查找某一个数的时候。

  • 频繁查找某一个数的时候,我们还要想到一个二分查找也可以快速查找某一个元素
  • 但是二分因为有些局限,具有二段性才可以用二分,它也比较快,时间复杂度O(logn)。
  • 而且能用二分我们尽量用二分,因为哈希表虽然非常快,但是它空间复杂度是O(n)。

怎么用哈希表?

  • 容器(哈希表)
  • 数组模拟简易哈希表

什么时候会想到用数组模拟简易哈希表,比如说:

  • 1. 关注字符串中的 “字符”

其中key就是index,value就是nums[index]

字符的ascll值当作index(key),nums[index]就是我们需要的值。非常快。

  • 2. 数据范围很小的时候

int 1~10^7, 最好不要有负数, - 10^3 ~ 10^3


2.两数之和

题目链接:1. 两数之和

题目分析:

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

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

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

示例 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]

题解

有且仅有一组答案,可以按任意顺序返回。

数组中同一个元素在答案里不能重复出现。比如示例2 不能选两个3。

解法一:暴力求解

  • 两层for循环把所有情况都找到,挑选符合条件的。
  • 以前我们是固定一个数,往后找。
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 {};
        
    }
};

但是我们还有一种暴力方法,固定一个数,往前找

  • 先固定其中一个数
  • 依次与该数之前的数相加

同样也能把所有情况都找完,并且少了判断边界情况。

解法二:利用哈希表做优化

  • 我们先看为什么暴力枚举过程这么慢。慢就慢在当固定一个数的时候
  • 比如说11,我们想在它的前面找到一个 target - nums[i] 的数 如 9 - 11 = - 2
  • 我们暴力策略就是从11之前依次遍历找这个-2。
  • 那此时我如果当固定一个数的时候就把它之前的是插入到hash表中,然后在hash表中就可以用O(1)的时间复杂度来找这个-2。

之前暴力解法O(N^2),利用哈希表做优化之后时间复杂度降到O(N)。

不过我们空间复杂度是O(N)的。

  • 具体如何操作可以和刚才的固定一个数往前找完美结合起来。
  • 当固定一个数的时候,就去hash表中找 target - nums[i] 的数在不在hash表。
  • 有就找到了返回对应两个数的下标即可,所有hash<nums[i] , i> 前面存值后面存对应的下标
  • 如果没找到就把这个数 加入到hash表中。

接下来扩展一下,为什么之前固定数,往后走不太好用呢?

  • 首先要将所有的数,先加入到hash表中,然后才能固定一个数,在去hash找 target - nums[i] 的数在不在hash表。
  • 注意我们这道题有个要求:数组中同一个数不能出现两次,如果target = 6,但是数组中有一个3, 你固定3然后在去hash表中找,找到的还是这个3,这是不对的。
  • 因为必须要加个if判断一下,当两个数相等时是不对的。
  • 定一个数往前找,是不会出现这种情况的。我们是先去找。没找到在把这个数加入到hash表中。

代码:

  • hash.count()
  • hash[num[i]]=i; //建立下标映射

3.面试题 01.02. 判定是否互为字符重排

题目链接:面试题 01.02. 判定是否互为字符重排

题目描述:

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

示例 1:

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

示例 2:

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

题解

  • 可以把 “abc” 全排列都找到,每找到一个就比较一下。
  • 怎么全排列,在递归专题哪里有。但是这样时间复杂度太高了。

解法一:利用哈希表

  • 可以用 两个hash表记录两个字符串中每个字符出现的次数
  • 最后在比较一下两个hash表是否相等就行了。

我们可以用数组模拟hash表。

class Solution {
public:
    bool CheckPermutation(string s1, string s2) 
    {
        //两个hash
        unordered_map<char,int> hash1,hash2;
        if(s1.size()!=s2.size()) return false;

        for(char& c:s1)
            hash1[c]++;
        for(char& c:s2)
            hash2[c]++;
        
//比较两hash表
       for (const auto& [key, val] : hash1) {
        auto it = hash2.find(key);
        if (it == hash2.end() || it->second != val) 
            return false;
    }
    return true;
    }
};
  • find return pair
  • count return value

优化一下:只使用一个hash表。

  • 将s1字符串中字符出现次数放到hash表
  • 然后取出s2字符串中每个字符,对hash表字符对应位置进行--操作。
  • 如果出现<0,说明就不是字符重排。
  • 还有当s1和s2字符串长度不相等也不是字符重排。
class Solution {
public:
    bool CheckPermutation(string s1, string s2) 
    {
        unordered_map<char,int> hash;
        if(s1.size()!=s2.size()) return false;
        for(char& c:s1)
             hash[c]++;
        for(char& c:s2)
        {
            hash[c]--;
            if(hash[c]<0)
                return false;
        }
        return true;
        
    }
};

4.存在重复元素

题目链接:217. 存在重复元素

题目描述:

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

示例 1:

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

输出:true

解释:

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

示例 2:

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

输出:false


题解

解法:哈希表

这道题和最开始那道题的解题思路是一样的。

  • 前面那道题固定一个数往前找找有没有出现 target - nums[i] 的数,遍历找太麻烦了,因此来一个hash表快速查找前面有没有出现target - nums[i] 的数。
  • 这道题 固定一个数 往前找 找有没有出现和我一样的数

也是利用哈希表快速查找。

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) 
    {
        //!!!往前找
        unordered_map<int,int> hash;
        for(int i=0;i<nums.size();i++)
        {
            if(hash.count(nums[i]))
                return true;
            hash[nums[i]]=i;
        }
        return false;
        
    }
};

5.存在重复元素 II

题目链接:219. 存在重复元素 II

题目分析:

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

示例 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

题解

  • 上一道题仅是找到重复元素即可,但是这道题不仅要是重复元素
  • 而且重复元素的下标必须满足 abs(i - j) <= k

解法:哈希

  • 还是固定一个数,往前找,看看有没有出现重复元素,可以用hash快速查找
  • 不过这道题有点不一样的就是,我们还需要知道元素对应的下标
  • 因此可以使用unordered_map<int,int>,记录nums[i] 对应的下标。
  • 这里有一个细节问题,两个相同元素下标之差要小于等于k,unordered_map不允许插入相同的元素,value会覆盖。

但是这 正好满足我们abs(i - j) <= k,因为我们就是想要找距离小的。

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        // 相同  距离<k
        // 可覆盖
        unordered_map<int, int> hash;
        for (int i = 0; i < nums.size(); i++) {
            if (hash.count(nums[i])) {
                int l = i - hash[nums[i]];
                if (l <= k)
                    return true;
            }
            hash[nums[i]] = i;
        }
        return false;
    }
};

⭕6.字母异位词分组

题目链接: 49. 字母异位词分组

题目分析:

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

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

示例 1:

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

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

题解

  • 字母异位词:原有字符串的字符重新排序得到的新的字符串。
  • 这道题要求将属于同一个字母异位词放在一块。

解法:哈希表

这道题我们要解决的有两个问题:

  • 如何判断两个字符串是字母异位词
  • 如何分组

如何判断两个字符串是字母异位词,我们可以用之前的思路

  • 弄一个hash表统计每个字符出现的次数。不过代码写着有点麻烦。这里我们可以将字符串按照ascll码 排序!
  • 如果是 相同的字母异位词排完序 后是一样的。

如何分组?

分组是将相同的字母异位词添加到属于自己的数组中。因此这里我们可以来一个unordered_map<string,vector>

  • string 排序后的字符串
  • vector<string> 是属于相同字母异位词的数组。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) 
    {
        //如何实现hash排序
        unordered_map<string,vector<string>> hash;
        vector<vector<string>> ret;

        for(auto& s:strs)
        {
            string str=s;
            sort(str.begin(),str.end());
    //!!排序后 插入
            hash[str].push_back(s);
        }

        for(auto& [x,y]:hash)
        {
            ret.push_back(y);
        }
        return ret;
    }
};

相关文章:

  • 鸿蒙app开发中实现 底部抽屉效果动效
  • 修改secure-file-priv参数-mysql5.7.26限制不允许导入或导出的解决方法
  • 阿里云操作系统控制台实战评测:提升云资源管理与监控效率
  • MobileBERT: 一种适用于资源有限设备的紧凑型任务无关BERT
  • Ubuntu系统部署.NET 8网站项目
  • 部署vue+django项目(初版)
  • C语言:6.20字符型数据练习题
  • 基于Python 3.7、使用PyTorch构建的回归定位框架的详细实现
  • 线程相关作业
  • CI/CD—Jenkins配置Maven+GitLab自动构建jar包
  • 华为DSVPN
  • Manus AI:国产AI Agent的破局与隐忧
  • MyBatis-Plus 分页查询接口返回值问题剖析
  • got表hook和inlinehook的优缺?(面试题)
  • 深度学习历程
  • 【从零开始学习计算机科学】计算机体系结构(二)指令级并行(ILP)
  • 面试之《技巧》
  • 云服务运维智能时代:阿里云操作系统控制台
  • ctf-WEB: 关于 GHCTF Message in a Bottle plus 与 Message in a Bottle 的非官方wp解法
  • 2025涡轮展技术论坛看点:整机研发-核心部件-材料工艺-运维服务
  • 上海充电桩调研:须全盘考量、分步实现车网互动规模化
  • 上海发布大风黄警:预计未来24小时内将出现8-10级大风
  • AI智能体,是不是可以慢一点? | ToB产业观察
  • 央行行长:债券市场“科技板”准备工作基本就绪,目前近百家市场机构计划发行超三千亿科技创新债
  • 央行宣布优化两项支持资本市场的货币政策工具
  • 溢价率19.48%,民企番禺置业3.07亿元竞得广州番禺融媒体中心北侧地块