前言

有点惊讶吧?这个Hot100解题笔记还是提前到来了。因为呢,dp题单实在是太多了,然后面试题又有大部分都出现在Hot100里面。我的目标是把hot100刷个2遍左右,所以我准备现在抢跑:将dp题单和hot100同时往前推(在文章创建的时候是这样的,后面我猜可能dp会先停下来,然后继续推hot100)。

话不多说,下面直接开始解题!

PS:下面的数字格式是序号-力扣的题目编号,解题语言默认cpp。

1-1 两数之和

题目链接:https://leetcode.cn/problems/two-sum/description/?envType=study-plan-v2&envId=top-100-liked

难度:简单

解题日期:2025.12.15

标签:数组 哈希表

虽说这是一道简单题,直接套双重循环也能过,但是这样总是差点意思。我是看了一眼题目的提示才知道要用哈希表(注意!unordered_map才是哈希表,map不是!)。

基本逻辑就是,利用哈希表查找速度O(1)的特性,把数组和下标对应丢进映射,然后遍历数组,将target与当前元素作差,然后在映射里面查找这个:如果找得到,就取出两个下标返回;如果找不到,就接着往下找,总能找到的。

然后呢,题目不允许取同一个下标两次,所以还得在取出下标的时候判断一下是否和当前下标相等,不然不能取。

至于你们可能担心的数组有重复数字的问题,不用担心,下面的代码在写映射的时候默认保留的是那个数字最大的下标,而我们遍历是从前往后遍历的,就算这是唯一解也凑得出。是不是有点bug+bug=没有bug的感觉了哈哈哈

上代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll MOD = 1000000007;

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> dict;
        //1.给数字-下标对放进哈希映射
        for (int i=0;i<nums.size();i++) {
            dict[nums[i]] = i;
        }
        //2.遍历找搭配
        for (int i=0;i<nums.size();i++) {
            int x = target-nums[i];
            if (dict.count(x)>0 && dict[x]!=i) {
                return vector<int>{i,dict[x]}; //题目说返回顺序随意,所以我就懒得排序了
            }
        }
        return vector<int>{0,0}; //这一行是为了防止报错,题目用例保证有且只有一个答案,所以不会走到这里的
    }
};

2-49 字母异位词分组

题目链接:https://leetcode.cn/problems/group-anagrams/description/?envType=study-plan-v2&envId=top-100-liked

难度:中等

解题日期:2025.12.15

标签:数组 哈希表 字符串 排序

这题还是挺有意思的,你看标签就知道了,需要先把每个字符串排序,这样才能知道它们互相之间是不是字母异位词。

那么怎么排序呢?

我目前学过的语言有Python,Go,Cpp这三个,其中前两者的字符串(string)类型都是不可变的,但是cpp的string属实是让我大开眼界了:不但可变(支持用sort排序),而且还能拿来作为哈希表映射的键(可哈希),这写起来不要太舒适。

所以,用cpp做这道题,直接遍历一遍字符串数组,然后对每个字符串直接使用sort就行了,非常方便。

然后下面的逻辑可能稍微有点绕,我干讲也不太好讲,直接在注释里面和代码一起讲好了。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll MOD = 1000000007;

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        //1.拷贝一份字符串数组,并给每个字符串排序
        auto strs2 = strs; //这里用于存放唯一的字母异位词,大概率会出现一堆重复的字符串(从题目的用例来看)
        for (auto & s : strs2) {
            sort(s.begin(),s.end());
        }
        //2.使用哈希表管理二维数组的插入和下标管理
        unordered_map<string,int> dict; //这个映射存放已经插入的字符串在ans数组中的位置
        vector<vector<string>> ans; //存放答案的二维数组
        //这里用位置遍历是利用了strs和strs2的唯一字母异位词与原字符串的对齐,方便取出原字符串
        for (int i=0;i<strs.size();i++) {
            if (dict.count(strs2[i])==0) { //如果在dict里面没找到这个唯一的字母异位词,说明是新创建
                dict[strs2[i]]=ans.size(); //将这一系列字母异位词在ans数组中的位置存进dict
                ans.emplace_back(vector<string>{strs[i]}); //然后插入新的一行字符串数组
            } else { //如果有,只需找到对应的下标然后插入即可
                ans[dict[strs2[i]]].emplace_back(strs[i]); 
                //从dict中取出这一系列的字母异位词所在的ans数组的下标,然后插入最新的字母异位词
            }
        }
        return ans;
    }
};

看懂了吧?总结一下,就是利用排序后strs2数组中的字母异位词的唯一性,将其与strs中的原字符串相匹配,再配合映射进行它在ans二维数组中插入位置的管理,从而形成了有点小绕的逻辑闭环。

3-283 移动零

题目链接:https://leetcode.cn/problems/move-zeroes/description/?envType=study-plan-v2&envId=top-100-liked

难度:简单

解题日期:2025.12.16

标签:数组 双指针

好久没做双指针的题目了,虽说这道题的难度是简单,并且我看到题目没几秒钟就想到了用额外空间该怎么做。

但是题目要求原地修改数组。我真求你了。

然后我又想了一会,想出了用一个指针找0,另一个指针负责把后面的元素一个个往前挪到前面的指针指向的位置把0盖掉,纯纯的用时间复杂度换空间复杂度,我真没招了。

然后咨询了一下ai,也算是回到了正轨:使用快慢指针模型来解决这道题目。

啥是快慢指针模型?

“快慢指针 - 读写模型”(或者叫“填充/覆盖模型”):

  • 快指针 (Reader):负责读数据,判断这个数据要不要保留。
  • 慢指针 (Writer):负责写数据,始终指向“下一个有效数据的存放位置”。

在这道题目里面,快指针(下面统称为fast)负责寻找非0元素,而慢指针(下面统称为slow)负责寻找fast指针找到的非0元素的存放位置。

基本逻辑是这样的:fast一直往后挪,不停,所以直接用for循环就行;slow只有在fast找到非0元素时,才会将fast和slow所指元素交换,然后往后挪一位。

并且,这个设计非常精妙,保证了当发生交换时只有以下两种情况:要么是slow指向0,fast指向非0,要么是fast和slow一起指向同一个非0元素,也就是进行了"原地交换",无事发生。

这是如何保证的呢?因为在上面的逻辑里面,假设当前fast和slow都指向一个非0元素,而下一个元素就是0,然后按照逻辑,fast和slow都往后挪一位,此时slow就自然指向了0。然后呢,由于fast是一直往后的,所以下一轮循环,由于fast指向了0,所以,slow不会往后。这样自然而然,就使得fast指向非0而slow指向了0。也可以这么理解:slow只是正在维护非0元素的边界(有点像插入排序,都是维护已处理-未处理的边界)。

当 fast 和 slow 错开时,slow 和 fast 中间夹着的那个区域(即 nums[slow...fast-1]),全都是 0。这就解释了为什么要把 nums[fast] 换过来——因为要把中间的这堆 0 往后挤!

这样逻辑就很清楚了吧?看代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll MOD = 1000000007;

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow=0;
        for (int fast=0;fast<nums.size();fast++) {
            if (nums[fast]!=0) { //只有遇到非0才会往后挪slow
                swap(nums[fast],nums[slow]);
                slow++;
            }
        }
    }
};

总结:一旦遇到数组原地筛选/移除/移动类题目,直接套用:Fast 找目标,Slow 守坑位。

最后修改:2025 年 12 月 16 日
如果觉得我的文章对你有用,请随意赞赏