力扣hot100

目录

哈希 

1 力扣49 两数之和

2 力扣49 字母异位词分组

3 力扣128 最长连续序列

双指针

4 力扣283 移动零

5 力扣11 盛水最多的容器

6 力扣15 三数之和

7 力扣42 接雨水

滑动窗口

8 力扣3 无重复字符的最长子串

9 力扣483 找到字符串中所有字母异位词

子串

10 力扣560 和为K的子数组(子数组连续)

11 力扣239 滑动窗口最大值

12 力扣76 最小覆盖子串

普通数组

13 力扣53 最大子数组和

14 力扣56 合并区间

15 力扣189 轮转数组(3次翻转)

16 力扣238 除自身以外数组的乘积

17 力扣41 缺失的第一个正数

矩阵

18 力扣73 矩阵置零

19 力扣54 螺旋矩阵

20 力扣48 旋转图像

20 力扣240 搜索二维矩阵2

链表

21 力扣160 相交链表

22 力扣206 反转链表

23 力扣234 回文链表

24 力扣141 环形链表

25 力扣142 环形链表2

26 力扣21 合并两个有序的链表

——27 力扣2 两数相加

28 力扣19 删除链表的倒数第N个节点

29 力扣24 两两交换链表的节点

30 力扣25 K个一组翻转链表

——31 力扣138 随机链表的复制

哈希表 

拼接+拆分

32 力扣148 排序链表

归并排序 

——33 力扣23 合并K个升序链表

归并排序 

——34 力扣146 LRU缓存

二叉树

35 力扣240 二叉树的中序遍历

36 力扣104 二叉树的最大深度

37 力扣226 翻转二叉树

38 力扣101 对称二叉树

39 力扣101 二叉树的直径

40 力扣102 二叉树的层序遍历

40 力扣108 将有序数组转为二叉搜索树

41 力扣98 验证二叉搜索树

42 力扣230 二叉搜索树中的第K个最小元素

43 力扣230 二叉树中的右视图

44 力扣114 二叉树展开为链表

45 力扣105 从前序与中序遍历序列构造二叉树

46 力扣437 路径总和3(二叉树 + 回溯)

47 力扣236 二叉树的最近公共祖先

48 力扣124 二叉树的最大路径和

——图论

49 力扣200 岛屿数量

50 力扣200 腐烂的橘子

51 力扣207 课程表

52 力扣208 前缀树

回溯

53 力扣46 全排列

54 力扣78 子集

55 力扣17 电话号码的字母组合

56 力扣39 组合总和

57 力扣22 括号生成

——58 力扣79 单词搜索

59 力扣131 分割回文串

60 力扣51 N皇后

二分查找

61 力扣35 搜索插入位置

62 力扣74 搜索二维矩阵

63 力扣34 在排序数组中查找元素的第一个和最后一个位置

取巧方式 

常规思想

64 力扣33 搜索旋转排序数组

65 力扣153 寻找旋转排序数组中的最小值

66 力扣4 寻找两个正序数组的中位数

——栈

67 力扣20 有效的括号

68 力扣155 最小栈

69 力扣394 字符串解码

70 力扣739 每日温度(单调栈应用)

71 力扣84 柱状图中最大的矩形

72 力扣215 数组中的第K个最大元素

泛型算法排序

快排

73 力扣347 前K个高频元素

74 力扣295 数据流的中位数

贪心算法

75 力扣121 买卖股票的最佳时机

76 力扣55 跳跃游戏

77 力扣45 跳跃游戏2

78 力扣763 划分字母区间

动态规划

79 力扣70 爬楼梯

80 力扣118 杨辉三角

81 力扣198 打家劫舍

82 力扣297 完全平方数

83 力扣322 零钱兑换

84 力扣139 单词拆分

85 力扣300 最长递增子序列

86 力扣152 乘积最大子数组

87 力扣416 分割等和子集

——88 力扣32 最长有效括号

动态多维规划

89 力扣62 不同路径

——90 力扣64 最小路径和

——91 力扣5 最长回文子串

92 力扣1143 最长公共子序列

93 力扣72 编辑距离

技巧

94 力扣136 只出现一次的数字

95 力扣169 多数元素

96 力扣75 颜色分类

97 力扣31 下一个排列

98 力扣287 寻找重复数


 哈希 

1 力扣49 两数之和

1d874b27b2b44794a177280d411c4462.png

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int>umap;     //key存放数值,value存放下标
        
        for(int i=0;i<nums.size();i++){
            auto iter = umap.find(target-nums[i]);
            if(iter!=umap.end()){
                return {iter->second,i};
            }
            umap.insert(make_pair(nums[i],i));
        }

        return{};
    }
};

2 力扣49 字母异位词分组

7ff9bfc300a44e0bb1cf124a1ffd4d83.png

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string> &strs) {
        unordered_map<string, vector<string>> m;
        for (auto &s : strs) {
            string sorted_s = s;
            ranges::sort(sorted_s);
            m[sorted_s].push_back(s); // sorted_s 相同的字符串分到同一组
        }
        vector<vector<string>> ans;
        ans.reserve(m.size()); // 预分配空间
        for (auto &[_, value] : m) {
            ans.push_back(value);
        }
        return ans;
    }
};

3 力扣128 最长连续序列

98efafefce7e4870b8a5df0da503b5d5.png

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> num_set;
        for (const int& num : nums) {
            num_set.insert(num);
        }

        int longestStreak = 0;

        for (const int& num : num_set) {
            if (!num_set.count(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;

                while (num_set.count(currentNum + 1)) {
                    currentNum += 1;
                    currentStreak += 1;
                }

                longestStreak = max(longestStreak, currentStreak);
            }
        }

        return longestStreak;           
    }
};

双指针

4 力扣283 移动零

cd132d43521d46ef80c86785acf0c314.png

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size(), left = 0, right = 0;
        while (right < n) {
            if (nums[right]) {
                swap(nums[left], nums[right]);
                left++;
            }
            right++;
        }
    }
};

5 力扣11 盛水最多的容器

ebd02206fbea4c8d8e8d7e6d45a32c8f.png

class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0, r = height.size() - 1;
        int ans = 0;
        while (l < r) {
            int area = min(height[l], height[r]) * (r - l);
            ans = max(ans, area);
            if (height[l] <= height[r]) {
                ++l;
            }
            else {
                --r;
            }
        }
        return ans;
    }
};

6 力扣15 三数之和

5257d65a76e1467995970fefd710488e.png

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>>res;
        sort(nums.begin(),nums.end());

        for(int i=0;i<nums.size();i++){
            if(nums[i]>0)return res;    //边界条件

            if(i>0&&nums[i]==nums[i-1])continue;
            unordered_set<int>uset;
            for(int j = i+1;j<nums.size();j++){
                //因为i\j\k都不能相同,所以需要从i+2开始
                if(j>i+2&&nums[j]==nums[j-1]&&nums[j]==nums[j-2])continue;
                int c = 0-nums[i]-nums[j];
                if(uset.find(c)!=uset.end()){
                    res.push_back(vector<int>{nums[i],nums[j],c});
                    uset.erase(c);      //c只是一个媒介,不需要保持
                }else{
                    uset.insert(nums[j]);   //将遍历的元素放入哈希表
                }
            }

        }
        return res;
    }
};

7 力扣42 接雨水

55c6ac54f71a4afea4777384a107a9f6.png

记录从左向右的最大值和从右到左的最大值,每个坐标的高度水平能接的雨水量是两边比他高的更小的乘上差值630e319f666940caab39b671fb63ba49.png

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);
        int size = maxRight.size();

        // 记录每个柱子左边柱子最大高度
        maxLeft[0] = height[0];
        for (int i = 1; i < size; i++) {
            maxLeft[i] = max(height[i], maxLeft[i - 1]);
        }
        // 记录每个柱子右边柱子最大高度
        maxRight[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            maxRight[i] = max(height[i], maxRight[i + 1]);
        }
        // 求和
        int sum = 0;
        for (int i = 0; i < size; i++) {
            int count = min(maxLeft[i], maxRight[i]) - height[i];
            if (count > 0) sum += count;
        }
        return sum;
    }
};

滑动窗口

8 力扣3 无重复字符的最长子串

