力扣100题

学习目标:

熟悉hot100中的常见数据结构

unordered_map<int,int> map;	map.find(3);//是查找键为3的元素,如果找到了键为 1 的元素,则 it 将指向该元素的迭代器;如果没有找到,则 it 将等于 umap.end(),表示未找到。
unordered_set<int> set; set.find(3); //是查找值为3的元素。

一、哈希表

最常用的:std::unordered_map 。key无序,key不可重复。
std::multimap ,key有序,key可重复。
哈希表的创建:unordered_map<int,int> Hash; 迭代器unordered_map<int,int>::iterator it; 或者auto it
unordered_map 中元素唯一,即key相同的元素不再进行插入。
unordered_set 常用方法:

.insert /*插入后自动去重*/    .find     .count

添加元素

Hash[1]=3;
Hash.insert<make_pair(1,3)>;
Hash.insert({ {1,3},{2,4} });

查找 ——找不到返回hash.end()

it=Hash.find(1);

清除哈希表

Hash.erase(1);
Hash.clear();

tips:
数组去重常用方法:

1.unordered_set 2.sort之后nums.erase(unique(nums.begin(),nums.end()),nums.end()) ;//去重

学习内容:

2024-2-7:

两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map <int,int> umap;
        // vector<int> result;
        for(int i = 0;i<nums.size() ;i++){
            int temp = target - nums[i];
            auto it = umap.find(temp);
            if( it ==umap.end()){//没找到
                umap[nums[i]] = i;
            }
            else{ //找到了        
                // result[0] = i;
                // result[1] = nums[i];
                return {i,it->second};
            }
        }
    return {};
    }
};
示例 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]

字母易位词

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        /*对每个元素排序,字母易位词排序后一样,作为键,本身作为值*/
        vector<vector<string>> result = {};
        unordered_map<string,vector<string>> umap;
        // for(int i = 0;i<strs.size();i++){

        // }
        for(string each:strs){
            string tmp = each;
            sort(tmp.begin(),tmp.end());
            umap[tmp].push_back(each);
        }
        //把map转为vector
        for(auto it = umap.begin();it!=umap.end();it++)
            result.push_back(it->second);
        return result;
    }
};
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]

2024-2-8:

最长连续序列(秒)
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
    if(nums.size() == 0)    return 0;
    //排序后遍历
    int result = 1;
    // unordered_set<int> uset ;
    vector<int> num = nums;
    sort(num.begin(),num.end());
    num.erase(unique(num.begin(),num.end()),num.end()) ;//去重
   
    int tmp = 1;
    for(int i = 0;i<num.size()-1;i++){
        if(num[i] - num[i+1] == -1){
            tmp++;
        }
        else{
             tmp = 1;
        }
        if(result < tmp ) result = tmp;
    }
    return result;
    }
};
示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

移动0
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        //双指针应用
        int fast = 0,slow = 0; 
        for(  ;fast<nums.size();fast++){
            if(nums[fast] == 0) {   
            }
            else{   //交换
                nums[slow++] = nums[fast];
            }
        }
        for(int i = slow;i<nums.size();i++)
            nums[i] = 0;
    }
};
示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:

输入: nums = [0]
输出: [0]

盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。说明:你不能倾斜容器。
(题解:向内移动短板,面积可能增加,向内移动长板,面积一定减小。)

class Solution {
public:
    int maxArea(vector<int>& height) {
        //双指针向内移动
        int i = 0,j = height.size()-1;
        int res = 0;
        int tmp = 0;
        while(i<j){ //向内移动短板,面积可能增大
            if(height[i]<height[j]) {
                tmp = (j-i)*height[i];
                res = max(res,tmp);
                i++;
            }
            else{
                tmp = (j-i)*height[j];
                res = max(res,tmp);
                j--;
            }
        }
        return res;
    }
};
输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:

输入:height = [1,1]
输出:1

2024-2-13:

三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。(难点在于降重上,很麻烦这个)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if(nums.size()<3) return {};
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        int totlen = nums.size();
        for(int i = 0;i<totlen;i++){
            if(nums[i] > 0 ) return res;
            int L = i+1,R =totlen -1;
            while(L<R){
                int tmp = nums[i]+nums[L]+nums[R];
                if(tmp == 0)   {
                    res.push_back({nums[i],nums[L],nums[R]}); 
                    // break; 不能直接跳出
                    while(L<R && nums[L] == nums[L+1])    L++;
                    while(L<R && nums[R] == nums[R-1])    R--;
                    L++;
                    R--;
                } 
                else if(tmp > 0)    R--;
                else if(tmp < 0)    L++;    
            }
            //对i处理,降重,i一样就跳过一个
            while(i<totlen-1 && nums[i+1] == nums[i])  i++;
        }
        return res;
    }
};

接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。(使用的双指针,超时,下一次使用一个数组记录每列左右最高点位置,用空间换时间)