f0a5417d77424280ab9417d575e8bed8.png

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> dic;
        int i = -1, res = 0, len = s.size();

        //遍历完才能知道最长的不重复长度    abcaingnkdl
        for(int j = 0; j < len; j++) {        
            if (dic.find(s[j]) != dic.end())
                i = max(i, dic.find(s[j])->second); // 更新左指针
            dic[s[j]] = j; // 哈希表记录
            res = max(res, j - i); // 更新结果
        }
        return res;
    }
};

9 力扣483 找到字符串中所有字母异位词

c13f37dbaef14a949c5acb0540b44222.png

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int sLen = s.size(), pLen = p.size();

        if (sLen < pLen) {
            return vector<int>();
        }

        vector<int> ans;
        vector<int> sCount(26);
        vector<int> pCount(26);

        //同时统计字符串中前Plen长度的字符个数
        for (int i = 0; i < pLen; ++i) {
            ++sCount[s[i] - 'a'];
            ++pCount[p[i] - 'a'];
        }

        if (sCount == pCount) {    //两个数组相等装入结果0
            ans.emplace_back(0);
        }

        //开始滑动
        for (int i = 0; i < sLen - pLen; ++i) {
            --sCount[s[i] - 'a'];        //减少窗口的首位
            ++sCount[s[i + pLen] - 'a'];    //增加窗口的末尾

            if (sCount == pCount) {
                ans.emplace_back(i + 1);
            }
        }

        return ans;
    }
};

子串

10 力扣560 和为K的子数组(子数组连续)

db684363469e42f6b850561bac4b0847.png

算法面试实录-和为 k 的子数组_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1gN411E7Zx/?spm_id_from=333.337.search-card.all.click&vd_source=b5cc04f324fc9d6ee48a5febd77392fc 上面的链接中前缀和 和 哈希的关联找子数组讲的清楚

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int preSum = 0;
        int ans = 0;
        // 键:前缀和,值:前缀和出现的次数
        unordered_map<int, int> record;

        // 初始化时为空区间,则前缀和为0,出现的次数为1,这样可以统计单个元素满足的情况
        record[0] = 1;

        for(const int& num: nums) {
            preSum += num;
            // 检查当前的前缀和preSum减去目标值k后的结果在哈希表中是否存在
            if(record.count(preSum - k)) {
                ans += record[preSum - k];
            }
            ++record[preSum];
        }
        return ans;
    }
};

11 力扣239 滑动窗口最大值

8f07207820214e1d9c956ba744b52a66.png

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        deque<int> q;

        //队列中只存放大到小的数值,当有更大的值出现时,把队列中的数全部出队
        for (int i = 0; i < k; ++i) {
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
        }

        vector<int> ans = {nums[q.front()]};
        for (int i = k; i < n; ++i) {
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);

            //当左侧的值要被丢弃时,i-k是窗口左侧第一个下标
            while (q.front() <= i - k) {
                q.pop_front();
            }
            ans.push_back(nums[q.front()]);
        }
        return ans;
    }
};

12 力扣76 最小覆盖子串

2f622327b63b4bb0984fc2b431fabe9a.png

class Solution {
    bool is_covered(int cnt_s[], int cnt_t[]) {

        //如果其中一个字母出现的次数小于寻找的字符串中的次数,不存在
        for (int i = 'A'; i <= 'Z'; i++) {
            if (cnt_s[i] < cnt_t[i]) {
                return false;
            }
        }
        for (int i = 'a'; i <= 'z'; i++) {
            if (cnt_s[i] < cnt_t[i]) {
                return false;
            }
        }
        return true;
    }

public:
    string minWindow(string s, string t) {
        int m = s.length();
        int ans_left = -1, ans_right = m, left = 0;
        int cnt_s[128]{}, cnt_t[128]{};    //使用128个ASCII码存储

        for (char c : t) {    //统计t字符串的字符出现情况
            cnt_t[c]++;
        }

        //滑动窗口
        for (int right = 0; right < m; right++) { // 移动子串右端点
            cnt_s[s[right]]++; // 右端点字母移入子串
            while (is_covered(cnt_s, cnt_t)) { // 涵盖
                if (right - left < ans_right - ans_left) { // 找到更短的子串
                    ans_left = left; // 记录此时的左右端点
                    ans_right = right;
                }
                cnt_s[s[left++]]--; // 左端点字母移出子串
            }
        }

        //如果子串的起始值为0,返回空字符串,否则从左侧起点截取对应的长度
        return ans_left < 0 ? "" : s.substr(ans_left, ans_right - ans_left + 1);
    }
};

普通数组

13 力扣53 最大子数组和

f3d6a722558c48b992b5707b0234b59a.png

贪心算法,当目前和小于0就从头开始加,当更大的时候就更换结果 

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res=INT_MIN;
        int count = 0;
        for(int i=0;i<nums.size();i++){
            count+=nums[i];
            if(count>res){
                res = count;
            }
            if(count<0)count = 0;
        }
        return res;
    }
};

14 力扣56 合并区间

b209a41a5d264fb5bfb5670dfa772648.png

先按一边排序,先放进一个区间,之后遍历,对另一边进行判断,不断更新判断边的数值,无重合就插入新的区间,有重合就更新插入新区间的边界值 

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),[](vector<int>l,vector<int>r){
            return l[0]<r[0];
        });

        vector<vector<int>>res={};
        res.emplace_back(intervals[0]);

        for(int i=1;i<intervals.size();i++){
            if(res.back()[1]>=intervals[i][0]){
                res.back()[1] = max(intervals[i][1],res.back()[1]);
            }else{
                res.emplace_back(intervals[i]);
            }
        }
        return res;

    }
};

15 力扣189 轮转数组(3次翻转)

f936a9865cc94ae0a9489dbd71086b01.png

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        auto reverse = [&](int i, int j) {
            while (i < j) {
                swap(nums[i++], nums[j--]);
            }
        };

        int n = nums.size();
        k %= n; // 轮转 k 次等于轮转 k%n 次
        reverse(0, n - 1);
        reverse(0, k - 1);
        reverse(k, n - 1);
    }
};

16 力扣238 除自身以外数组的乘积

83c785b91e434fcd9ac3cb3af3b3d60b.png9ea9db8eebe94fb7b1a8dd8f4f08c829.png

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> pre(n, 1);
        for (int i = 1; i < n; i++) {
            pre[i] = pre[i - 1] * nums[i - 1];
        }

        vector<int> suf(n, 1);
        for (int i = n - 2; i >= 0; i--) {
            suf[i] = suf[i + 1] * nums[i + 1];
        }

        vector<int> ans(n);
        for (int i = 0; i < n; i++) {
            ans[i] = pre[i] * suf[i];
        }
        return ans;
    }
};
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int len = nums.size();
        if (len == 0) return {};
        vector<int> ans(len, 1);
        ans[0] = 1;
        int tmp = 1;

        for (int i = 1; i < len; i++) {        //先求左部分乘积
            ans[i] = ans[i - 1] * nums[i - 1];
        }

        for (int i = len - 2; i >= 0; i--) {    //使用tmp存储右侧乘积结果
            tmp *= nums[i + 1];
            ans[i] *= tmp;
        }
        return ans;
    }
};

17 力扣41 缺失的第一个正数

f893e161d0354c0c9f19ec44ea850aa2.png

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        for (int& num: nums) {    //先将元素小于0的数剔除
            if (num <= 0) {
                num = n + 1;
            }
        }
        for (int i = 0; i < n; ++i) {        //使用负数标记出现过的位置
            int num = abs(nums[i]);
            if (num <= n) {
                nums[num - 1] = -abs(nums[num - 1]);
            }
        }
        for (int i = 0; i < n; ++i) {
            if (nums[i] > 0) {
                return i + 1;
            }
        }
        return n + 1;
    }
};

矩阵

18 力扣73 矩阵置零

c34abc3b8b524d62bf6180bdb1068511.png

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<int> row(m), col(n);    //记录出现0的情况

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (!matrix[i][j]) {
                    row[i] = col[j] = true;
                }
            }
        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (row[i] || col[j]) {        //如果判断行或者列是0,则将其置为0
                    matrix[i][j] = 0;
                }
            }
        }
    }
};

19 力扣54 螺旋矩阵

a059770ea8494e8c85dce681cb207d72.png