class Solution {
public:
    int getlocl(int cur,vector<int>& height){
        int max = 0;
        int res = -1;
        for(int i = 0;i<cur;i++){
            if(height[i] >= max) {
                res = i;
                max = height[i];
            }
        }
        return res;
    }
    int getlocr(int cur,vector<int>& height){
        int max = 0;
        int res = -1;
        for(int i = cur;i<height.size();i++){
            if(height[i] >=max) {
                res = i;
                max = height[i];
            }
        }
        return res;
    }
    int trap(vector<int>& height) {
        int len = height.size();
        if(len <3) return 0;
        //解法一:按列求(双指针)
        int sum = 0;
        for(int i = 0;i<len-1;i++ ){
            int cur = i+1;
            int l = getlocl(cur, height);
            int r = getlocr(cur, height);
            int minvalue = min(height[l],height[r]);
            if(height[cur]<minvalue){
                sum+=minvalue-height[cur];
            }
        }
        return sum;
    }

};

2024-2-14:

无重复字符最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
注意unordered_set的使用,不能使用user.erase(uset.begin(),uset.find(s[i])); 最多只能使用uset.erase(uset.begin(),uset.end());

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        unordered_set<char> uset;
        int left = 0;
        for(int i = 0;i<s.length();i++){
            auto it = uset.find(s[i]);
            if(it!=uset.end()){ //找到了相同元素
                // uset.erase(uset.begin(),it);   //擦除此元素及之前位置的元素
                // uset.insert(s[i]);
                //上述代码不对是因为erase方法不能这么使用
                while(s[i]!=s[left] ){
                    uset.erase(s[left++]);
                }
                left++;
            }
            else{ //没找到
                uset.insert(s[i]);
            }
            if(uset.size()>res){
                res = uset.size();
            }
        }
        return res;
    }
};
    // //使用队列   注:队列不能访问任意位置的元素,pwwkew,pww就不能pass
    // queue<char> q;
    // int res = 0;
    // for(int i = 0;i<s.length();i++){
    //     if(s[i] != q.front()) //没找到
    //     {
    //         q.push(s[i]); // 添加元素
    //     }
    //     else{ //移除首元素
    //         q.pop();
    //     }

    //     if(q.size()>res){
    //          res = q.size();
    //     }
    // }
    // return res;
    示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        vector<int> sv(26);
        vector<int> pv(26);
        int plen = p.size();
        int slen = s.size();
        if(slen < plen) return vector<int>();
        for(int i = 0;i<plen;i++){
            sv[s[i]-'a']++;
            pv[p[i]-'a']++;
        }
        if(sv == pv) {
            res.emplace_back(0);
        }
        for(int i = 0;i<slen-plen;++i){
            sv[s[i]-'a']--;
            sv[s[i+plen]-'a']++;
            if(sv == pv) {
                res.emplace_back(i+1);
            }
        }
        return res;
    }
};
示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
 示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

2024-2-15:

和为k的子数组(秒)
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int res = 0;
        int tmp = 0;
        // sort(nums.begin(),nums.end()); //不能改变数组,改了结果就变了,因为“子数组是数组中元素的连续非空序列。
        //滑动一个不定长数组
        for(int i = 0;i<nums.size();i++){
            tmp = nums[i];
            if(tmp == k) res++;
            int j = i+1;
            while(j<nums.size()){
                tmp+=nums[j++];
                if(tmp == k) res++;
            }
        }
        return res;
    }
};
示例 1:

输入:nums = [1,1,1], k = 2
输出:2
示例 2:

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

滑动窗口最大值(超时)
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        //暴力解法,超时
        vector<int> res;
        for(int i = 0;i<nums.size()-k+1;i++){
            int tmp = nums[i];
            int j = 1;
            while(j<k){
                tmp = max(tmp,nums[i+j]);
                j++;
            }
            res.push_back(tmp);
        }
        return res;
    }
};
示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
示例 2:

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

2024-2-16:

最小覆盖字串(超时)
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

class Solution {
public:
    bool check(unordered_map<char,int> smap,unordered_map<char,int>tmap){ //判断两个map是否一样
        for(auto &tmp:smap){
            if(tmap[tmp.first]<tmp.second){ //不一样
                return false;
            }
        }
        return true;
    }
    string minWindow(string s, string t) {
        unordered_map<char,int> tmap,smap;
        int ans = -1;
        for(char tmp:t){
            tmap[tmp]++;
        }
        int r = -1 ,l = 0;
        int len = s.size()+1; //防止只有一个元素的特殊情况
        int totlen = s.size();
        while(r<totlen){
            if(tmap.find(s[++r])!=tmap.end()){ //先判断完一轮
                smap[s[r]]++;
            }
            while((check(tmap,smap) ==true)&& l<=r){ // smap与tmap一样时,判断长度与更新左边界
                if(r-l+1<len) {
                    len = r-l+1;
                    ans = l;
                }
                if(tmap.find(s[l])!=tmap.end()){
                    smap[s[l]]--;
                }
                l++;
            }
        }
        if(ans == -1) return string();
        else    return s.substr(ans,len);
    }
};

2024-2-17:

最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        //动态规划
        int res = nums[0];
        for(int i = 0;i<nums.size()-1;i++){
            if(nums[i]>0){
                nums[i+1] = nums[i+1]+nums[i];
            }
            res = max(nums[i+1],res);
        }
        return res;
    }
};
示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23