class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m=matrix.size();
        int n=matrix[0].size();
        vector<int> res;
        int i,j;
        int startx=0,starty=0;
        int minmum=min(m,n);//把m和n的最小值记录下来
        int loop=minmum/2;//要跑的圈数
        int offset=1;//动态调整每圈每个边的长度,都遵循左闭右开的原则
        while(loop--){
            i=startx;//每圈开始前要调整初始元素的位置,比如第一圈初始为[0][0],第二圈就是[1][1]
            j=starty;
            for(j=starty;j<n-offset;j++){//上
                res.push_back(matrix[startx][j]);
            }
            for(i=startx;i<m-offset;i++){//右
                res.push_back(matrix[i][j]);
            }
            for(;j>starty;j--){//下
                res.push_back(matrix[i][j]);
            }
            for(;i>startx;i--){//左
                res.push_back(matrix[i][j]);
            }
            startx++;
            starty++;
            offset++;
        }
        if(minmum%2==1){//矩阵的宽为奇数时,遍历到最后会留一行或一列元素未遍历
            i=startx;//把所有的圈遍历完后,要把i,j再次赋值到新的初始位置准备遍历那一行或一列
            j=starty;//startx,starty已经在最后一圈里自增1了
            if(minmum==m){//左右为宽,上下为长,这种情况未遍历的为一行
                for(;j<n-offset+1;j++){//不用在遵循左闭右开的原则了,所以n-offset+1,把最后一个元素也遍历了
                    res.push_back(matrix[startx][j]);
                } 
            }
            else{//左右为长,上下为狂,这种情况未遍历的为一列
                for(;i<m-offset+1;i++){
                    res.push_back(matrix[i][j]);
                } 
            }
        }
        return res;
    }
};

20 力扣48 旋转图像

79c11d5f0f2c4c75b8b58d9cf0c0af11.png

ad9598ec95dc4bbc9ac4aebfcf11db77.png

class Solution {
public:
    void rotate(vector<vector<int> > &matrix) {

        int Layer   = matrix.size()/2;    //需要旋转的层数
        int Move    = matrix.size();

        /* Top, Left, Bottom, Right */    //每次移动4个元素
        int T = 0;
        int L = 0;
        int B = matrix.size()-1;
        int R = matrix.size()-1;
        
        for( int LayerIdx = 0; LayerIdx < Layer; ++LayerIdx )
        {
            for(int i = 0; i < (Move-1) ; i++) 
            {
                swap( matrix[T][L+i], matrix[T+i][R] );
                swap( matrix[T][L+i], matrix[B][R-i] );
                swap( matrix[T][L+i], matrix[B-i][L] );
            }
            ++T; 
            --B; 
            --R; 
            ++L;        
            Move -= 2;
        }
    }
};

20 力扣240 搜索二维矩阵2

83b06664fbc142a880e1b90f402e1518.png

从左下或者右上开始 ,如果两边的单调性相同,移动时无法判断

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int i = 0, j = matrix[0].size() - 1;
        while(i < matrix.size() && j >= 0) {
            if (target == matrix[i][j]) {
                return true;
            }
            else if (target > matrix[i][j]) {
                i++;
            } else {
                j--;
            }
        }
        return false;
    }
};

链表

21 力扣160 相交链表

ce1ede3a75f449829c19a8138e711a69.png

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == NULL || headB == NULL) {
            return NULL;
        }
        ListNode* tmp1 = headA;
        ListNode* tmp2 = headB;
        while(tmp1!=tmp2){
            if(tmp1==NULL){
                tmp1 = headB;
            }else{
                tmp1 = tmp1->next;
            }
            if(tmp2==NULL){
                tmp2 = headA;
            }else{
                tmp2 = tmp2->next;
            }
        }
        return tmp1;
    }
};

22 力扣206 反转链表

72f348a5190b49d7ba9ee5e3e5f19c75.png

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;

        while(cur!=nullptr){
            ListNode* tmp =cur->next;
             cur->next=pre ;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

23 力扣234 回文链表

0267cf881b4641dc9eda36a0360df365.png

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<int> vals;
        while (head != nullptr) {
            vals.emplace_back(head->val);
            head = head->next;
        }
        for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {
            if (vals[i] != vals[j]) {
                return false;
            }
        }
        return true;
    }
};

24 力扣141 环形链表

76b7ca5a63e847f4a1ab9d99e765fef7.png

class Solution {
public:
    bool hasCycle(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return false;
        }
        ListNode* slow = head;
        ListNode* fast = head->next;
        while (slow != fast) {
            if (fast == nullptr || fast->next == nullptr) {
                return false;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

25 力扣142 环形链表2

cd55e82d7ea440fea87f8c2fcd4ae06e.png

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==NULL)return NULL;
            ListNode* fast = head;
            ListNode* slow = head;
            while(fast->next!=NULL&&fast->next->next!=NULL){
                slow = slow->next;
                fast = fast->next->next;
                if(fast==slow){
                    ListNode* tmp1 = fast;
                    ListNode* tmp2 = head;
                    while(tmp1!=tmp2){
                        tmp1 = tmp1->next;
                        tmp2 = tmp2->next;
                    }
                    return tmp2;
                }
            }
            return NULL;
    }
};

26 力扣21 合并两个有序的链表

29ea5befac654498b30767669f7f3ee8.png

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* dum = new ListNode(0);
        ListNode* cur = dum;
        while (list1 != nullptr && list2 != nullptr) {
            if (list1->val < list2->val) {
                cur->next = list1;
                list1 = list1->next;
            }
            else {
                cur->next = list2;
                list2 = list2->next;
            }
            cur = cur->next;
        }
        cur->next = list1 != nullptr ? list1 : list2;
        return dum->next;
    }
};

——27 力扣2 两数相加

3962a22666d948d09e7709400bb3755f.png

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        // 构造哑巴节点 dummy,最后返回 dummy.next, 以方便处理新链表的头节点。
        ListNode* dummy = new ListNode(0);
        ListNode* node = dummy; // node 一直会变化(前进)
        int carrier = 0; // 进位
        
        // 只要有没走到头的链表或者进位不为 0 就一直前进。
        while (l1 || l2 || carrier) {
            // 求和,考虑可能有链表走到头
            int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carrier;

            // 在尾部添加节点
            node->next = new ListNode(sum % 10);
            node = node->next;
            
            // 更新进位,并向两个链表尾部前进
            carrier = sum / 10;
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }

        return dummy->next;
    }
};

28 力扣19 删除链表的倒数第N个节点

f8db9c8be9c4417ab9fcb004c142c59f.png

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* fast = dummyhead;
        
        while(n--&&fast!=nullptr){
            fast = fast->next;
        }
        fast= fast->next;   //fast多走一步,这样才能将slow放到删除节点的前一个节点

        ListNode* slow = dummyhead;
        while(fast!=nullptr){
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;

        return dummyhead->next;
    }
};

29 力扣24 两两交换链表的节点

640c7524d730481aa436a76edac04100.png

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;     
        ListNode* cur = dummyhead;
        while(cur->next!=nullptr&&cur->next->next!=nullptr){
             ListNode* tmp1 = cur->next->next;
             ListNode* tmp2 = tmp1->next;
             ListNode* tmp3 = cur->next;
             cur->next->next = tmp2;
             tmp1->next = tmp3;
             cur->next = tmp1;
             cur = cur->next;
             cur = cur->next;
        }
        return dummyhead->next;
    }
};

30 力扣25 K个一组翻转链表

d47261f4dc624d7dad07ed21d4fd2fd7.png

两两翻转改为K个一组翻转,通过循环将两两翻转改为K个一组 

class Solution {
public:
    ListNode *reverseKGroup(ListNode *head, int k) {
        int n = 0;
        for (ListNode *cur = head; cur; cur = cur->next)
            ++n; // 统计节点个数

        ListNode *dummy = new ListNode(0, head), *p0 = dummy;
        ListNode *pre = nullptr, *cur = head;
        for (; n >= k; n -= k) {
            for (int i = 0; i < k; ++i) { // 同 92 题
                ListNode *nxt = cur->next;
                cur->next = pre; // 每次循环只修改一个 next,方便大家理解
                pre = cur;
                cur = nxt;
            }

            // 见视频
            ListNode *nxt = p0->next;
            p0->next->next = cur;
            p0->next = pre;
            p0 = nxt;
        }
        return dummy->next;
    }
};

——31 力扣138 随机链表的复制

9123fa17a1a848df904b0df8f0282778.png