合并区间
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> res;
        sort(intervals.begin(),intervals.end());
        int l = intervals[0][0],r = intervals[0][1];
        for(int i = 1 ; i<intervals.size();i++){
            if(intervals[i][0]<= r){ 
                r = max(intervals[i][1],r);
            }   
            else{
                res.push_back({l,r});
                l = intervals[i][0];
                r = intervals[i][1];
            } 
        }
        res.push_back({l,r});
        return res;
    }
};
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3][2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4][4,5] 可被视为重叠区间。

轮转数组(秒)
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。

示例 1: 输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2: 输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步:[99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        vector<int> res = nums;
        int totlen = nums.size();
        k = k%totlen;
        int j = -1;
        for(int i = 0;i<totlen;i++){
            int tmp = totlen - k + i;
            if(tmp >= totlen ) tmp = ++j;

            if(tmp < totlen && tmp>=0)  
                res[i] = nums[tmp];
            
        }
        nums = res;
    }
};

2024-2-19:

除自身以外的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

示例 1:输入: nums = [1,2,3,4] 输出: [24,12,8,6]
示例 2: 输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        //空间换时间典型案例
        int totlen = nums.size();
        int tmp = 1;
        vector<int> ans(totlen,1);
        //计算下三角
        for(int i = 1;i<totlen;i++){
            ans[i] = ans[i-1]*nums[i-1];
        }
        for(int i = totlen-2;i>=0;i--){
            tmp = tmp*nums[i+1];
            ans[i] = ans[i]*tmp;
        }
        return ans;
    }
};
        //暴力(超时)
        // vector<int> ans;
        // int totlen = nums.size();
        // int tmp = 1;
        // for(int i = 0;i<totlen;i++){
        //     int j = 0;
        //     while(j<totlen){
        //         if(i==j)  continue;
        //         tmp *= nums[j++];
        //     }
        //     ans.push_back(tmp);
        // }
        // return ans;

2024-2-20:

矩阵置零(秒)
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        vector<vector<int>> re = matrix;

        for(int i = 0 ;i<matrix.size();i++){
            for(int j = 0;j<matrix[0].size();j++){
                if(re[i][j]==0) {
                    for(int a = 0;a<matrix.size();a++) matrix[a][j] = 0;
                    for(int b = 0;b<matrix[0].size();b++) matrix[i][b] = 0;
                }
            }
        }
    }
};

螺旋矩阵(重要,参考别人的更改边界的思想)
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector <int> res;
        int left = 0;
        int right = matrix[0].size()-1;
        int up = 0;
        int down = matrix.size()-1;
        for(;;){
            for(int i = left;i<=right;i++)   res.push_back(matrix[up][i]);
            if(++up >down) break;
            for(int i = up;i<=down;i++)   res.push_back(matrix[i][right]);
            if(--right <left) break;
            for(int i = right;i>=left;i--)   res.push_back(matrix[down][i]);
            if(--down <up) break;
            for(int i = down;i>=up;i--)   res.push_back(matrix[i][left]);
            if(++left >right) break;
        }
        return res;
    }
};

旋转图像(秒,直觉上来说对应坐标存在一个映射,很容易分析出来数学关系)
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        //就是一个数学关系,仿射变换
        //x与y互换后,一个不变,一个用n-1-x
        vector<vector<int>> re = matrix;
        int n = matrix.size();
        for(int i = 0;i<n;i++){
            for(int j = 0;j<n;j++){
                matrix[i][j] = re[n-1-j][i];
            }
        }
    }
};

搜索二维矩阵Ⅱ(学习二分查找与为什么引用可以减小内存使用)
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

碰到的问题:*** for(auto const& row:matrix)*** 这种循环使用了引用 &,即每次循环迭代都会访问 matrix 中的元素的引用,而不是进行值的拷贝。如果不加引用会超出内存限制。
这种方式避免了在循环中创建对象的副本,因此在循环内部使用的是原始对象的引用,节省了内存和时间。
因此,对于大型的数据结构或对象,使用引用可以提高程序的效率,并减少内存开销。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int i = 0,j = 0;
        #if 0
        //暴力搜索
        for(int i = 0;i<matrix.size();i++){
            for(int j = 0;j<matrix[0].size();j++){
                if(matrix[i][j] == target) return  true;
            }
        }
        #else
        for(auto const& row:matrix){
            auto it = lower_bound(row.begin(),row.end(),target); //由于每行都是排好序的,使用二分查找target
            if(it != row.end() && *it == target) return true;
        }
        #endif

        return false;
    }
};

相交链表(秒)
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。

/**
 * 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) {
        //先保持长度一致,再往后遍历
        ListNode* tmp = headA;
        int lena = 0,lenb = 0;
        while(tmp!=NULL){
            lena++;
            tmp = tmp->next;
        }
        tmp = headB;
        while(tmp!=NULL){
            lenb++;
            tmp = tmp->next;
        }
        int dif = abs(lena-lenb);
        ListNode* tmpS ;//较短的链表
        if(lena>lenb) {tmp = headA;tmpS = headB;}
        else          {tmp = headB;tmpS = headA;}
        for(int i = 0;i<dif;i++){
            tmp = tmp->next;
        }
        do{ //do while是为了解决只有一个元素直接跳过的情况
            if(tmp == tmpS) return tmp;
            tmp = tmp->next;    tmpS = tmpS->next;
        }while(tmp !=NULL);

        return NULL;
    }
};

2024-2-21:

反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *pre = nullptr;
        ListNode *tmp ;
        ListNode *cur = head ;
        while(cur!=nullptr)
        {
            tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        } 
        return pre;
    }
};

回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

输入:head = [1,2,2,1] 输出:true

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        //引入一个数组
        vector <int> val;
        ListNode* cur = head;
        int k = 0;
        while(cur!=NULL){
            val.push_back(cur->val);
            cur = cur->next;
        }
        int len = val.size();
#if 0               
        for(int i = 0;i<len/2;i++){
            if(val[i] != val[len-1-i]) return false;
        }
#else
        vector <int> val2 = val;
        reverse(val.begin(),val.end());
        return val == val2;
#endif
        return true;
    }
};

环形链表
给你一个链表的头节点 head ,判断链表中是否有环。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        //双指针
        ListNode* fast = head;
        ListNode* slow = head;
        if(head == NULL || head->next ==NULL) return false;
        while(fast!=NULL){
            if(fast->next!=NULL)    fast = fast->next;
            else return false;
            if(fast->next!=NULL)    fast = fast->next;
            else return false;
            if(slow->next!=NULL)    slow = slow->next;
            else return false;
            if(fast == slow)    return true;
        }
        return false;
    }
};

环形链表Ⅱ
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。

注意:第一相遇不一定在哪,第二次一定是在相交节点相遇

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head,*slow = head;
        //第一次相遇不一定在哪
        while(1){
            if(fast!=NULL)  fast = fast->next;
            else return NULL;
            if(fast!=NULL) fast = fast->next;
            else return NULL;
            slow = slow->next;
            if(slow == fast) break;  
        }
        fast = head;
        while(fast !=slow){ //第二次相遇一定是在相交节点上
            fast = fast->next;
            slow = slow->next;
        }
        return fast;
    }
};

合并两个有序链表(不知道为什么不设置虚拟头节点时,在最后一个元素相等的情况下运行会找不到指针)
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode *list;
        ListNode *pre = new ListNode(-1);
        ListNode *tmp = pre;
        //设置了虚拟头节点可以运行,没有虚拟头节点会在最后一个元素相同时报找不到指针的错,怀疑是likou的bug
        // if(list1 !=nullptr && list2!=nullptr){
        //     if(list1->val <= list2->val)  {list = list1;list1 = list1->next;}
        //     else {list = list2; list2 = list2->next;}
        //     tmp = list;
        // }
        while(list1!=nullptr && list2!=nullptr){
            if(list1->val <= list2->val )     {tmp->next = list1; list1 = list1->next;}
            else {tmp->next = list2;  list2 = list2->next;}
            tmp = tmp->next;
        }
        if(list1 ==nullptr) {tmp->next = list2; } 
        else {tmp->next = list1; }       

        return pre->next;
    }
};

两数相加(秒)
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int flag = 0;//进位标志
        ListNode* prehead = new ListNode(-1);
        ListNode* p = prehead; 
        while(l1!= nullptr && l2!= nullptr){//前n共同位相加
            int tmp = l1->val+l2->val+flag;
            ListNode* list;
            if(tmp>9) {flag = 1;  list = new ListNode(tmp%10);}
            else  {flag = 0;      list = new ListNode(tmp);} 
            
            p->next = list;
            p = p->next;
            l1 = l1->next;
            l2 = l2->next;
        }
        /*处理多余的部分*/
        while(l1!= nullptr) {
            int tmp = l1->val+flag;   
            ListNode* list;
            if(tmp>9) {flag = 1;  list = new ListNode(tmp%10);}
            else  {flag = 0;      list = new ListNode(tmp);} 

            p->next = list;
            p = p->next;
            l1 = l1->next;
        }
        while(l2!= nullptr) {
            int tmp = l2->val+flag;   
             ListNode* list;
            if(tmp>9) {flag = 1;  list = new ListNode(tmp%10);}
            else  {flag = 0;      list = new ListNode(tmp);} 
            p->next = list;
            p = p->next;
            l2 = l2->next;
        }
        if(flag == 1) { //处理最后的进位
            ListNode* list = new ListNode(1);
            p->next = list;
        }
        return prehead->next;
    }
};
        //不是此题的目的,unsigned long int存储不了这么大的数,除非进行拆分
        // unsigned long int a = 0,b = 0;
        // int i = 0,j = 0;
        // while(l1!= nullptr){
        //     int tmp = l1->val;
        //     a += tmp*pow(10,i++);
        //     l1 = l1->next;
        // }
        // while(l2!= nullptr){
        //     int tmp = l2->val;
        //     b += tmp*pow(10,j++);
        //     l2 = l2->next;
        // }
        // unsigned long int sum = a+b;
        // ListNode* prehead = new ListNode(-1);
        // ListNode* p = prehead; 
        // if(sum == 0) return new ListNode(0);
        // while(sum!=0){
        //     int tmp = sum%10;
        //     sum /=10;
        //     ListNode* list = new ListNode(tmp);
        //     p->next = list;
        //     p = p->next;
        // }
        // return prehead->next;