哈希表 

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        Node* cur = head;
        unordered_map<Node*, Node*> map;
        // 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        while(cur != nullptr) {
            map[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        // 4. 构建新链表的 next 和 random 指向
        while(cur != nullptr) {
            map[cur]->next = map[cur->next];
            map[cur]->random = map[cur->random];
            cur = cur->next;
        }
        // 5. 返回新链表的头节点
        return map[head];
    }
};

拼接+拆分

 

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        Node* cur = head;
        // 1. 复制各节点,并构建拼接链表
        while(cur != nullptr) {
            Node* tmp = new Node(cur->val);
            tmp->next = cur->next;
            cur->next = tmp;
            cur = tmp->next;
        }
        // 2. 构建各新节点的 random 指向
        cur = head;
        while(cur != nullptr) {
            if(cur->random != nullptr)
                cur->next->random = cur->random->next;
            cur = cur->next->next;
        }
        // 3. 拆分两链表
        cur = head->next;
        Node* pre = head, *res = head->next;
        while(cur->next != nullptr) {
            pre->next = pre->next->next;
            cur->next = cur->next->next;
            pre = pre->next;
            cur = cur->next;
        }
        pre->next = nullptr; // 单独处理原链表尾节点
        return res;      // 返回新链表头节点
    }
};

32 力扣148 排序链表

58ac272c077849ecb23a1932d0197dc9.png

归并排序 

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode* mid = findMid(head);
        ListNode* newHead = mid->next;
        mid->next = nullptr;

        ListNode* left = sortList(head);
        ListNode* right = sortList(newHead);
        return merge(left, right);
    }

    ListNode* findMid(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast->next && fast->next->next) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }

    ListNode* merge(ListNode* l1, ListNode* l2) {
        if (!l1) return l2;
        if (!l2) return l1;
        if (l1->val <= l2->val) {
            l1->next = merge(l1->next, l2);
            return l1;
        } else {
            l2->next = merge(l1, l2->next);
            return l2;
        }
    }
};

——33 力扣23 合并K个升序链表

6edd51bbff7840d4b1f9ff75a46917ec.png

归并排序 

class Solution {
    // 21. 合并两个有序链表
    ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {
        ListNode dummy{}; // 用哨兵节点简化代码逻辑
        auto cur = &dummy; // cur 指向新链表的末尾
        while (list1 && list2) {
            if (list1->val < list2->val) {
                cur->next = list1; // 把 list1 加到新链表中
                list1 = list1->next;
            } else { // 注:相等的情况加哪个节点都是可以的
                cur->next = list2; // 把 list2 加到新链表中
                list2 = list2->next;
            }
            cur = cur->next;
        }
        cur->next = list1 ? list1 : list2; // 拼接剩余链表
        return dummy.next;
    }

    // 合并从 lists[i] 到 lists[j-1] 的链表
    ListNode *mergeKLists(vector<ListNode *> &lists, int i, int j) {
        int m = j - i;
        if (m == 0) return nullptr; // 注意输入的 lists 可能是空的
        if (m == 1) return lists[i]; // 无需合并,直接返回
        auto left = mergeKLists(lists, i, i + m / 2); // 合并左半部分
        auto right = mergeKLists(lists, i + m / 2, j); // 合并右半部分
        return mergeTwoLists(left, right); // 最后把左半和右半合并
    }

public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        return mergeKLists(lists, 0, lists.size());
    }
};

——34 力扣146 LRU缓存

66beaaf535c447399bcff9673971f8d2.png

struct DLinkedNode {
    int key, value;
    DLinkedNode* prev;
    DLinkedNode* next;
    DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
    DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
private:
    unordered_map<int, DLinkedNode*> cache;
    DLinkedNode* head;
    DLinkedNode* tail;
    int size;
    int capacity;

public:
    LRUCache(int _capacity): capacity(_capacity), size(0) {
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head->next = tail;
        tail->prev = head;
    }
    
    int get(int key) {
        if (!cache.count(key)) {
            return -1;
        }
        // 如果 key 存在,先通过哈希表定位,再移到头部
        DLinkedNode* node = cache[key];
        moveToHead(node);
        return node->value;
    }
    
    void put(int key, int value) {
        if (!cache.count(key)) {
            // 如果 key 不存在,创建一个新的节点
            DLinkedNode* node = new DLinkedNode(key, value);
            // 添加进哈希表
            cache[key] = node;
            // 添加至双向链表的头部
            addToHead(node);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode* removed = removeTail();
                // 删除哈希表中对应的项
                cache.erase(removed->key);
                // 防止内存泄漏
                delete removed;
                --size;
            }
        }
        else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            DLinkedNode* node = cache[key];
            node->value = value;
            moveToHead(node);
        }
    }

    void addToHead(DLinkedNode* node) {
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    
    void removeNode(DLinkedNode* node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void moveToHead(DLinkedNode* node) {
        removeNode(node);
        addToHead(node);
    }

    DLinkedNode* removeTail() {
        DLinkedNode* node = tail->prev;
        removeNode(node);
        return node;
    }
};

二叉树

35 力扣240 二叉树的中序遍历

474a3e8542c3448ea1729372cc20ba01.png

class Solution {
public:
    void traversal(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal(cur->left, vec);  // 左
        vec.push_back(cur->val);    // 中
        traversal(cur->right, vec); // 右
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

36 力扣104 二叉树的最大深度

1f7d0eae176649f58d37a664c9525f3e.png

class solution {
public:
    int getdepth(treenode* node) {
        if (node == NULL) return 0;
        int leftdepth = getdepth(node->left);       // 左
        int rightdepth = getdepth(node->right);     // 右
        int depth = 1 + max(leftdepth, rightdepth); // 中
        return depth;
    }
    int maxdepth(treenode* root) {
        return getdepth(root);
    }
};

37 力扣226 翻转二叉树

06757428bc684dcc854d791adbd59651.png

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
};

38 力扣101 对称二叉树

5c05a611a85c48f2b7bb92db422ad2a5.png

class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right) {
        // 首先排除空节点的情况
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        else if (left == NULL && right == NULL) return true;
        // 排除了空节点,再排除数值不相同的情况
        else if (left->val != right->val) return false;

        // 此时就是:左右节点都不为空,且数值相同的情况
        // 此时才做递归,做下一层的判断
        bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
        bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
        bool isSame = outside && inside;                    // 左子树:中、 右子树:中 (逻辑处理)
        return isSame;

    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};

39 力扣101 二叉树的直径

c05abdcf5f5546908ef5096d4597f78c.png

class Solution {
    int ans;
    int depth(TreeNode* rt){
        if (rt == NULL) {
            return 0; // 访问到空节点了,返回0
        }
        int L = depth(rt->left); // 左儿子为根的子树的深度
        int R = depth(rt->right); // 右儿子为根的子树的深度
        ans = max(ans, L + R + 1); // 计算d_node即L+R+1 并更新ans
        return max(L, R) + 1; // 返回该节点为根的子树的深度
    }
public:
    int diameterOfBinaryTree(TreeNode* root) {
        ans = 1;
        depth(root);
        return ans - 1;
    }
};

40 力扣102 二叉树的层序遍历

8d3775c4d7f14155b0cbde5ce3cb6623.png

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        vector<vector<int>> result;
        while (!que.empty()) {
            int size = que.size();
            vector<int> vec;
            // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
            result.push_back(vec);
        }
        return result;
    }
};

40 力扣108 将有序数组转为二叉搜索树

34bf7e1a586d43a6bb356d6048d9ec41.png

class Solution {
private:
    TreeNode* traversal(vector<int>& nums, int left, int right) {
        if (left > right) return nullptr;
        int mid = left + ((right - left) / 2);
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = traversal(nums, left, mid - 1);
        root->right = traversal(nums, mid + 1, right);
        return root;
    }
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        TreeNode* root = traversal(nums, 0, nums.size() - 1);
        return root;
    }
};

41 力扣98 验证二叉搜索树

5569cb29c90143ee8e2deecf60bb0dd5.png

中序遍历放入数组,之后对数组进行判断

class Solution {
private:
    vector<int> vec;
    void traversal(TreeNode* root) {
        if (root == NULL) return;
        traversal(root->left);
        vec.push_back(root->val); // 将二叉搜索树转换为有序数组
        traversal(root->right);
    }
public:
    bool isValidBST(TreeNode* root) {
        vec.clear(); // 不加这句在leetcode上也可以过,但最好加上
        traversal(root);
        for (int i = 1; i < vec.size(); i++) {
            // 注意要小于等于,搜索树里不能有相同元素
            if (vec[i] <= vec[i - 1]) return false;
        }
        return true;
    }
};