删除链表中的倒数第N个结点(麻烦的地方在于删除首位结点的处理)
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *myhead = new ListNode(0);
        myhead->next = head;
        ListNode *cur = head;
        int cnt = 0;
        while(cur !=nullptr){
            cur = cur->next;
            cnt++;
        }
        cur = head;
        int tmp = cnt - n;
        if(tmp > 0){ //小于等于0如果进入下面的循环,--tmp,tmp始终为负数
            while((--tmp )&&(cur!=nullptr)){
             cur = cur->next;
            }
        }
        else if(tmp ==0) {return head->next;} //删除第一个元素

        if(cur == nullptr){ return nullptr;}
        if(cur->next == nullptr){ return nullptr;}
        if(cur->next->next ==nullptr &&(n ==1)){cur->next = nullptr;} //删除最后一个元素
        else{
            ListNode *temp = cur->next->next;
            cur->next = temp;
            // delete temp;
            // temp = nullptr;
        }
        return myhead->next;
    }
};

2024-2-22:

两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

输入:head = [1,2,3,4] 输出:[2,1,4,3]

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *myhead = new ListNode(0);
        myhead->next = head;
        ListNode *cur = myhead;
       
        while(cur->next != nullptr && cur->next->next != nullptr){
            ListNode *tmp= cur->next;
            ListNode *tmp1=cur->next->next->next;
          
            cur->next = cur->next->next;
            cur->next->next = tmp;
            cur->next->next->next = tmp1;
           
            cur = cur->next->next;
           
        }
        return myhead->next;

    };

随机链表的复制(注意哈希表的用法)
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

class Solution {
public:
    Node* copyRandomList(Node* head) {
        unordered_map<Node*,Node*> map;
        Node *cur = head;
        while(cur!=nullptr){
            map[cur] = new Node(cur->val);
            cur = cur->next;
        }
        cur = head;
        //构建新链表的next和random
        while(cur!=nullptr){
            map[cur]->next = map[cur->next];
            map[cur]->random = map[cur->random];
            cur = cur->next;
        }
        return map[head];
    }
};

排序链表(秒)(有序,有重复值,使用multimap存储链表,打散链表,重新组织链表!!!碾压官解)
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

输入:head = [4,2,1,3] 输出:[1,2,3,4]

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(head == nullptr) return nullptr;
        multimap <int,ListNode*> map;
        ListNode * cur = head;
        ListNode * pre = new ListNode(-1);
        pre->next = head;
        while(cur!=nullptr){
            // map[cur->val] = cur;
            map.insert({cur->val,cur});
            cur = cur->next;
        }
        cur = head;
        /*断开各指针之间的联系,分散开避免后续造成混乱*/
        for(auto it = map.begin();it!=map.end();it++){
            it->second->next = nullptr; //first-键  second-value
        }
        cur = pre;
        for(auto it = map.begin();it!=map.end();it++){
            cur->next = it->second; //first-键  second-value
            cur = cur->next;
        }
        return pre->next;
    }
};
//         if(!head) return nullptr;
//         multiset<int> set; //可能会有重复值,且要有序
//         ListNode* ptr = head;
//         while(ptr)
//         {
//             set.insert(ptr->val);
//             ptr = ptr->next;
//         }
//         ptr = head;
// #if 0
//         //取巧方法,只是改了值没有动链表。事实上使用multiset已经大大简化了此题。
//         for(auto it=set.begin(); it!=set.end(); it++) {
// 		    ptr->val = *it;
//             ptr = ptr->next;
// 	    }
// #else
//         //变更指针
//         for(auto it=set.begin(); it!=set.end(); it++) {
// 		    ptr->val = *it;
//             ptr = ptr->next;
// 	    }
// #endif
//         return head;

合并k个升序链表(秒)(和上面的题一样,记录链表,打散链表,组织链表)*
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [
1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        multimap <int ,ListNode*> map;
        ListNode *tmp ;
        ListNode *pre = new ListNode(-1) ;
        
        for(int i = 0;i<lists.size();i++){
            tmp = lists[i];
            while(tmp!=nullptr){
                map.insert({tmp->val,tmp});
                tmp = tmp->next;
            }
        }
        for(auto it = map.begin();it!=map.end();it++){
            it->second->next = nullptr;
        }
        if(map.empty() ==true)  return nullptr; //数组空和链表空
        //拿到头节点
        auto iter = map.begin();
        ListNode *head = iter->second ;
        pre->next = head;
        tmp = pre;
        for(auto it = map.begin();it!=map.end();it++){
            tmp->next = it->second;
            tmp = tmp->next;
        }
        return pre->next;
    }
};

2024-2-23:

二叉树的中序遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

输入:root = [1,null,2,3] 输出:[1,3,2]

class Solution {
public:
    void tra(TreeNode*cur ,vector<int>& res){ //左中右
        if(cur == nullptr)  return ;
        tra(cur->left,res);
        res.push_back(cur->val);
        tra(cur->right,res);
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        TreeNode* cur = root;
        tra(cur,res);
        return res;
    }
};

二叉树的最大深度(层序遍历)
给定一个二叉树 root ,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

输入:root = [3,9,20,null,null,15,7] 输出:3

class Solution {
public:
    int maxDepth(TreeNode* root) {
        int dep = 0;
        queue<TreeNode*> que;
        if(root !=nullptr)  que.push(root);
        while(!que.empty()){
            int size = que.size();
            // vector <int> vec;
            for(int i = 0;i<size;i++){
                TreeNode* cur = que.front();
                que.pop();
                // vec.push_back(cur->val);
                if(cur->left)   que.push(cur->left);
                if(cur->right)  que.push(cur->right);
            }
            dep++;
        }
        return dep;
    }
};

翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> q;
        if(root!=nullptr) q.push(root);
        while(!q.empty()){
            int size = q.size();
            for(int i = 0;i<size;i++){
                TreeNode* cur = q.front();
                q.pop();
                // if(cur->left!=nullptr && cur->right!=nullptr) swap(cur->left->val,cur->right->val);
                swap(cur->left,cur->right); //链表交换
                if(cur->left)   q.push(cur->left);
                if(cur->right)    q.push(cur->right);
            }
        }
        return root;
    }
};

二叉树的直径
给你一棵二叉树的根节点,返回该树的 直径 。二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。两节点之间路径的 长度 由它们之间边数表示。

输入:root = [1,2,3,4,5] 输出:3 解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。

class Solution {
public:
    int ans = 0;
    int dfs(TreeNode *cur){
        if(cur == nullptr) return 0;
        int l = dfs(cur->left);
        int r = dfs(cur->right);
        ans = max(ans,l+r);
        return max(l,r)+1; //最后一层往上返回的时候路径得增加1
    }
    int diameterOfBinaryTree(TreeNode* root) {
        dfs(root);
        return ans;
    }
};

二叉树 的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]]

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        queue <TreeNode*> q;
        if(root!=nullptr) q.push(root);
        while(!q.empty()){ //队列非空进循环
            int size = q.size();
            vector<int> vec;
            for(int i = 0;i<size;i++){
                TreeNode* node = q.front();
                q.pop();
                vec.push_back(node->val);
                if(node->left)  q.push(node->left);
                if(node->right) q.push(node->right);
            }
            res.push_back(vec);
        }
        return res;
    }
};

验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。

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

class Solution {
public:
    bool tra(TreeNode* cur,long low,long big){
        if(cur == nullptr)  return true;
        if(cur->val<= low || cur->val >= big) return false;
        bool tmp1 = tra(cur->left,low,cur->val);   
        bool tmp2 = tra(cur->right,cur->val,big);
        return tmp1&&tmp2;
    }
    bool isValidBST(TreeNode* root) {
        return tra(root,LONG_MIN,LONG_MAX);
    }
};

2024-2-25:

有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。

输入:s = “()” 输出:true 示例 2:
输入:s = “()[]{}” 输出:true 示例 3:
输入:s = “(]” 输出:false

class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        if(s.size()%2!=0)   return false;
        for(char t:s){
            if(t =='(') st.push(')');
            else if(t == '[')   st.push(']');
            else if(t =='{')    st.push('}');
            else if(st.empty()||st.top()!=t)    return false;
            else st.pop();
        }
        if(!st.empty()) return false;
        return true;
    }
};

2024-2-26:

最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int getMin() 获取堆栈中的最小元素。

示例 1:
输入: [“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]
输出: [null,null,null,null,-3,null,0,-2]
解释: MinStack minStack = new MinStack(); minStack.push(-2);
minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3.
minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin();
–> 返回 -2.

class MinStack {
public:
    stack<int> st;
    stack<int> st1;//存储每个元素对应的最小元素
    int tmp = INT_MAX;

    MinStack() {
        st1.push(tmp);
    }
    
    void push(int val) {
        st.push(val);
        if(val<st1.top())    st1.push(val);
        else st1.push(st1.top());
    }
    