42 力扣230 二叉搜索树中的第K个最小元素

306e436dfd7f46a49237f4bdb0c1d5b9.png

class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        vector<int> res;
        dfs(root, res);
        return res[k - 1];
    }
    void dfs(TreeNode* root, vector<int>& res) {
        if (!root) return;
        dfs(root->left, res);
        res.push_back(root->val);
        dfs(root->right, res);
    }
};

43 力扣230 二叉树中的右视图

1498de2f51fa4ebf9815815660e25ee9.png

层序遍历,每层最后一个元素放进去

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        vector<int> result;
        while (!que.empty()) {
            int size = que.size();        //先记录下每层的元素个数
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();

                // 将每一层的最后元素放入result数组中
                if (i == (size - 1)) result.push_back(node->val); 

                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

44 力扣114 二叉树展开为链表

a6f841eed62b4f4aba7c61a705216264.png

class Solution {
public:
    void flatten(TreeNode* root) {
        while(root){
            TreeNode* p = root->left;
            if(p){
                while(p->right) p = p->right; 
                p->right = root->right;
                root->right = root->left;
                root->left = nullptr;
            }
            root = root->right;
        }
        
    }
};

45 力扣105 从前序与中序遍历序列构造二叉树

3eac90a12b4f4a9d9fd2c2bd918bc3fb.png

class Solution {
private:
    TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
        if (postorder.size() == 0) return NULL;

        // 后序遍历数组最后一个元素,就是当前的中间节点
        int rootValue = postorder[postorder.size() - 1];
        TreeNode* root = new TreeNode(rootValue);

        // 叶子节点
        if (postorder.size() == 1) return root;

        // 找到中序遍历的切割点
        int delimiterIndex;
        for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
            if (inorder[delimiterIndex] == rootValue) break;
        }

        // 切割中序数组
        // 左闭右开区间:[0, delimiterIndex)
        vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
        // [delimiterIndex + 1, end)
        vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );

        // postorder 舍弃末尾元素
        postorder.resize(postorder.size() - 1);

        // 切割后序数组
        // 依然左闭右开,注意这里使用了左中序数组大小作为切割点
        // [0, leftInorder.size)
        vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
        // [leftInorder.size(), end)
        vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

        root->left = traversal(leftInorder, leftPostorder);
        root->right = traversal(rightInorder, rightPostorder);

        return root;
    }
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if (inorder.size() == 0 || postorder.size() == 0) return NULL;
        return traversal(inorder, postorder);
    }
};

46 力扣437 路径总和3(二叉树 + 回溯)

c3db0067771e4f08aa593eeda7185424.png

class Solution {
private:
    unordered_map<int, int> prefix;         // <前缀和,其出现次数>
    void dfs(TreeNode* root, int sum, int cur_sum, int& res)
    {
        if (!root) return;
        cur_sum += root->val;               // 更新前缀和
        // 当前路径中存在以当前节点为终点的和为sum的子路径
        if (prefix.find(cur_sum - sum) != prefix.end())
            res += prefix[cur_sum - sum];
        prefix[cur_sum]++;                  // 将当前节点加入路径
        dfs(root->left, sum, cur_sum, res); // 在其左子树中递归寻找
        dfs(root->right, sum, cur_sum, res);// 在其右子树中递归寻找
        prefix[cur_sum]--;                  // 回溯
    }
public:
    int pathSum(TreeNode* root, int sum) 
    {
        int res = 0;    // 满足条件的路径数量
        prefix[0] = 1;  // 前缀和为0的路径只有一条:哪个节点都不选
        dfs(root, sum, 0, res);
        return res;
    }
};

47 力扣236 二叉树的最近公共祖先

f89f762d7ffb4fb1b0bdac12238328fd.png

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == q || root == p || root == NULL) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left != NULL && right != NULL) return root;

        if (left == NULL && right != NULL) return right;
        else if (left != NULL && right == NULL) return left;
        else  { //  (left == NULL && right == NULL)
            return NULL;
        }

    }
};

48 力扣124 二叉树的最大路径和

55497b1e297a4209b5178a39aa5b2b3a.png

class Solution {
    int res = Integer.MIN_VALUE;    // 最大路径和,初始为极小值

    public int maxPathSum(TreeNode root) {
        dfs(root);  // 深度优先搜索
        return res;
    }

    private int dfs(TreeNode node){
        if(node == null)return 0;   // 空节点,贡献值为0
        int leftMaxPathSum = Math.max(0, dfs(node.left));   // 递归搜索节点的左子树,获取左子树贡献的路径值,小于0的贡献为0
        int rightMaxPathSum = Math.max(0, dfs(node.right)); // 递归搜索节点的右子树,获取左子树贡献的路径值,小于0的贡献为0
        res = Math.max(res, node.val + leftMaxPathSum + rightMaxPathSum);   // 以当前节点为中点的路径和
        return node.val + Math.max(leftMaxPathSum, rightMaxPathSum);   // 返回该节点参与的最大子树路径和
    }
}

——图论

49 力扣200 岛屿数量

e5a58ba58fac4eb39c8ebe5ca6b46b9c.png

 

50 力扣200 腐烂的橘子

b240979c31d24be1bb5f9a177072d810.png

 

51 力扣207 课程表

961c2c186b89402cbc235110f56829fc.png

 

52 力扣208 前缀树

1f84eafd30ab4b05bced96d6f805994b.png

 

回溯

53 力扣46 全排列

9d1c424d7298479a97953c98c40008dc.png

与子集问题不同,排列后也无法直接开始索引判断,但是还需要判断元素是否使用过,所以使用used数组 

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) continue; // path里已经收录的元素,直接跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

54 力扣78 子集

aeba161dcee34d5dbb16e9a402054be0.png

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
        if (startIndex >= nums.size()) { // 终止条件可以不加
            return;
        }
        for (int i = startIndex; i < nums.size(); i++) {
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

55 力扣17 电话号码的字母组合

6268778d7e4b41f3aca626e8e2868baf.png

class Solution {
public:
    const string lettermap[10]{     //先得到字符数组的映射
        "",
        "",
        "abc",
        "def",
        "ghi",
        "jkl",
        "mno",
        "pqrs",
        "tuv",
        "wxyz"
    };
    vector<string>res;
    string path="";
    void backtrading(string digits,int idx){
        if(path.size()==digits.size()){
            res.push_back(path);
            return ;
        }
        int dig = digits[idx] -'0';     //将指向位置的字符转为数字
        string letter = lettermap[dig];     //获得数字对应的字符串
        for(int i=0;i<letter.size();i++){
            path.push_back(letter[i]);
            backtrading(digits,idx+1);    //在backtrading的索引位置+1
            path.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        
        if(digits.size()==0){
            return res;
        }
        
        backtrading(digits,0);
        return res;
    }
};

56 力扣39 组合总和

ca3f2b691582488ca85139a00cb95bc9.png

class Solution {
public:
    vector<vector<int>>res;
    vector<int>path;
    void backtrading(vector<int>candidates,int target,int sum,int idx){
        if(sum==target){
            res.push_back(path);
            return;
        }
        if(sum>target){
            return ;
        }
        for(int i=idx;i<candidates.size();i++){
            sum+=candidates[i];
            path.push_back(candidates[i]);
            
            backtrading(candidates,target,sum,i);
            
            path.pop_back();
            sum-=candidates[i];
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        if(candidates.size()==0){
            return res;
        }
        backtrading(candidates,target,0,0);
        return res;
    }
};

57 力扣22 括号生成

529eef474307476696b90f414538f574.png

使用左右括号变量计数来进行回溯 

class Solution {
    void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
        if (cur.size() == n * 2) {
            ans.push_back(cur);
            return;
        }

        if (open < n) {    //左括号判断
            cur.push_back('(');
            backtrack(ans, cur, open + 1, close, n);
            cur.pop_back();
        }

        if (close < open) {    //右括号判断
            cur.push_back(')');
            backtrack(ans, cur, open, close + 1, n);
            cur.pop_back();
        }
    }
public:
    vector<string> generateParenthesis(int n) {
        vector<string> result;
        string current;
        backtrack(result, current, 0, 0, n);
        return result;
    }
};

——58 力扣79 单词搜索

7fca6adde5d346b38f915690c3c24426.png

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        rows = board.size();
        cols = board[0].size();
        for(int i = 0; i < rows; i++) {
            for(int j = 0; j < cols; j++) {
                if (dfs(board, word, i, j, 0)) return true;
            }
        }
        return false;
    }
private:
    int rows, cols;
    bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
        if (i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
        if (k == word.size() - 1) return true;
        board[i][j] = '\0';
        bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
        board[i][j] = word[k];
        return res;
    }
};