    void pop() {
        st.pop();
        st1.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int getMin() {
        return st1.top();
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1: 输入: temperatures = [73,74,75,71,69,72,76,73] 输出:
[1,1,4,2,1,1,0,0]

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> res(temperatures.size(),0); //数组必须初始化吗
        stack <int> st;
        if(temperatures.size()==0) return vector<int>();
        st.push(0);
        for(int i = 1;i<temperatures.size();i++){
            if(temperatures[i]<temperatures[st.top()]) { //当前元素小于栈顶元素
                st.push(i);
            }
            else if(temperatures[i] == temperatures[st.top()]){
                st.push(i);
            }
            else{   //当前元素>栈顶元素 ,需要判断历史元素
                while(st.empty()==false && temperatures[i]>temperatures[st.top()]){
                    res[st.top()] = i - st.top(); st.pop(); 
                }
                st.push(i);
            }
        }
        return res;
    }
};

2024-2-28:

对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。

输入:root = [1,2,2,3,4,4,3] 输出:true

class Solution {
public:
    bool tre(TreeNode *l,TreeNode *r){
        if(l == nullptr && r ==nullptr) return true;
        else if(!l || !r )   return false; //一个为null时
        if(l->val == r->val) {
            return tre(l->left,r->right)&&tre(l->right,r->left);
        }
        return false;
    }
    bool isSymmetric(TreeNode* root) {
        if(root ==nullptr) return false;
        
        return tre(root,root);;
    }
};

二叉搜索树中第k小的元素
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

输入:root = [3,1,4,null,2], k = 1 输出:1

class Solution {
public:
    vector<int>res;
    void tre(TreeNode *cur){
        if(cur == nullptr) return ;
        res.push_back(cur->val);
        tre(cur->left);
        tre(cur->right);
    } 
    int kthSmallest(TreeNode* root, int k) {
        //遍历后排序
        tre(root);
        sort(res.begin(),res.end());
        return res[k-1];
    }
};

2024-2-29:

二叉树的右视图(秒)
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

输入: [1,2,3,null,5,null,4] 输出: [1,3,4]

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        //层序遍历返回最右侧的节点值
        vector<int> res;
        if(root == nullptr) return vector<int>();
        queue <TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int size = que.size();
            for(int i = 0;i<size;i++){
                TreeNode *tmp = que.front();
                que.pop();
                if(i == size-1) res.push_back(tmp->val);
                if(tmp->left)   que.push(tmp->left);
                if(tmp->right)  que.push(tmp->right);
           }
        }
        return res;
    }
};

将二叉树转换为链表(秒,依然是我们的先进思想)
给你二叉树的根结点 root ,请你将它展开为一个单链表:展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同。

输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6]

class Solution {
public:
    queue <TreeNode*> que;
    void  tre(TreeNode *cur){
        if(cur == nullptr) return;
        que.push(cur);
        tre(cur->left);
        tre(cur->right);
    }
    void flatten(TreeNode* root) {
        tre(root);
        while(!que.empty()){
            TreeNode* tmp = que.front();
            que.pop();
            tmp->left =nullptr;
            if(!que.empty()) {
                TreeNode* next = que.front(); //取出来但不放回去
                tmp->right = next;
            }
            else break;
        }
        return ;
    }
};

2024-3-1:

只出现一次的数字
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

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

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int,int> umap;
        for(int tmp:nums){
            umap[tmp]++;
        }
        // auto it = umap.find(1);
       
        for( auto it = umap.begin();it!=umap.end();it++){
            if(it->second == 1)
                return it->first;
        }
        return -1;
    }
};

路径总和
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 输出:3 解释:和等于8 的路径有 3 条,如图所示。

class Solution {
public:
    int res = 0;
    void tre(TreeNode* cur,int target,long int nowval){
        if(cur == nullptr) return;
        nowval+=cur->val;
        if(nowval == target) {res++; /*nowval=0;*/}
        tre(cur->left,target,nowval);
        tre(cur->right,target,nowval);
        return;
    }
    int pathSum(TreeNode* root, int targetSum) {
        //递归
        if(root == nullptr) return 0;
        TreeNode* cur = root;
        tre(cur,targetSum,0);
        pathSum(cur->left,targetSum);
        pathSum(cur->right,targetSum);
        return res;
    }
};

二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 输出:3 解释:节点 5 和节点1 的最近公共祖先是节点 3 。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL) return NULL;
        if(p == root || q == root) return root;

        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        TreeNode* right = lowestCommonAncestor(root->right,p,q);
        if(left == NULL) return right;
        if(right== NULL) return left;
        return root;
    }
};

2024-3-3:

从前序与中序序列中构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出:[3,9,20,null,null,15,7]

class Solution {
public:
    unordered_map<int,int> index;
    TreeNode* tre(vector<int>& preorder, vector<int>& inorder,int pl,int pr,int il,int ir){
        if(pl>pr) return nullptr;
        int proot = pl;
        int inroot = index[preorder[proot]];
        TreeNode* root = new TreeNode(preorder[proot]);
        int sub = inroot - il;
        root->left = tre(preorder,inorder,pl+1,pl+sub,il,inroot-1);
        root->right = tre(preorder,inorder,pl+sub+1,pr,inroot+1,ir);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size() == 0)    return nullptr;    
        for(int i = 0;i<preorder.size();i++){
            index[inorder[i]] = i;
        }
        return tre(preorder,inorder,0,preorder.size()-1,0,preorder.size()-1);    
    }
};

二叉树中的最大路径和
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root ,返回其 最大路径和 。

输入:root = [1,2,3] 输出:6 解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

class Solution {
public:
    int maxval = INT_MIN;
    int tre(TreeNode* root){
        if(root == nullptr) return 0;
        int left = max(tre(root->left),0);
        int right = max(tre(root->right),0);
        int newval = root->val+left+right;
        maxval = max(maxval,newval);
        return root->val+max(left,right);
    }
    int maxPathSum(TreeNode* root) {
         tre(root);
         return maxval;
    }
};

2024-3-5:

电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:输入:digits = "23"输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]

class Solution {
public:
    vector<string> res;
    void backtrack(string& digits,int index,string *map,string s){
        if(index == digits.size()){
            res.push_back(s);
            return ;
        }
        int digit = digits[index]-'0';
        string tmp = map[digit];
        for(char m:tmp){
            s.push_back(m);
            backtrack(digits,index+1,map,s);
            s.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        string map[10] = {
            "","",
            "abc",
            "def",
            "ghi",
            "jkl",
            "mno",
            "pqrs",
            "tuv",
            "wxyz"
        };
        string s;
        if(digits.size()==0) return vector<string>();
        backtrack( digits,0 , map,s);
        return res;
    }
};

组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:输入:candidates = [2,3,6,7], target = 7输出:[[2,2,3],[7]]解释:2 和 3
可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。仅有这两种组合。

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

2024-3-6:

括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

输入:n = 3 输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]

class Solution {
public:
    vector<string> res;
    void backtrack(int n,int l,int r,string& s){
        if(l==r && l==n){
            res.push_back(s);   return ;
        }
        if(l<r) return ;
        if(l<n){
            s.push_back('(');
            backtrack(n,l+1,r,s);
            s.pop_back();
        }
        if(l>r){
            s.push_back(')');
            backtrack(n,l,r+1,s);
            s.pop_back();            
        }
    }
    vector<string> generateParenthesis(int n) {
        string s;
        backtrack(n,0,0,s);
        return res;
    }
};

单词搜索(超时)
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]],
word = “ABCCED” 输出:true

// class Solution {
// public:
//     bool flag = false;
    
//     void backtrack(vector<vector<char>>& board, string word,string& s,int startindex,int m,int n,int M,int N,vector<vector<int>> subboard,int k){
//         if(s == word){
//             flag = true;    return ;
//         }
//         if(k++ >word.length()) return;
//         if(m>=M || m<0 || n>=N || n<0 ||board[m][n] != word[startindex]) return ;
//         if(subboard[m][n] == 1) return;
//         if(board[m][n] == word[startindex] ){//有相等的就回溯
//             s.push_back(word[startindex]);
//             subboard[m][n] = 1;
//             backtrack(board,word,s,startindex+1,m+1,n,M,N,subboard,k);
//             subboard[m][n] = 0;
//             s.pop_back();
//             s.push_back(word[startindex]);
//             subboard[m][n] = 1;
//             backtrack(board,word,s,startindex+1,m-1,n,M,N,subboard,k);
//             subboard[m][n] = 0;
//             s.pop_back();
//             s.push_back(word[startindex]);
//             subboard[m][n] = 1;
//             backtrack(board,word,s,startindex+1,m,n+1,M,N,subboard,k);
//             subboard[m][n] = 0;
//             s.pop_back(); 
//             s.push_back(word[startindex]);
//             subboard[m][n] = 1;
//             backtrack(board,word,s,startindex+1,m,n-1,M,N,subboard,k);
//             subboard[m][n] = 0;
//             s.pop_back(); 
//         }
        
//     }
//     bool exist(vector<vector<char>>& board, string word) {
//         int M = board.size();
//         int N = board[0].size();
//         vector<vector<int>> subboard(M,vector<int>(N));
//         string s;
//         for(int i = 0;i<M;i++){
//             for(int j = 0;j<N;j++){
//                 if(board[i][j] == word[0]){
//                     backtrack(board,word,s,0,i,j,M,N,subboard,0);
//                 }
//             }
//         }
//         return flag;
//     }
// };
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;
    }
};


分割回文字符串
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
示例 1:

输入:s = “aab” 输出:[[“a”,“a”,“b”],[“aa”,“b”]]

class Solution {
public:
    vector<vector<string>> res;
    vector<string> an;
    bool isHui(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;
    }
    void backtrack(string s,int startindex){
        if(startindex>=s.size()) {
            res.push_back(an);  return ;
        }
        for(int i = startindex;i<s.size();i++){
            if(isHui(s,startindex,i)){
                string tmp = s.substr(startindex,i-startindex+1);
                an.push_back(tmp);
            }else continue;
            backtrack(s,i+1);
            an.pop_back();
        }
    }
    vector<vector<string>> partition(string s) {
        backtrack( s,0);
        return res;
    }
};

爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶
class Solution {
public:
    // int dp(int n ){
    //     if(n ==1)   return 1;
    //     if(n == 2)  return 2;
    //     if(n == 3)  return 3;
    //     return dp(n-1)+dp(n-2);
    // }
    int climbStairs(int n) {
        // return dp(n);
        int t1 =2,t2 =3,t3 = 1;
        if(n == 3) return 3;
        if(n == 2) return 2;
        for(int i =4;i<=n;i++){
            t3 = t1+t2;
            t1 = t2;
            t2 = t3;
        }
        return t3;
    }
};

反转链表
反转链表

学习产出:

提示:这里统计学习计划的总量

例如:

  • 技术笔记 2 遍
  • CSDN 技术博客 3 篇
  • 习的 vlog 视频 1 个
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_45281309

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值