59 力扣131 分割回文串

4747f76438ae4782988a9d630284b62b.png

class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    void backtracking (const string& s, int startIndex) {
        // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
        if (startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome(s, startIndex, i)) {   // 是回文子串
                // 获取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳过
                continue;
            }
            backtracking(s, i + 1); // 寻找i+1为起始位置的子串
            path.pop_back(); // 回溯过程,弹出本次已经填在的子串
        }
    }
    bool isPalindrome(const string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }
public:
    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();
        backtracking(s, 0);
        return result;
    }
};

60 力扣51 N皇后

cd510f587aa34a2d9d174eb62e693249.png

class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
    if (row == n) {
        result.push_back(chessboard);
        return;
    }
    for (int col = 0; col < n; col++) {
        if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
            chessboard[row][col] = 'Q'; // 放置皇后
            backtracking(n, row + 1, chessboard);
            chessboard[row][col] = '.'; // 回溯,撤销皇后
        }
    }
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
    // 检查列
    for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}
public:
    vector<vector<string>> solveNQueens(int n) {
        result.clear();
        std::vector<std::string> chessboard(n, std::string(n, '.'));
        backtracking(n, 0, chessboard);
        return result;
    }
};

二分查找

61 力扣35 搜索插入位置

ac14aba7585c4f90bf82099950e11573.png

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0, right = n - 1, ans = n;    //先将结果放到最后一个位置
        while (left <= right) {
            int mid = ((right - left) >> 1) + left;
            if (target <= nums[mid]) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
};

62 力扣74 搜索二维矩阵

3bb3bdae1b7c43cab7ba28b7830ff12c.png

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        //m是行数,n是列数
        int m = matrix.size(), n = matrix[0].size();

        int i = 0, j = n - 1;
        while (i < m && j >= 0) { // 还有剩余元素
            if (matrix[i][j] == target) {
                return true; // 找到 target
            }
            if (matrix[i][j] < target) {
                i++; // 这一行剩余元素全部小于 target,排除
            } else {
                j--; // 这一列剩余元素全部大于 target,排除
            }
        }
        return false;
    }
};

63 力扣34 在排序数组中查找元素的第一个和最后一个位置

19a8afaea29f4b6f958eb794b3ccdfd9.png

取巧方式 

class Solution {
public:

int searchInsert(vector<int>& nums, double target) {
        int n = nums.size();
        int left = 0, right = n - 1, ans = n;    //先将结果放到最后一个位置
        while (left <= right) {
            int mid = ((right - left) >> 1) + left;
            if (target <= nums[mid]) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }

    vector<int> searchRange(vector<int>& nums, int target) {
        int left = searchInsert(nums,target-0.5);
        int right = searchInsert(nums,target+0.5);
        if(left>=nums.size()||nums[left]!=target){
            return {-1,-1};
        }else{
            return {left,right-1};
        }
    }
};

常规思想

 

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        // 二分查找下标,是否存在target的对应下标mid,没有就返回[-1,-1],有的话返回下标,
        // mid向左右范围扩展是否有相同值
        vector<int> ans;
        int left = 0, right = nums.size() - 1;
        // 用来标记是否有target在nums数组中
        int pivot = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                pivot = mid;
                break;
            }
        }
        // 二分查找中是否有结果
        if (pivot == -1) {
            ans.push_back(-1);
            ans.push_back(-1);    
        }
        else {
            int r;
            int l = r = pivot; 
            while (l >= 0 && nums[l] == target) l--;
            while (r <= nums.size() - 1 && nums[r] == target) r++;
            ans.push_back(l + 1);
            ans.push_back(r - 1);
        }            
        return ans;
    }
};

64 力扣33 搜索旋转排序数组

02679a1ab7944b8bbf9811d4545a2e7e.png

7400e5dadf4b4b71857a1fdef9d39f18.png

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = (int)nums.size();
        if (!n) {
            return -1;
        }
        if (n == 1) {
            return nums[0] == target ? 0 : -1;
        }
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) return mid;
            if (nums[0] <= nums[mid]) {
                if (nums[0] <= target && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
};

65 力扣153 寻找旋转排序数组中的最小值

5f4a1b9bccca429ba437781e329445c7.png

e35e5e0a8b794653b2f14c30a24c487c.png

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low = 0;
        int high = nums.size() - 1;
        while (low < high) {
            int pivot = low + (high - low) / 2;
            if (nums[pivot] < nums[high]) {
                high = pivot;
            }
            else {
                low = pivot + 1;
            }
        }
        return nums[low];
    }
};

66 力扣4 寻找两个正序数组的中位数

1da52104b91249aaaf9d2cc2b135b812.png

class Solution {
public:
    int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
        /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
         * 这里的 "/" 表示整除
         * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
         * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
         * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
         * 这样 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
         * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
         * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
         */

        int m = nums1.size();
        int n = nums2.size();
        int index1 = 0, index2 = 0;

        while (true) {
            // 边界情况
            if (index1 == m) {
                return nums2[index2 + k - 1];
            }
            if (index2 == n) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return min(nums1[index1], nums2[index2]);
            }

            // 正常情况
            int newIndex1 = min(index1 + k / 2 - 1, m - 1);
            int newIndex2 = min(index2 + k / 2 - 1, n - 1);
            int pivot1 = nums1[newIndex1];
            int pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
            else {
                k -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }
        }
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int totalLength = nums1.size() + nums2.size();
        if (totalLength % 2 == 1) {
            return getKthElement(nums1, nums2, (totalLength + 1) / 2);
        }
        else {
            return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
        }
    }
};

——栈

67 力扣20 有效的括号

84e8dd7c9096450298075a09d49c361f.png

class Solution {
public:
    bool isValid(string s) {
        if(s.size()%2!=0)return false;
        stack<char>st;
        for(int i=0;i<s.size();i++){
            if(s[i]=='('){
                st.push(')');
            }else if(s[i]=='['){
                st.push(']');
            }else if(s[i]=='{'){
                st.push('}');
            }else if(st.empty()||st.top()!=s[i]){   //多右括号或者不能匹配
                return false;
            }else{
                st.pop();
            }
        }
        return st.empty();
    
    }
};

68 力扣155 最小栈

e9d129b9fa244762af4bf28c9735bbba.png

class MinStack {
    stack<int> x_stack;
    stack<int> min_stack;
public:
    MinStack() {
        min_stack.push(INT_MAX);
    }
    
    void push(int x) {
        x_stack.push(x);
        min_stack.push(min(min_stack.top(), x));
    }
    
    void pop() {
        x_stack.pop();
        min_stack.pop();
    }
    
    int top() {
        return x_stack.top();
    }
    
    int getMin() {
        return min_stack.top();
    }
};

69 力扣394 字符串解码

79b26c171a2b4512978a202618027159.png

class Solution {
public:
    string getDigits(string &s, size_t &ptr) {
        string ret = "";
        while (isdigit(s[ptr])) {
            ret.push_back(s[ptr++]);
        }
        return ret;
    }

    string getString(vector <string> &v) {
        string ret;
        for (const auto &s: v) {
            ret += s;
        }
        return ret;
    }

    string decodeString(string s) {
        vector <string> stk;
        size_t ptr = 0;

        while (ptr < s.size()) {
            char cur = s[ptr];
            if (isdigit(cur)) {
                // 获取一个数字并进栈
                string digits = getDigits(s, ptr);
                stk.push_back(digits);
            } else if (isalpha(cur) || cur == '[') {
                // 获取一个字母并进栈
                stk.push_back(string(1, s[ptr++])); 
            } else {
                ++ptr;
                vector <string> sub;
                while (stk.back() != "[") {
                    sub.push_back(stk.back());
                    stk.pop_back();
                }
                reverse(sub.begin(), sub.end());
                // 左括号出栈
                stk.pop_back();
                // 此时栈顶为当前 sub 对应的字符串应该出现的次数
                int repTime = stoi(stk.back()); 
                stk.pop_back();
                string t, o = getString(sub);
                // 构造字符串
                while (repTime--) t += o; 
                // 将构造好的字符串入栈
                stk.push_back(t);
            }
        }

        return getString(stk);
    }
};

70 力扣739 每日温度(单调栈应用)

599a6515fbd740ca99f222280e03c723.png

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int>res(temperatures.size(),0);

        stack<int>st;   //单调栈,存放数组下标

        st.push(0);
        for(int i=1;i<temperatures.size();i++){
            if(temperatures[i]<=temperatures[st.top()]){    //小于等于就将遍历元素的下标放入单调栈
                st.push(i);
            }else{      //当前遍历元素大于栈顶的下标元素
                while(!st.empty()&&temperatures[i]>temperatures[st.top()]){
                    res[st.top()] = i-st.top();
                    st.pop();       //因为有出栈操作,所以在while循环对栈是否为空进行判断
                }
                //对当前元素进行操作后,还需要将当前遍历元素入栈
                st.push(i);
            }
        }
        return res;
    }
};

71 力扣84 柱状图中最大的矩形

225cb2a906e04a2aaf714879402df154.png

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        vector<int> left(n), right(n);
        
        stack<int> mono_stack;
        for (int i = 0; i < n; ++i) {
            while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
                mono_stack.pop();
            }
            left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
            mono_stack.push(i);
        }

        mono_stack = stack<int>();
        for (int i = n - 1; i >= 0; --i) {
            while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
                mono_stack.pop();
            }
            right[i] = (mono_stack.empty() ? n : mono_stack.top());
            mono_stack.push(i);
        }
        
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
        }
        return ans;
    }
};

72 力扣215 数组中的第K个最大元素

45ee8e5da7744b358845fb0477303acb.png

泛型算法排序

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        return nums[nums.size() - k];
    }
};

快排

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        return quickSelect(nums, k);
    }
    
private:
    int quickSelect(vector<int>& nums, int k) {
        // 随机选择基准数
        int pivot = nums[rand() % nums.size()];
        // 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
        vector<int> big, equal, small;
        for (int num : nums) {
            if (num > pivot)
                big.push_back(num);
            else if (num < pivot)
                small.push_back(num);
            else
                equal.push_back(num);
        }
        // 第 k 大元素在 big 中,递归划分
        if (k <= big.size())
            return quickSelect(big, k);
        // 第 k 大元素在 small 中,递归划分
        if (nums.size() - small.size() < k)
            return quickSelect(small, k - nums.size() + small.size());
        // 第 k 大元素在 equal 中,直接返回 pivot
        return pivot;
    }
};

73 力扣347 前K个高频元素

9eee6292f54a43dc906e9b4e24b5a675.png

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> um;
        for (auto& i : nums) {
            ++um[i];
        }

        priority_queue<pair<int, int>> pq;
        for (auto& i : um) {
            pq.push({i.second, i.first});
        }
        
        vector<int> res;
        while (k--) {
            res.push_back(pq.top().second);
            pq.pop();
        }

        return res;
    }
};

74 力扣295 数据流的中位数

e435a070d5404f269b48edeb1994d16e.png

左边大顶堆,右边小顶堆,小的加左边,大的加右边,平衡俩堆数,新加就弹出,堆顶给对家,奇数取多的,偶数取除2 

class MedianFinder {
public:
    priority_queue<int, vector<int>, less<int>> queMin;
    priority_queue<int, vector<int>, greater<int>> queMax;

    MedianFinder() {}

    void addNum(int num) {
        if (queMin.empty() || num <= queMin.top()) {
            queMin.push(num);
            if (queMax.size() + 1 < queMin.size()) {
                queMax.push(queMin.top());
                queMin.pop();
            }
        } else {
            queMax.push(num);
            if (queMax.size() > queMin.size()) {
                queMin.push(queMax.top());
                queMax.pop();
            }
        }
    }

    double findMedian() {
        if (queMin.size() > queMax.size()) {
            return queMin.top();
        }
        return (queMin.top() + queMax.top()) / 2.0;
    }
};

贪心算法

75 力扣121 买卖股票的最佳时机

4f03203752074ef2b1ea14c5374ad7db.png

class Solution {
public:
    int maxProfit(vector<int>& prices) 
    {
        int cost = INT_MAX, profit = 0;     //成本初始化最大值,利润初始化为0
        for (int price : prices) 
        {
            cost = min(cost, price);        //花费在之前和现在之间选择更小值
            profit = max(profit, price - cost);     //利润在当前利润和后续利润之间选择更大值
        }
        return profit;
    }
};

76 力扣55 跳跃游戏

db83bf21cfde4dd8a82d0e1e8bfdb756.png

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int cover = 0;
        if(nums.size()==1)return true;
        for(int i=0;i<=cover;i++){      //在覆盖的范围内跳动
            cover = max(cover,nums[i]+i);
            if(cover>=nums.size()-1){
                return true;
            }
        }
        return false;
    }
};

77 力扣45 跳跃游戏2

4fd9502b36924c1c80610f841939e782.png

// 版本二
class Solution {
public:
    int jump(vector<int>& nums) {
        int curDistance = 0;    // 当前覆盖的最远距离下标
        int ans = 0;            // 记录走的最大步数
        int nextDistance = 0;   // 下一步覆盖的最远距离下标
        for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在
            nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标
            if (i == curDistance) {                 // 遇到当前覆盖的最远距离下标
                curDistance = nextDistance;         // 更新当前覆盖的最远距离下标
                ans++;
            }
        }
        return ans;
    }
};

78 力扣763 划分字母区间

90c6659205e241ceb7a2dd8ac7e062ad.png

class Solution {
public:
    vector<int> partitionLabels(string s) {
        int hash[27]={};
        for(int i=0;i<s.size();i++){
            hash[s[i]-'a'] = i;     //将每个字母出现的最远下标计入数组
        }
        vector<int>res;
        int left = 0;
        int right = 0;
        for(int i=0;i<s.size();i++){
            right = max(right,hash[s[i]-'a']);
            if(i==right){
                res.push_back(right-left+1);
                left = i+1;
            }
        }
        return res;
    }
};

动态规划

79 力扣70 爬楼梯

86f01a56fe5e4021a925648c038418ca.png

class Solution {
public:
    int dp[46]={};  //dp[i],到第i个台阶有dp[i]种方法

    int climbStairs(int n) {
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};

80 力扣118 杨辉三角

1c62227de7714ed0829b0a30f4112389.png

7cb112b0a00548f08af9a4e6bfba03c2.png

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> c(numRows);
        for (int i = 0; i < numRows; i++) {
            c[i].resize(i + 1, 1);
            for (int j = 1; j < i; j++) {
                // 左上方的数 + 正上方的数
                c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
            }
        }
        return c;
    }
};

81 力扣198 打家劫舍

c34353d1c1e142f4ac8113748ceee822.png

class Solution {
public:
    int rob(vector<int>& nums) {
        vector<int>dp(nums.size()+1,0);     //考虑前i个房间的最大值为dp[i]

        if(nums.size()==1)return nums[0];
        if(nums.size()==2)return max(nums[0],nums[1]);

        dp[1] = nums[0];
        dp[2] = max(nums[0],nums[1]);

        for(int i=3;i<=nums.size();i++){
            dp[i] = max(dp[i-1],dp[i-2]+nums[i-1]);
        }
        return dp[nums.size()];

    }
};

82 力扣297 完全平方数

1b71297b9e404b1689d12dc9c0dfe3f7.png

class Solution {
public:
    int numSquares(int n) {
        //问最少放多少,初始化为最大值
        vector<int>dp(n+1,INT_MAX);     //dp[i]表示装满空间为i的背包最少使用dp【i】个

        dp[0]=0;
        for(int i=1;i*i<=n;i++){
            for(int j=i*i;j<=n;j++){
                dp[j] = min(dp[j],dp[j-i*i]+1);
            }
        }
        return dp[n];
    }
};

83 力扣322 零钱兑换

ca2b9c7d18c24acf97783ebd0c23f610.png

完全背包,装满有几种装法 

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int>dp(amount+1,INT_MAX);

        dp[0]=0;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){   //背包之间的便利从小到大
                if(dp[j-coins[i]]!=INT_MAX){
                    dp[j] = min(dp[j],dp[j-coins[i]]+1);
                }
            }
        }
        if(dp[amount]==INT_MAX)return -1;
        return dp[amount];
    }
};

84 力扣139 单词拆分

46238e9f67db4eb68619e0dba15cf01c.png

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        vector<bool>dp(s.size()+1,false);
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());

        dp[0]=true;
        for(int j=0;j<=s.size();j++){    //遍历背包      排列对结果有影响
            for(int i=0;i<j;i++){       //遍历物品

                //截出长度判断是否为字典里的值
                string word = s.substr(i,j-i);    //(截取位置,截取长度)

                //如果字典里有且之前的位置是true,那么现在的位置也是true
                if(wordSet.find(word)!=wordSet.end()&&dp[i]==true){
                    dp[j] = true;
                }
            }
        }
        return dp[s.size()];
    }
};

85 力扣300 最长递增子序列

b096e8e892a84587933b4c13448e9a78.png

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()<=1)return nums.size();
        vector<int>dp(nums.size(),1);       //dp[i]表示i之前以i结尾的最长递增子序列长度

        int res=0;      //记录最大值,因为不一定最后的是最长的

        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j])dp[i] = max(dp[i],dp[j]+1);
            }
            if(dp[i]>res)res = dp[i];
        }
        return res;
    }
};

86 力扣152 乘积最大子数组

af990fc5cfd0461e9fa81595a9e6df35.png

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return 0;
        } else if(n == 1) {
            return nums[0];
        }
        int p = nums[0];
        int maxP = nums[0];
        int minP = nums[0];
        for(int i = 1; i < n; i++) {
            int t = maxP;
            maxP = max(max(maxP * nums[i], nums[i]), minP *nums[i]);
            minP = min(min(t * nums[i], nums[i]), minP * nums[i]);
            p = max(maxP, p);
        }
        return p;
    }
};

87 力扣416 分割等和子集

e0a71370f8bd4e43ab666e153982049d.png

背包为分半判断

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int count=0;
        for(int i=0;i<nums.size();i++){
            count+=nums[i];
        }
        if(count%2==1){
            return false;
        }
        int target = count/2;

        vector<vector<int>>dp(nums.size(),vector<int>(nums.size(),0));      //dp[j]表示背包空间为j的最大价值是dp[j]

        for(int i=0;i<nums.size();i++){
            for(int j=0;j<=;j++){       //背包的下限得能装下物品空间
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);
            }
        }
        
        if(dp[nums.size()][target]==target){
            return true;
        }else{
            return false;
        }
        
    }
};

——88 力扣32 最长有效括号

6721854196834bc1aeea96e7ce114df9.png

class Solution {
public:
    int longestValidParentheses(string s) {
        int size = s.length();
        vector<int> dp(size, 0);

        int maxVal = 0;
        for(int i = 1; i < size; i++) {
            if (s[i] == ')') {
                if (s[i - 1] == '(') {
                    dp[i] = 2;
                    if (i - 2 >= 0) {
                        dp[i] = dp[i] + dp[i - 2];
                    }
                } else if (dp[i - 1] > 0) {
                    if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
                        dp[i] = dp[i - 1] + 2;
                        if ((i - dp[i - 1] - 2) >= 0) {
                            dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
                        }
                    }
                }
            }
            maxVal = max(maxVal, dp[i]);
        }
        return maxVal;
    }
};

动态多维规划

89 力扣62 不同路径

cbc9e50d818947b8a7b2980b5458777c.png

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>>dp(m,vector<int>(n,0));      //到(i,j)位置有dp[i,j]种方法

        //初始化
        for(int i=0;i<m;i++){   //最左侧只有一种走法
            dp[i][0]=1;
        }
        for(int j=0;j<n;j++){   //最上侧只有一种走法
            dp[0][j]=1;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=dp[i-1][j]+dp[i][j-1];         //动态转移方程
            }
        }
        return dp[m-1][n-1];    //第二行第二列的到达方法有dp[1][1]种
        
    }
};

——90 力扣64 最小路径和

b8adde9d770d45768c87695ce0f11691.png

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if (grid.size() == 0 || grid[0].size() == 0) {
            return 0;
        }
        int rows = grid.size(), columns = grid[0].size();
        auto dp = vector < vector <int> > (rows, vector <int> (columns));
        dp[0][0] = grid[0][0];
        for (int i = 1; i < rows; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        for (int j = 1; j < columns; j++) {
            dp[0][j] = dp[0][j - 1] + grid[0][j];
        }
        for (int i = 1; i < rows; i++) {
            for (int j = 1; j < columns; j++) {
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        return dp[rows - 1][columns - 1];
    }
};

——91 力扣5 最长回文子串

831086c904804368b31b994b99199efb.png

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        if (n < 2) {
            return s;
        }

        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        vector<vector<int>> dp(n, vector<int>(n));
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < n; i++) {
            dp[i][i] = true;
        }
        // 递推开始
        // 先枚举子串长度
        for (int L = 2; L <= n; L++) {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < n; i++) {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= n) {
                    break;
                }

                if (s[i] != s[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substr(begin, maxLen);
    }
};

92 力扣1143 最长公共子序列

4f4a3b9caee04d79a41ae71d84de1b33.png

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        //以下标i-1结尾的text1 和 以下标j-1结尾的text2 的最长公共子序列为dp[i][j]
        vector<vector<int>>dp(text1.size()+1,vector<int>(text2.size()+1,0));

        for(int i=1;i<=text1.size();i++){
            for(int j=1;j<=text2.size();j++){
                if(text1[i-1]==text2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
                
            }
        }
        return dp[text1.size()][text2.size()];
    }
};

93 力扣72 编辑距离

1778bb859e114dc6b7258b6b084051c8.png

class Solution {
public:
    int minDistance(string word1, string word2) {
        //以下标i-1结尾的word1 和 以下标j-1结尾的word2 的最小操作数为dp[i][j]
        vector<vector<int>>dp(word1.size()+1,vector<int>(word2.size()+1));

        for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
        for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
        for(int i=1;i<=word1.size();i++){
            for(int j=1;j<=word2.size();j++){
                if(word1[i-1]==word2[j-1]){
                    dp[i][j] = dp[i-1][j-1];    //相等的情况下什么也不做,向前找
                }else{
                    //增删(增删是相同的,其中一个向前退一步,之后再增加一步操作)换(两个各向前退一步在换)
                    dp[i][j] = min({dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1});
                }
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

技巧

94 力扣136 只出现一次的数字

1ef28c30319d47b88637854d438a7bec.png

整个数组异或操作,使用0对整个数组操作

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (auto e: nums) ret ^= e;
        return ret;
    }
};

95 力扣169 多数元素

338f9080b59d4d56aecdadbe650cf4eb.png

抵消原则: 在一个数组中,如果某个元素的出现次数超过了数组长度的一半,那么这个元素与其他所有元素一一配对,最后仍然会剩下至少一个该元素。 通过“投票”和“抵消”的过程,可以逐步消除不同的元素,最终留下的候选人就是可能的主要元素。 

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int x = 0, votes = 0;
        for (int num : nums){
            if (votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }
        return x;
    }
};

96 力扣75 颜色分类

7b4c3b3cd8a342abaf4b186d63c970c9.png

双指针实现 

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n = nums.size();
        int p0 = 0, p1 = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] == 1) {
                swap(nums[i], nums[p1]);
                ++p1;
            } else if (nums[i] == 0) {
                swap(nums[i], nums[p0]);
                if (p0 < p1) {
                    swap(nums[i], nums[p1]);
                }
                ++p0;
                ++p1;
            }
        }
    }
};

97 力扣31 下一个排列

847897811bec4155862b94125ead910c.png

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int i = nums.size() - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.size() - 1;
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            swap(nums[i], nums[j]);
        }
        reverse(nums.begin() + i + 1, nums.end());
    }
};

98 力扣287 寻找重复数

6031848cf1c8442bb89daa738bd48211.png

当作一个链表来看,数组的下标就是指向元素的指针,把数组的元素也看作指针。 

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        
        int fast = 0, slow = 0;
        while(true){
            fast = nums[nums[fast]];
            slow = nums[slow];
            if(fast == slow)
                break;
        }
        int finder = 0;
        while(true){
            finder = nums[finder];
            slow = nums[slow];
            if(slow == finder)
                break;        
        }
        return slow;
    }
};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值