LeetCode hot 100(C++版本)

LeetCode hot 100(C++版本)

哈希

第一题:两数之和

题干 :给定一个整数数组 nums和一个整数目标值 target,请你在该数组中找出和为目标值 target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。你可以按任意顺序返回答案。

示例

  • 示例 1:输入:nums = [2,7,11,15]target = 9;输出:[0,1](解释:nums[0] + nums[1] == 9)。
  • 示例 2:输入:nums = [3,2,4]target = 6;输出:[1,2]
  • 示例 3:输入:nums = [3,3]target = 6;输出:[0,1]
C++ 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> numMap;//存储{数组:下标}
        for(int i=0;i<nums.size();i++)
        {
            int complement=target-nums[i];
            if(numMap.find(complement)!=numMap.end())
            {
                return {numMap[complement],i};
            }
            numMap[nums[i]]=i;//插入/更新操作,将数值​ nums[i]作为键,下标​ i作为值,存入哈希表
        }
        return {};//为了保证有返回值,不会执行
    }
};

第二题:最长连续序列

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

示例

  • 示例 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
  • 示例 3:输入:nums = [1,0,1,2];输出:3
C++ 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> numSet(nums.begin(),nums.end());
        int maxLength=0;
        for(int num:numSet)
        {
            // 只有当前数字是序列起点时才处理
            if(numSet.find(num-1)==numSet.end())
            {
                int currentNum=num;
                int currentLength=1;
                // 向后延伸连续序列
                while(numSet.find(currentNum+1)!=numSet.end())
                {
                    currentNum++;
                    currentLength++;
                }
                maxLength=max(maxLength,currentLength);
            }
        }
        return maxLength;
    }
};

双指针

第三题: 移动零

题干 :给定数组 nums,将所有 0移到数组末尾,保持非零元素相对顺序,原地操作(不复制数组)。

示例

  • 输入:nums = [0,1,0,3,12];输出:[1,3,12,0,0]

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

    C++ 复制代码
    class Solution {
    public:
        void moveZeroes(vector<int>& nums) {
            for(int cur=0,dest=-1;cur<nums.size();cur++)
                if(nums[cur])
                    swap(nums[++dest],nums[cur]);
        }
    };

第四题:盛最多水的容器

题干 :给定长度为 n的整数数组 height(表示 n条垂线的高度),选两条线与 x轴构成容器,求最大容水量(不能倾斜)。

示例

  • 输入:height = [1,8,6,2,5,4,8,3,7];输出:49(两线高度 87,宽度 7,面积 7×7=49)。
  • 输入:height = [1,1];输出:1(两线高度 1,宽度 1,面积 1×1=1)。
C++ 复制代码
class Solution {
public:
    int maxArea(vector<int>& height) {
        int left=0,right=height.size()-1,ret=0;
        while(left<right)
        {
            int v=min(height[left],height[right])*(right-left);
            ret=max(ret,v);
            if(height[left]<height[right])left++;
            else right--;
        }
        return ret;
    }
};

第五题: 三数之和

题干 :给定整数数组 nums,找出所有和为 0且不重复的三元组 [nums[i], nums[j], nums[k]](满足 i≠j≠k),返回所有符合条件的三元组。

示例

  • 输入:nums = [-1,0,1,2,-1,-4];输出:[[-1,-1,2], [-1,0,1]]
  • 输入:nums = [0,1,1];输出:[](无和为 0的三元组)。
  • 输入:nums = [0,0,0];输出:[[0,0,0]]
C++ 复制代码
class Solution
 {
 public:
 vector<vector<int>> threeSum(vector<int>& nums) 
 {
    vector<vector<int>> ret;

    // 1. 排序
    sort(nums.begin(), nums.end());

    // 2. 利⽤双指针解决问题
    int n = nums.size();
    for(int i = 0; i < n; ) // 固定数 a
    {
        if(nums[i] > 0) break; // ⼩优化
        int left = i + 1, right = n - 1, target = -nums[i];
    while(left < right)
    {
        int sum = nums[left] + nums[right];
        if(sum > target) right--;
        else if(sum < target) left++;
        else
        {
            ret.push_back({nums[i], nums[left], nums[right]});
            left++, right--;
            // 去重操作 left 和 right
            while(left < right && nums[left] == nums[left - 1]) left++;
            while(left < right && nums[right] == nums[right + 1]) 
            right--;
        }
    }
    // 去重 i 
    i++;
    while(i < n && nums[i] == nums[i - 1]) i++;
    }
     return ret;
}
};

滑动窗口

第六题:无重复字符的最长子串

题干 :给定字符串 s,找出其中不含有重复字符的最长子串的长度。

示例

  • 输入:s = "abcabcbb";输出:3(最长子串如 "abc""bca""cab")。
  • 输入:s = "bbbbb";输出:1(最长子串为 "b")。
  • 输入:s = "pwwkew";输出:3(最长子串为 "wke")。
C++ 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ret=0;
        int n=s.size();
        for(int i=0;i<n;i++)
        {
            int hash[128]={0};
            for(int j=i;j<n;j++)
            {
                hash[s[j]]++;
                if(hash[s[j]]>1)
                {
                    break;
                }
                ret=max(ret,j-i+1);
            }
        }
        return ret;
    }
};

第七题: 找到字符串中所有字母异位词

题干 :给定字符串 sp,找到 s中所有 p的字母异位词的子串,返回这些子串的起始索引(不考虑输出顺序)。

示例

  • 输入:s = "cbaebabacd", p = "abc";输出:[0,6](子串 "cba""bac""abc"的异位词)。
  • 输入:s = "abab", p = "ab";输出:[0,1,2](子串 "ab""ba""ab""ab"的异位词)。
C++ 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> res;
        int n=s.size(),m=p.size();
        if(n<m) return res;
        vector<int> pCount(26,0),winCount(26,0);
        // 统计 p 的字符频次
        for(char c:p)
        {
            pCount[c-'a']++;
        }
        // 初始化第一个窗口
        for(int i=0;i<m;i++)
        {
            winCount[s[i]-'a']++;
        }
        // 比较第一个窗口
        if(pCount==winCount)
        {
            res.push_back(0);
        }
        // 滑动窗口
        for(int i=m;i<n;i++)
        {
            // 加入新字符
            winCount[s[i]-'a']++;
            // 移除旧字符
            winCount[s[i-m]-'a']--;
            // 检查是否匹配
            if(pCount==winCount)
            {
                res.push_back(i-m+1);
            }
        }
        return res;
    }
};

子串

第八题:和为 K 的子数组

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

示例

  • 示例 1:输入:nums = [1,1,1], k = 2;输出:2(子数组 [1,1](前两个元素)、[1,1](后两个元素)的和为 2)。
  • 示例 2:输入:nums = [1,2,3], k = 3;输出:2(子数组 [1,2][3]的和为 3)。
C++ 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
      unordered_map<int,int> prefixCount;
      prefixCount[0] =1;// 初始状态:前缀和为0出现1次
      int sum=0,count=0;
      for(int num:nums)
      {
        sum+=num;//当前前缀和
        // 如果之前出现过 prefixSum = sum - k,说明存在子数组和为k
        if (prefixCount.find(sum - k) != prefixCount.end()) {
                count += prefixCount[sum - k];
        }
            // 更新当前前缀和的出现次数
            prefixCount[sum]++;
      }
      return count;
    }
};

普通数组

第九题:最大子数组和

题干 :给定整数数组 nums,找出一个连续子数组(至少包含一个元素),使其和最大,返回这个最大和。

示例

  • 输入:nums = [-2,1,-3,4,-1,2,1,-5,4];输出:6(最大和子数组为 [4,-1,2,1])。
  • 输入:nums = [1];输出:1(仅一个元素,和为自身)。
  • 输入:nums = [5,4,-1,7,8];输出:23(最大和子数组为 [5,4,-1,7,8])。
C++ 复制代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int current_sum = nums[0];
        int max_sum = nums[0];
        
        for (int i = 1; i < nums.size(); i++) {
            // 如果 current_sum 大于 0,保留并累加;否则抛弃,从当前元素重新开始
            current_sum = max(nums[i], current_sum + nums[i]);
            // 更新全局最大值
            max_sum = max(max_sum, current_sum);
        }
        
        return max_sum;
    }
};

第十题: 合并区间

题干 :给定若干区间的集合 intervals(每个区间为 [start_i, end_i]),合并所有重叠的区间,返回一个不重叠且恰好覆盖所有输入区间的新区间数组。

示例

  • 输入:intervals = [[1,3],[2,6],[8,10],[15,18]];输出:[[1,6],[8,10],[15,18]](区间 [1,3][2,6]重叠,合并为 [1,6])。
  • 输入:intervals = [[1,4],[4,5]];输出:[[1,5]](区间 [1,4][4,5]可视为重叠,合并为 [1,5])。
  • 输入:intervals = [[4,7],[1,4]];输出:[[1,7]](区间 [4,7][1,4]可视为重叠,合并为 [1,7])。
C++ 复制代码
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        if (intervals.empty()) return {};
        
        // 1. 按区间起点排序
        sort(intervals.begin(), intervals.end());
        
        vector<vector<int>> res;
        res.push_back(intervals[0]); // 第一个区间直接加入
        
        for (int i = 1; i < intervals.size(); i++) {
            // 当前区间的起点 <= 上一个区间的终点 → 有重叠
            if (intervals[i][0] <= res.back()[1]) {
                // 合并:更新上一个区间的终点为较大值
                res.back()[1] = max(res.back()[1], intervals[i][1]);
            } else {
                // 无重叠,直接加入
                res.push_back(intervals[i]);
            }
        }
        
        return res;
    }
};  

第十一题: 轮转数组

题干 :给定整数数组 nums,将数组中的元素向右轮转 k个位置k为非负数),需原地修改数组(或满足空间复杂度要求)。

示例

  • 输入:nums = [1,2,3,4,5,6,7], k = 3;输出:[5,6,7,1,2,3,4](向右轮转 3 步后的结果)。
  • 输入:nums = [-1,-100,3,99], k = 2;输出:[3,99,-1,-100](向右轮转 2 步后的结果)。
C++ 复制代码
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        if (n == 0) return;
        k = k % n;  // 防止 k 大于 n
        if (k == 0) return;

        // 1. 整体反转
        reverse(nums.begin(), nums.end());

        // 2. 反转前 k 个
        reverse(nums.begin(), nums.begin() + k);

        // 3. 反转剩余 n-k 个
        reverse(nums.begin() + k, nums.end());
    }
};

第十二题: 除了自身以外数组的乘积

题干 :给定整数数组 nums,返回数组 answer,其中 answer[i]等于 numsnums[i]之外其余所有元素的乘积。要求:不使用除法,时间复杂度 O(n),且任意元素的前缀/后缀乘积在 32 位整数范围内。

示例

  • 输入:nums = [1,2,3,4];输出:[24,12,8,6]answer[0]=2×3×4=24answer[1]=1×3×4=12,依此类推)。
  • 输入:nums = [-1,1,0,-3,3];输出:[0,0,9,0,0]answer[0]=1×0×(-3)×3=0answer[2]=(-1)×1×(-3)×3=9,依此类推)。
C++ 复制代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> answer(n);

        // 1. 计算前缀积:answer[i] = nums[0] * nums[1] * ... * nums[i-1]
        answer[0] = 1;  // 第一个元素左边没有数,乘积为1
        for (int i = 1; i < n; i++) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }

        // 2. 计算后缀积并乘入 answer:从右往左,用变量 rightProduct 记录右边乘积
        int rightProduct = 1;
        for (int i = n - 1; i >= 0; i--) {
            answer[i] *= rightProduct;
            rightProduct *= nums[i];
        }

        return answer;
    }
};

矩阵

第十三题:矩阵置零

题干 :给定一个 m x n的矩阵,若某个元素为 0,则将其所在的行和列的所有元素 都设为 0。要求使用原地算法(直接修改输入的矩阵,不使用额外矩阵)。

示例

  • 输入:matrix = [[1,1,1],[1,0,1],[1,1,1]];输出:[[1,0,1],[0,0,0],[1,0,1]](中间元素为 0,其所在行、列均置 0)。
  • 输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]];输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]](第一列和第四列的元素因含 0被置 0,第一行和第四行也因含 0被置 0)。
C++ 复制代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m=matrix.size();
        int n=matrix[0].size();
        vector<int> row(m),col(n);
        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])
                {
                    matrix[i][j]=0;
                }
            }
        }
    }
};

第十四题:螺旋矩阵

题干 :给定一个 mn列的矩阵 matrix,按照顺时针螺旋顺序返回矩阵中的所有元素。

示例

  • 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]];输出:[1,2,3,6,9,8,7,4,5](按顺时针螺旋遍历:右→下→左→上→右...)。
  • 输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]];输出:[1,2,3,4,8,12,11,10,9,5,6,7](螺旋遍历所有元素)。
c++ 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.empty() || matrix[0].empty()) return {};
        
        int m = matrix.size(), n = matrix[0].size();
        int top = 0, bottom = m - 1, left = 0, right = n - 1;
        vector<int> result;
        
        while (top <= bottom && left <= right) {
            // 1. 左 → 右
            for (int j = left; j <= right; j++) {
                result.push_back(matrix[top][j]);
            }
            top++;
            
            // 2. 上 → 下
            for (int i = top; i <= bottom; i++) {
                result.push_back(matrix[i][right]);
            }
            right--;
            
            // 3. 右 → 左(需判断是否还有行)
            if (top <= bottom) {
                for (int j = right; j >= left; j--) {
                    result.push_back(matrix[bottom][j]);
                }
                bottom--;
            }
            
            // 4. 下 → 上(需判断是否还有列)
            if (left <= right) {
                for (int i = bottom; i >= top; i--) {
                    result.push_back(matrix[i][left]);
                }
                left++;
            }
        }
        
        return result;
    }
};

第十五题: 旋转图像

题干 :给定一个 n x n的二维矩阵(表示图像),将图像顺时针旋转 90 度 。要求原地旋转(直接修改输入的矩阵,不使用额外矩阵)。

示例

  • 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]];输出:[[7,4,1],[8,5,2],[9,6,3]](顺时针旋转 90 度后的结果)。
  • 输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]];输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]](顺时针旋转 90 度后的结果)。
C++ 复制代码
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size();
        // Step 1: 转置矩阵(沿主对角线)
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                swap(matrix[i][j],matrix[j][i]);
            }
        }
        // Step 2: 反转每一行
        for(int i=0;i<n;i++)
        {
            reverse(matrix[i].begin(),matrix[i].end());
        }
    }
};

第十六题: 搜索二维矩阵 II

题干 :编写一个高效的算法,搜索 m x n矩阵 matrix中的一个目标值 target。矩阵特性:

  • 每行的元素从左到右升序排列

  • 每列的元素从上到下升序排列

    示例

  • 输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5;输出:true(目标值 5存在于矩阵中)。

  • 输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20;输出:false(目标值 20不存在于矩阵中)。

C++ 复制代码
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty())
        {
            return false;
        }
        int m=matrix.size(),n=matrix[0].size();
        int row=0,col=n-1;//从右上角开始
        while(row<m&&col>=0)
        {
            if(matrix[row][col]==target)
            {
                return true;
            }
            else if(matrix[row][col]>target)
            {
                col--;//向左
            }
            else
            {
                row++;//向下移动
            }
        }
        return false;
    }
};

链表

第十七题: 相交链表

题干 :给定两个单链表的头节点 headAheadB,找到并返回它们的相交起始节点 (若无相交返回 null)。保证链表无环。

核心 :双指针法(A走完走 BB走完走 A,相遇时即为相交点,或都走到 null)。

C++ 复制代码
/**
 * 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 || !headB) return nullptr;

        ListNode *pA = headA, *pB = headB;

        while (pA != pB) {
            pA = pA ? pA->next : headB;
            pB = pB ? pB->next : headA;
        }

        return pA;
    }
};

第十八题:反转链表

题干 :给定单链表头节点 head反转链表并返回新头节点。

核心 :迭代法(三指针 prev, curr, next)或递归法(从后往前反转)。

C++ 复制代码
/**
 * 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* prev = nullptr;   // 指向前一个节点
        ListNode* curr = head;      // 当前节点
        
        while (curr != nullptr) {
            ListNode* nextTemp = curr->next;  // 临时保存下一个节点
            curr->next = prev;                // 反转指针
            prev = curr;                      // 移动 prev
            curr = nextTemp;                  // 移动 curr
        }

        return prev;  // prev 现在是新链表的头
    }
};

第十九题:回文链表

题干 :判断单链表是否为回文链表(正读和反读相同)。

核心:快慢指针找中点,反转后半部分,比较前半和后半是否一致。

C++ 复制代码
/**
 * 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:
    bool isPalindrome(ListNode* head) {
        if (!head || !head->next) return true;

        // 1. 快慢指针找中点
        ListNode *slow = head, *fast = head;
        while (fast->next && fast->next->next) {
            slow = slow->next;
            fast = fast->next->next;
        }

        // 2. 反转后半部分(从 slow->next 开始)
        ListNode *secondHalf = reverseList(slow->next);
        slow->next = nullptr; // 断开前半部分和后半部分(可选,便于比较)

        // 3. 比较前后两部分
        ListNode *p1 = head, *p2 = secondHalf;
        bool result = true;
        while (p1 && p2) {
            if (p1->val != p2->val) {
                result = false;
                break;
            }
            p1 = p1->next;
            p2 = p2->next;
        }

        // 4. (可选)恢复链表结构
        slow->next = reverseList(secondHalf);

        return result;
    }

private:
    ListNode* reverseList(ListNode* head) {
        ListNode *prev = nullptr, *curr = head, *nextTemp = nullptr;
        while (curr) {
            nextTemp = curr->next;
            curr->next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }
};

第二十题: 环形链表

题干 :判断单链表是否有环(节点可通过 next再次到达)。

核心:快慢指针(快指针走两步,慢指针走一步,相遇则有环)。

C++ 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (!head || !head->next) return false;

        ListNode *slow = head;
        ListNode *fast = head;

        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;

            if (slow == fast) {
                return true;
            }
        }

        return false;
    }
};

第二十一题: 环形链表 II

题干 :找到环形链表中环的入口节点 (若无环返回 null)。

核心:快慢指针相遇后,一个指针回到头,两指针同速前进,相遇点即为环入口。

C++ 复制代码
/**
 * 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 || !head->next) return nullptr;

        ListNode *slow = head, *fast = head;

        // 第一步:找相遇点,判断是否有环
        do {
            if (!fast || !fast->next) return nullptr; // 无环
            slow = slow->next;
            fast = fast->next->next;
        } while (slow != fast);

        // 第二步:找环入口
        slow = head; // slow 回到起点
        while (slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }

        return slow; // 返回环入口节点
    }
};

第二十二题:合并两个有序链表

题干 :将两个升序链表合并为一个新的升序链表,返回新链表头节点。

核心:迭代法(新建虚拟头节点,依次拼接较小的节点)或递归法(合并剩余部分)。

C++ 复制代码
/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        // 创建伪头节点
        ListNode dummy(0);
        ListNode* tail = &dummy;

        // 双指针遍历两个链表
        while (list1 != nullptr && list2 != nullptr) {
            if (list1->val <= list2->val) {
                tail->next = list1;
                list1 = list1->next;
            } else {
                tail->next = list2;
                list2 = list2->next;
            }
            tail = tail->next;
        }

        // 拼接剩余部分
        if (list1 != nullptr) {
            tail->next = list1;
        } else {
            tail->next = list2;
        }

        return dummy.next;
    }
};

第二十三题:两数相加

题干 :两个非负整数的链表(逆序存储每位数字),将它们相加,返回和的链表(同样逆序)。

核心:逐位相加,处理进位,注意链表长度不同时的补零。

C++ 复制代码
/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* cur1 = l1, *cur2 = l2;
        ListNode* newhead = new ListNode(0); // 创建虚拟头结点
        ListNode* prev = newhead;             // 尾指针
        int t = 0;                            // 记录进位

        while (cur1 || cur2 || t) {
            // 加上第一个链表
            if (cur1) {
                t += cur1->val;
                cur1 = cur1->next;
            }
            // 加上第二个链表
            if (cur2) {
                t += cur2->val;
                cur2 = cur2->next;
            }
            // 创建新节点,存储当前位结果
            prev->next = new ListNode(t % 10);
            prev = prev->next;
            t /= 10; // 计算进位
        }

        prev = newhead->next;
        delete newhead; // 释放虚拟头结点
        return prev;
    }
};

第二十四题: 删除链表的倒数第 N 个结点

题干 :删除单链表的倒数第 n个节点,返回头节点。

核心 :双指针法(快指针先走 n步,然后快慢同走,快到末尾时慢指针指向待删节点的前一个)。

C++ 复制代码
/**
 * 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* dummy = new ListNode(0); // 创建哑节点
        dummy->next = head;
        ListNode* fast = dummy;
        ListNode* slow = dummy;

        // 快指针先走 n 步
        for (int i = 0; i < n; i++) {
            fast = fast->next;
        }

        // 快慢指针同时走,直到快指针到末尾
        while (fast->next) {
            fast = fast->next;
            slow = slow->next;
        }

        // 删除倒数第 n 个节点
        ListNode* toDelete = slow->next;
        slow->next = slow->next->next;
        delete toDelete; // 可选:释放内存(C++)

        ListNode* result = dummy->next;
        delete dummy;    // 释放哑节点
        return result;
    }
};

第二十五题:两两交换链表中的节点

题干 :两两交换链表中的相邻节点,返回新头节点。要求只交换节点(不修改节点值)

核心:迭代法(虚拟头节点 + 三指针)或递归法(交换前两个节点,递归处理剩余)。

C++ 复制代码
/**
 * 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) {
        // 边界情况
        if (!head || !head->next) return head;

        // 创建虚拟头节点
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode* prev = dummy;

        while (head && head->next) {
            ListNode* first = head;
            ListNode* second = head->next;

            // 交换节点指针
            prev->next = second;
            first->next = second->next;
            second->next = first;

            // 移动指针准备下一轮
            prev = first;
            head = first->next;
        }

        return dummy->next;
    }
};

第二十六题:复制带随机指针的链表

题干 :复制一个含 random指针的链表(每个节点有 valnextrandom),要求深拷贝,且新链表的 random指针与原链表对应节点一致。

核心:哈希表存储原节点与新节点的映射,或原地插入复制节点后再拆分。

C++ 复制代码
// Definition for a Node.
// class Node {
// public:
//     int val;
//     Node* next;
//     Node* random;
    
//     Node(int _val) {
//         val = _val;
//         next = nullptr;
//         random = nullptr;
//     }
// };

class Solution {
public:
    Node* copyRandomList(Node* head) {  
        if (head == nullptr) {
            return nullptr;
        }
        
        // 第一步:在每个原节点后面插入一个拷贝节点
        Node* pcur = head;
        while (pcur != nullptr) {
            Node* copyNode = new Node(pcur->val);
            copyNode->next = pcur->next;
            pcur->next = copyNode;
            pcur = copyNode->next;
        }
        
        // 第二步:设置拷贝节点的random指针
        pcur = head;
        while (pcur != nullptr) {
            Node* copyNode = pcur->next;
            if (pcur->random != nullptr) {
                copyNode->random = pcur->random->next;
            }
            pcur = copyNode->next;
        }
        
        // 第三步:拆分原链表和拷贝链表
        pcur = head;
        Node* newHead = head->next;
        Node* newTail = newHead;
        
        while (pcur != nullptr) {
            // 恢复原链表
            pcur->next = newTail->next;
            pcur = pcur->next;
            
            // 构建新链表
            if (pcur != nullptr) {
                newTail->next = pcur->next;
                newTail = newTail->next;
            }
        }
        
        return newHead;
    }
};

第二十七题: 排序链表

题干 :给定单链表头节点 head,将链表升序排列后返回。

C++ 复制代码
/**
 * 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* sortList(ListNode* head) {
        if(!head||!head->next) return head;
        ListNode *slow=head;
        ListNode *fast=head->next;
        while(fast&&fast->next)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        ListNode* rightHead=slow->next;
        slow->next = nullptr;  // 断开

        ListNode* left = sortList(head);
        ListNode* right = sortList(rightHead);

        // 3. 合并两个有序链表
        return merge(left, right);
    }
    private:
    ListNode* merge(ListNode* l1, ListNode* l2) {
        ListNode dummy(0);
        ListNode* tail = &dummy;

        while (l1 && l2) {
            if (l1->val <= l2->val) {
                tail->next = l1;
                l1 = l1->next;
            } else {
                tail->next = l2;
                l2 = l2->next;
            }
            tail = tail->next;
        }

        tail->next = l1 ? l1 : l2;
        return dummy.next;
    }
};

第二十八题: LRU 缓存

题干 :设计实现 LRUCache数据结构,满足以下要求:

  • LRUCache(int capacity):以正整数 capacity为容量初始化缓存。

  • int get(int key):若 key存在则返回对应 value,否则返回 -1

  • void put(int key, int value):若 key存在则更新 value;否则插入键值对。若插入后缓存大小超过 capacity,则逐出最近最少使用的键值对。

  • getput操作的平均时间复杂度需为 O(1)

    C++ 复制代码
    class LRUCache {
    public:
        LRUCache(int capacity) : _capacity(capacity) {}
        int get(int key) {
            auto it = _hashmap.find(key);
            if (it == _hashmap.end()) return -1;
            // 移动到头部
            auto listit = it->second;
            pair<int, int> kv = *listit;
            _list.erase(listit);
            _list.push_front(kv);
            _hashmap[key] = _list.begin();
            return kv.second;
        }
        void put(int key, int value) {
            auto it = _hashmap.find(key);
    
            if (it == _hashmap.end()) {
                // 新增
                if (_list.size() >= _capacity) {
                    _hashmap.erase(_list.back().first); // 删除哈希记录
                    _list.pop_back();                   // 删除链表尾
                }
                _list.push_front({key, value});
                _hashmap[key] = _list.begin();
            } else {
                // 更新
                auto listit = it->second;
                pair<int, int> kv = *listit;
                kv.second = value;
                _list.erase(listit);
                _list.push_front(kv);
                _hashmap[key] = _list.begin();
            }
        }
    
    private:
        list<pair<int, int>> _list;
        size_t _capacity;
        unordered_map<int, list<pair<int, int>>::iterator> _hashmap;
    };

二叉树

第二十九题:二叉树的中序遍历

题干 :给定二叉树的根节点 root,返回它的中序遍历结果(左子树→根节点→右子树)。

示例

  • 输入:root = [1,null,2,3];输出:[1,3,2](中序遍历:1→3→2)。
C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        inorder(root, result);
        return result;
    }

private:
    void inorder(TreeNode* node, vector<int>& result) {
        if (node == nullptr) return;
        
        inorder(node->left, result);   // 1. 遍历左子树
        result.push_back(node->val);   // 2. 访问根节点
        inorder(node->right, result);  // 3. 遍历右子树
    }
};

第三十题:二叉树的最大深度

题干 :给定二叉树 root,求其最大深度(从根节点到最远叶子节点的最长路径上的节点数)。

示例

  • 输入:root = [3,9,20,null,null,15,7];输出:3(路径:3→20→15 或 3→20→7,长度3)。
C++ 复制代码
/*
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) {
            return 0;
        }
        int leftDepth = maxDepth(root->left);
        int rightDepth = maxDepth(root->right);
        return max(leftDepth, rightDepth) + 1;
    }
};

第三十一题:对称二叉树

题干 :给定二叉树 root,判断它是否为对称二叉树(沿根节点的垂直中线折叠后,左右子树完全重合)。

示例

  • 输入:root = [1,2,2,3,4,4,3];输出:true(左右子树对称)。

    C++ 复制代码
    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
     *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
     *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
     * };
     */
    class Solution {
    public:
        bool isSymmetric(TreeNode* root) {
            if (!root) return true;
            return isMirror(root->left, root->right);
        }
    
    private:
        bool isMirror(TreeNode* left, TreeNode* right) {
            if (!left && !right) return true;
            if (!left || !right) return false;
            if (left->val != right->val) return false;
            return isMirror(left->left, right->right) && isMirror(left->right, right->left);
        }
    };

第三十二题:二叉树的层序遍历

题干 :给定二叉树 root,按层序遍历(从顶部到底部,每层从左到右)返回节点值的列表。

示例

  • 输入:root = [3,9,20,null,null,15,7];输出:[[3],[9,20],[15,7]](逐层输出)。
C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if(!root) return result;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            int levelSize=q.size();
            vector<int> currentLevel;
            for(int i=0;i<levelSize;i++)
            {
                TreeNode* node=q.front();
                q.pop();
                currentLevel.push_back(node->val);
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            result.push_back(currentLevel);
        }
        return result;
    }
};

第三十三题:将有序数组转换为二叉搜索树

题干 :给定升序数组 nums,将其转换为一棵高度平衡的二叉搜索树(左右子树高度差≤1,且左子树所有节点<根<右子树所有节点)。

示例

  • 输入:nums = [-10,-3,0,5,9];输出:[0,-3,9,-10,null,5](平衡BST)。
C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        // 入口函数:调用递归辅助函数,初始范围是整个数组
        return buildBST(nums, 0, nums.size() - 1);
    }

private:
    // 递归辅助函数:在区间 [left, right] 上构建 BST
    TreeNode* buildBST(vector<int>& nums, int left, int right) {
        // 基础情况:如果左边界超过右边界,说明没有元素可构建,返回空
        if (left > right) {
            return nullptr;
        }

        // 取中间位置(防止整数溢出,推荐使用 left + (right - left) / 2)
        int mid = left + (right - left) / 2;

        // 创建根节点
        TreeNode* root = new TreeNode(nums[mid]);

        // 递归构建左子树(左半部分)
        root->left = buildBST(nums, left, mid - 1);

        // 递归构建右子树(右半部分)
        root->right = buildBST(nums, mid + 1, right);

        // 返回当前构建好的子树的根节点
        return root;
    }
};

第三十四题: 二叉树的右视图

题干 :给定二叉树 root,返回从右侧观察能看到的节点值(即每层最右边的节点)。

示例

  • 输入:root = [1,2,3,null,5,null,4];输出:[1,3,4](每层最右节点:1→3→4)。
C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        vector<int> result;
        if (!root) return result;

        queue<TreeNode*> q;
        q.push(root);

        while (!q.empty()) {
            int levelSize = q.size(); // 当前层的节点数

            for (int i = 0; i < levelSize; ++i) {
                TreeNode* curr = q.front();
                q.pop();

                // 如果是当前层的最后一个节点,加入结果
                if (i == levelSize - 1) {
                    result.push_back(curr->val);
                }

                // 先左后右入队(保证下一层从左到右处理)
                if (curr->left) q.push(curr->left);
                if (curr->right) q.push(curr->right);
            }
        }

        return result;
    }
};

第三十五题: 翻转二叉树

题干 :给定二叉树 root翻转该树(每个节点的左右子树交换位置),返回翻转后的根节点。

示例

  • 输入:root = [4,2,7,1,3,6,9];输出:[4,7,2,9,6,3,1](左右子树交换)。
C++ 复制代码
**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        // 基础情况:空节点直接返回
        if (!root) return nullptr;

        // 递归翻转左右子树
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);

        // 交换左右子树
        root->left = right;
        root->right = left;

        return root;
    }
};

第三十六题: 二叉树的直径

题干 :给定二叉树 root,求其直径(任意两节点之间最长路径的边数,路径可能不经过根节点)。

示例

  • 输入:root = [1,2,3,4,5];输出:3(路径:4→2→5 或 5→2→1→3,边数3)。
C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
        int m=0;
        dfs(root,m);
        return m;
    }
private:
    int dfs(TreeNode* node,int &m)
    {
        if(!node) return 0;
        int dleft=dfs(node->left,m);
        int dright=dfs(node->right,m);
        m=max(m,dleft+dright);
        return max(dleft,dright)+1;
    }
};

第三十七题:二叉搜索树中第 K 小的元素

题干 :给定二叉搜索树 root和整数 k,返回树中第 k 小的元素(按升序排列的第 k 个节点值)。

示例

  • 输入:root = [3,1,4,null,2], k=1;输出:1(升序:1→2→3→4,第1小是1)。
C++ 复制代码
class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        int result = 0;
        inorder(root, k, result);
        return result;
    }

private:
    void inorder(TreeNode* node, int& k, int& result) {
        if (!node) return;
        
        inorder(node->left, k, result);  // 左
        k--;                              // 访问当前节点,计数减一
        if (k == 0) {                     // 找到第k个
            result = node->val;
            return;
        }
        inorder(node->right, k, result); // 右
    }
};

第三十八题:验证二叉搜索树

题干 :给定二叉树 root,判断它是否为有效二叉搜索树(左子树所有节点<根,右子树所有节点>根,且子树自身也满足BST)。

示例

  • 输入:root = [2,1,3];输出:true(1<2<3,子树合法)。
C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        long long prev=LLONG_MIN;
        return inorder(root,prev);
    }
private:
    bool inorder(TreeNode *node,long long&prev)
    {
        if (!node) return true;

        // 遍历左子树
        if (!inorder(node->left, prev)) return false;

        // 检查当前节点是否大于前一个节点
        if (node->val <= prev) return false;
        prev = node->val; // 更新前一个值

        // 遍历右子树
        return inorder(node->right, prev);
    }
};

第三十九题:路径总和 III

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

C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        if(!root) return 0;
        //从当前节点开始
        int count=dfs(root,targetSum);
        count+=pathSum(root->left,targetSum);
        count+=pathSum(root->right,targetSum);
        return count;
    }
private:
    int dfs(TreeNode* node,long long target)
    {
        if(!node) return 0;
        int count=0;
        if(node->val==target)  count++;
        // 继续向下找,目标和减去当前节点值
        count += dfs(node->left, target - node->val);
        count += dfs(node->right, target - node->val);
        
        return count;
    }
};

第四十题:二叉树的最近公共祖先

题干 :给定二叉树的两个节点 pq,找到它们的最近公共祖先 (LCA)。最近公共祖先定义为:一个节点 x,满足 xpq的祖先,且 x的深度尽可能大(节点本身也可以是自己的祖先)。

C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root) return nullptr;
        if (p == root || q == root) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (right && left) return root;
        if (left) return left;
        if (right) return right;
        return nullptr; 
    }
};

第四十一题:从前序与中序遍历序列构造二叉树

题干 :给定两个整数数组 preorder(先序遍历)和 inorder(中序遍历),构造该二叉树并返回其根节点。

C++ 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        // 建立值到索引的映射,加速查找
        unordered_map<int, int> indexMap;
        for (int i = 0; i < inorder.size(); ++i) {
            indexMap[inorder[i]] = i;
        }

        // 递归函数:传入前序和中序的左右边界
        function<TreeNode*(int, int, int, int)> dfs = [&](int preLeft, int preRight, int inLeft, int inRight) -> TreeNode* {
            // 递归终止条件
            if (preLeft > preRight || inLeft > inRight) return nullptr;

            // 前序第一个元素是当前根节点
            int rootVal = preorder[preLeft];
            TreeNode* root = new TreeNode(rootVal);

            // 在中序中找到根节点位置
            int rootIdx = indexMap[rootVal];

            // 计算左子树节点数
            int leftSize = rootIdx - inLeft;

            // 递归构建左子树:前序[preLeft+1, preLeft+leftSize],中序[inLeft, rootIdx-1]
            root->left = dfs(preLeft + 1, preLeft + leftSize, inLeft, rootIdx - 1);

            // 递归构建右子树:前序[preLeft+leftSize+1, preRight],中序[rootIdx+1, inRight]
            root->right = dfs(preLeft + leftSize + 1, preRight, rootIdx + 1, inRight);

            return root;
        };

        return dfs(0, preorder.size() - 1, 0, inorder.size() - 1);
    }
};

第四十二题:二叉树展开为链表

题干 :给定二叉树的根节点 root,将其展开为一个单链表。展开后的链表应使用 TreeNode,其中 right指针指向链表的下一个节点,left指针始终为 null。链表应与二叉树的先序遍历顺序相同。

C++ 复制代码
class Solution {
public:
    void flatten(TreeNode* root) {
        if (!root) return;

        // 递归展开左右子树
        flatten(root->left);
        flatten(root->right);

        // 暂存右子树
        TreeNode* right = root->right;

        // 将左子树移到右子树位置
        root->right = root->left;
        root->left = nullptr;

        // 找到当前右链表的末尾
        TreeNode* p = root;
        while (p->right) {
            p = p->right;
        }

        // 接上原右子树
        p->right = right;
    }
};

图论

第四十三题:岛屿数量

给定由 '1'(陆地)和 '0'(水)组成的二维网格,计算岛屿数量。岛屿定义为水平/竖直方向相邻的陆地,且网格四边均被水包围。

C++ 复制代码
class Solution {
    int m, n;
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {1, -1, 0, 0};
    
public:
    int numIslands(vector<vector<char>>& grid) {
        if(grid.empty() || grid[0].empty()) return 0;
        m = grid.size(), n = grid[0].size();
        
        int ret = 0;
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == '1') {
                    ret++;
                    dfs(grid, i, j);
                }
            }
        return ret;
    }
    
private:
    void dfs(vector<vector<char>>& grid, int i, int j) {
        if(i < 0 || i >= m || j < 0 || j >= n || grid[i][j] != '1')
            return;
        
        grid[i][j] = '0';  // 标记为已访问
        
        for(int k = 0; k < 4; k++) {
            int x = i + dx[k], y = j + dy[k];
            dfs(grid, x, y);
        }
    }
};

第四十四题: 腐烂的橘子

m×n网格中,单元格值 0(空)、1(新鲜橘子)、2(腐烂橘子)。每分钟,腐烂橘子会使其上下左右相邻 的新鲜橘子腐烂。求所有橘子腐烂所需的最小分钟数 ;若存在新鲜橘子永远无法腐烂(与腐烂橘子不连通),返回 -1

C++ 复制代码
class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        queue<pair<int, int>> q;
        int fresh = 0; // 新鲜橘子计数
        int time = 0;  // 腐烂所需时间

        // 初始化:找到所有腐烂橘子入队,统计新鲜橘子
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 2) {
                    q.push({i, j});
                } else if (grid[i][j] == 1) {
                    fresh++;
                }
            }
        }

        // 特判:如果没有新鲜橘子,直接返回0
        if (fresh == 0) return 0;

        // 四个方向:上、下、左、右
        vector<pair<int, int>> dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};

        // BFS 多源扩散
        while (!q.empty()) {
            int size = q.size();
            bool rottenThisRound = false;

            for (int k = 0; k < size; k++) {
                auto [x, y] = q.front(); q.pop();

                for (auto& dir : dirs) {
                    int nx = x + dir.first, ny = y + dir.second;
                    if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {
                        grid[nx][ny] = 2; // 标记为腐烂
                        q.push({nx, ny});
                        fresh--;          // 新鲜橘子减少
                        rottenThisRound = true;
                    }
                }
            }

            // 如果本轮有感染发生,时间+1
            if (rottenThisRound) time++;
        }

        // 如果还有新鲜橘子没被感染,说明无法全部腐烂
        return fresh == 0 ? time : -1;
    }
};

第四十五题: 课程表

给定 numCourses门课程(编号 0~numCourses-1)和先修关系数组 prerequisitesprerequisites[i] = [a_i, b_i]表示学 a_i前需先学 b_i)。判断是否能完成所有课程(即先修关系无循环依赖)。

C++ 复制代码
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        //构建邻接表和入度数组
        vector<vector<int>> graph(numCourses);
        vector<int> indegree(numCourses,0);

        for(auto& pre:prerequisites)
        {
            int course=pre[0];
            int prereq=pre[1];
            graph[prereq].push_back(course);
            indegree[course]++;
        }

        queue<int> q;
        for(int i=0;i<numCourses;i++)
        {
            if(indegree[i]==0)
            {
                q.push(i);
            }
        }
        int visited=0;
        while(!q.empty())
        {
            int cur=q.front();
            q.pop();
            visited++;
            for(int next:graph[cur])
            {
                indegree[next]--;
                if(indegree[next]==0)
                {
                    q.push(next);
                }
            }
        }
        return visited==numCourses;
    }
};

第四十六题:实现 Trie(前缀树)

实现前缀树(Trie)数据结构,支持以下操作:

  • insert(word):插入字符串 word
  • search(word):判断 word是否已插入(精确匹配);
  • startsWith(prefix):判断是否存在已插入的字符串以 prefix为前缀。
C++ 复制代码
class Trie {
private:
    struct TrieNode {
        bool isEnd;
        TrieNode* children[26];
        TrieNode() {
            isEnd = false;
            for (int i = 0; i < 26; i++) {
                children[i] = nullptr;
            }
        }
    };
    TrieNode* root;

public:
    Trie() {
        root = new TrieNode();
    }

    void insert(string word) {
        TrieNode* node = root;
        for (char ch : word) {
            int idx = ch - 'a';
            if (node->children[idx] == nullptr) {
                node->children[idx] = new TrieNode();
            }
            node = node->children[idx];
        }
        node->isEnd = true;
    }

    bool search(string word) {
        TrieNode* node = root;
        for (char ch : word) {
            int idx = ch - 'a';
            if (node->children[idx] == nullptr) {
                return false;
            }
            node = node->children[idx];
        }
        return node->isEnd;
    }

    bool startsWith(string prefix) {
        TrieNode* node = root;
        for (char ch : prefix) {
            int idx = ch - 'a';
            if (node->children[idx] == nullptr) {
                return false;
            }
            node = node->children[idx];
        }
        return true;
    }
};
/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

回溯

第四十七题:全排列

给定不含重复数字 的数组 nums,返回其所有可能的全排列(顺序任意)。

C++ 复制代码
class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> result;
        backtrack(nums,0,result);
        return result;
    }
private:
    void backtrack(vector<int>&nums,int start,vector<vector<int>>&result)
    {
        if (start == nums.size()) {
            result.push_back(nums);
            return;
        }
        
        for (int i = start; i < nums.size(); ++i) {
            swap(nums[start], nums[i]);          // 交换,固定当前位置
            backtrack(nums, start + 1, result);  // 递归处理剩余部分
            swap(nums[start], nums[i]);          // 撤销交换,回溯
        }
    }
};

第四十八题:子集

给定元素互不相同 的整数数组 nums,返回其所有可能的子集(幂集,解不能含重复子集,顺序任意)。

C++ 复制代码
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        vector<int> path;
        backtrack(nums, 0, path, result);
        return result;
    }
private:
    void backtrack(const vector<int>& nums, int start, vector<int>& path, vector<vector<int>>& result) {
        // 每进入一次递归,就记录当前路径(即一个子集)
        result.push_back(path);

        // 从 start 开始,避免重复选择前面的元素(保证子集无序且不重复)
        for (int i = start; i < nums.size(); ++i) {
            path.push_back(nums[i]);      // 选择当前元素
            backtrack(nums, i + 1, path, result); // 递归处理下一个元素
            path.pop_back();              // 回溯,撤销选择
        }
    }
};

第四十九题:电话号码的字母组合

给定仅包含数字 2-9的字符串 digits,返回其能表示的所有字母组合(数字到字母映射同电话按键,顺序任意)。

C++ 复制代码
class Solution {
public:
    vector<string> letterCombinations(string digits) {
        // 边界情况
        if (digits.empty()) return {};

        // 建立数字到字母的映射
        vector<string> mapping = {
            "",     // 0
            "",     // 1
            "abc",  // 2
            "def",  // 3
            "ghi",  // 4
            "jkl",  // 5
            "mno",  // 6
            "pqrs", // 7
            "tuv",  // 8
            "wxyz"  // 9
        };

        vector<string> result;
        string path;

        // 回溯函数(lambda 表达式或可另写为成员函数)
        function<void(int)> backtrack = [&](int index) {
            // 终止条件:已处理完所有数字
            if (index == digits.size()) {
                result.push_back(path);
                return;
            }

            // 获取当前数字对应的字母串
            int digit = digits[index] - '0'; // 转为整数
            string letters = mapping[digit];

            // 遍历当前数字的所有可选字母
            for (char c : letters) {
                path.push_back(c);      // 做选择
                backtrack(index + 1);   // 递归下一层
                path.pop_back();        // 撤销选择(回溯)
            }
        };

        backtrack(0);
        return result;
    }
};

第五十题: 组合总和

给定无重复元素 的整数数组 candidates和目标整数 target,找出 candidates中数字和为 target的所有不同组合candidates中同一数字可无限重复选取,顺序任意)。

C++ 复制代码
class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> res;
        vector<int> path;
        sort(candidates.begin(), candidates.end()); // 排序便于剪枝
        backtrack(candidates, target, 0, path, res);
        return res;
    }

private:
    void backtrack(vector<int>& candidates, int target, int start, vector<int>& path, vector<vector<int>>& res) {
        if (target == 0) {
            res.push_back(path);
            return;
        }
        for (int i = start; i < candidates.size(); i++) {
            if (candidates[i] > target) break; // 剪枝:当前数已大于剩余目标
            path.push_back(candidates[i]);
            backtrack(candidates, target - candidates[i], i, path, res); // i 不加1,允许重复选
            path.pop_back(); // 回溯
        }
    }
};

第五十一题: 括号生成

数字 n代表生成括号的对数,设计函数生成所有可能的有效括号组合

C++ 复制代码
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> result;
        string current;
        backtrack(result,current,0,0,n);
        return result;
    }
private:
    void backtrack(vector<string> &result,string &current,int open,int close,int max)
    {
        if(current.size()==2*max)
        {
            result.push_back(current);
            return;
        }
        if(open<max)
        {
            current.push_back('(');
            backtrack(result,current,open+1,close,max);
            current.pop_back();
        }
        if(close<open)
        {
            current.push_back(')');
            backtrack(result,current,open,close+1,max);
            current.pop_back();
        }
    }
};

第五十二题:单词搜索

给定 m×n二维字符网格 board和字符串 word,若 word可通过水平/垂直相邻 (不可重复使用单元格)的字母按顺序构成,返回 true;否则返回 false

C++ 复制代码
class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        int m = board.size();
        int n = board[0].size();

        // 从每个格子作为起点尝试
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (dfs(board, word, i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }

private:
    bool dfs(vector<vector<char>>& board, string& word, int i, int j, int k) {
        // 匹配完成
        if (k == word.size()) return true;

        // 越界、字符不匹配、已访问
        if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size() || board[i][j] != word[k]) {
            return false;
        }

        // 标记当前格子为已访问
        char temp = board[i][j];
        board[i][j] = '#';

        // 四个方向搜索
        bool found = 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] = temp;

        return found;
    }
};

第五十三题:分割回文串

给定字符串 s,将其分割成若干子串,使每个子串都是回文串 ,返回 s所有可能的分割方案。

C++ 复制代码
class Solution {
public:
    vector<vector<string>> partition(string s) {
        vector<vector<string>> result;
        vector<string> path;
        backtrack(s, 0, path, result);
        return result;
    }
private:
    void backtrack(const string& s,int start,vector<string>& path,vector<vector<string>>& result)
    {
        // 终止条件:已处理完所有字符
        if(start==s.size())
        {
            result.push_back(path);
            return;
        }
        // 尝试从 start 开始,切割到每一个位置 i
        for(int i=start;i<s.size();i++)
        {
            if (isPalindrome(s, start, i)) {
                path.push_back(s.substr(start, i - start + 1));
                backtrack(s, i + 1, path, result);
                path.pop_back(); // 回溯
            }
        }
    }
    bool isPalindrome(const string& s, int start, int end) {
        while (start < end) {
            if (s[start] != s[end]) return false;
            start++;
            end--;
        }
        return true;
    }
};

二分查找

第五十四题: 搜索旋转排序数组

升序数组(元素互不相同)经未知下标左旋转后,给定旋转数组和目标值,用O(log n)时间找目标下标(不存在则-1)。

C++ 复制代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]==target)
            {
                return mid;
            }
            if(nums[left]<=nums[mid])
            {
                if(target>=nums[left]&&target<=nums[mid])
                {
                    right=mid-1;
                }
                else
                {
                    left=mid+1;
                }
            }
            else{
                if(target>=nums[mid]&&target<=nums[right])
                {
                    left=mid+1;
                }
                else
                {
                    right=mid-1;
                }
            }
        }
        return -1;
    }
};

第五十五题:在排序数组中查找元素的第一个和最后一个位置

非递减数组(含重复元素)中,用O(log n)时间找目标值的起始和结束位置(不存在则返回[-1,-1])。

C++ 复制代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        int start=-1,end=-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]==target)
            {
                start=mid;
                right=mid-1;
            }
            else if(nums[mid]<target)
            {
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
        if(start==-1)
        {
            return {-1,-1};
        }
        left=0;
        right=nums.size()-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]==target)
            {
                end=mid;
                left=mid+1;
            }
            else if(nums[mid]<target)
            {
                left=mid+1;
            }
            else
            {
                right=mid-1;
            }
        }
        return {start,end};
    }
};

第五十六题: 搜索插入位置

升序无重复数组,找目标值索引(不存在则返回插入位置),要求O(log n)时间。

C++ 复制代码
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0;// 左指针,初始指向数组起始位置
        int right=nums.size()-1;// 右指针,初始指向数组末尾位置
        while(left<=right)// 二分查找的循环条件:左指针不超过右指针
        {
            int mid=left+(right-left)/2;// 计算中间位置,避免(left+right)溢出
            if(nums[mid]==target)
            {
                return mid;// 找到目标值,返回其索引
            }
            else if(nums[mid]<target)
            {
                left=mid+1;// 目标值在右半部分,调整左指针
            }
            else if(nums[mid]>target)
            {
                right=mid-1;// 目标值在左半部分,调整右指针
            }
        }
        // 循环结束未找到,left即为插入位置(此时left > right,left是第一个大于target的位置)
        return left;
    }
};

第五十七题: 搜索二维矩阵

m×n矩阵满足"每行非严格递增、每行首元素>前一行尾元素",判断目标值是否存在。

C++ 复制代码
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0||matrix[0].size()==0)
        {
            return false;
        }
        int m=matrix.size();
        int n=matrix[0].size();
        int left=0,right=m*n-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            int row=mid/n;
            int col=mid%n;
            int val=matrix[row][col];
            if(val==target)
            {
                return true;
            }
            else if(val<target)
            {
                left=mid+1;
            }
            else
            {
                right=mid-1;
            }
        }
        return false;
    }
};

第五十八题:寻找旋转排序数组中的最小值

升序数组(元素互不相同)经多次旋转后,找其中的最小元素,要求O(log n)时间。

C++ 复制代码
class Solution {
public:
    int findMin(vector<int>& nums) {
        int left=0,right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<nums[right])
            {
                right = mid;  // 最小值在左半部分(含mid)
            }
            else
            {
                left=mid+1;// 最小值在右半部分(不含mid)
            }
        }
        return nums[left];// 循环结束时 left == right,即为最小值下标
    }
};

第五十九题: 有效的括号

给定一个只含 (), [], {}的字符串,判断其是否有效。

有效条件:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 每个右括号都有对应的左括号。
C++ 复制代码
class Solution {
public:
    bool isValid(string s) {
         // 优化:奇数长度必然无效
        if (s.length() % 2 != 0) return false;

        stack<char> stk;
        unordered_map<char, char> pairs = {
            {')', '('},
            {'}', '{'},
            {']', '['}
        };

        for (char c : s) {
            // 如果是左括号,入栈
            if (c == '(' || c == '{' || c == '[') {
                stk.push(c);
            }
            // 如果是右括号
            else {
                // 栈空 或 栈顶不匹配 → 无效
                if (stk.empty() || stk.top() != pairs[c]) {
                    return false;
                }
                stk.pop(); // 匹配成功,弹出
            }
        }

        // 最终栈为空才有效
        return stk.empty();
    }
};

第六十题: 最小栈

设计一个支持 pushpoptop操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack类,包含初始化、压栈、弹栈、取栈顶、取最小值等方法。

C++ 复制代码
class MinStack {
private:
    stack<int> st;// 主栈
    stack<int> Minst;// 辅助栈,存每个位置对应的历史最小值
public:
    MinStack() {
        
    }
    
    void push(int val) {
        st.push(val);
        if(Minst.empty()||val<=Minst.top())  // 注意:<= 保证重复最小值也能正确记录
        {
            Minst.push(val);
        }
        else
        {
            Minst.push(Minst.top());//否则延续之前的
        }
    }
    
    void pop() {
        st.pop();
        Minst.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int getMin() {
        return Minst.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();
 */

第六十一题: 字符串解码

给定编码字符串 s,格式为 k[encoded_string],表示方括号内的字符串 encoded_string要重复 k次(k为正整数)。返回解码后的字符串。

假设输入总是有效,数字仅表示重复次数,原始数据不含数字,且方括号格式正确。

C++ 复制代码
class Solution {
public:
    string decodeString(string s) {
        stack<pair<string, int>> st;
        string res = "";
        int num = 0;

        for (char c : s) {
            if (isdigit(c)) {
                num = num * 10 + (c - '0');
            } else if (c == '[') {
                st.push({res, num});
                res = "";
                num = 0;
            } else if (c == ']') {
                auto [prev_str, count] = st.top(); st.pop();
                string repeated = "";
                for (int i = 0; i < count; i++) {
                    repeated += res;
                }
                res = prev_str + repeated;
            } else {
                res += c;
            }
        }

        return res;
    }
};

第六十二题:每日温度

给定整数数组 temperatures,表示每天的温度,返回一个数组 answer,其中 answer[i]表示第 i天之后下一个更高温度 出现在几天后;若之后不会升高,则为 0

C++ 复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n=temperatures.size();
        vector<int> answer(n,0);//初始化全为0
        stack<int> st;//存储索引,维持单调递减
        for(int i=0;i<n;i++)
        {
            // 当前温度比栈顶温度高,则栈顶找到了"下一个更高温度"
            while(!st.empty()&&temperatures[st.top()]<temperatures[i])
            {
                int prevIndex=st.top();
                st.pop();
                answer[prevIndex]=i-prevIndex;
            }
            st.push(i);
        }
        return answer;
    }
};

第六十三题:215. 数组中的第K个最大元素

给定整数数组 nums和整数 k,返回数组排序后第 k个最大的元素(非去重后的第 k个不同元素)。要求设计算法时间复杂度为 O(n)。

C++ 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int,vector<int>,greater<int>> minHeap;
        for(int num:nums)
        {
            minHeap.push(num);
            if(minHeap.size()>k)
            {
                minHeap.pop();
            }
        }
        return minHeap.top();
    }
};

第六十四题:347. 前K个高频元素

给定整数数组 nums和整数 k,返回数组中出现频率前 k高的元素(顺序任意)。

C++ 复制代码
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
       unordered_map<int,int> freqMap;
       for(int num:nums) 
       {
        freqMap[num]++;
       }
       priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> minHeap;
       for (auto& entry : freqMap) {
            int num = entry.first;
            int freq = entry.second;
            minHeap.push({freq, num});
            if (minHeap.size() > k) {
                minHeap.pop(); // 弹出频率最小的,保持堆大小为 k
            }
        }

        // 3. 提取堆中的元素(频率前 k 高)
        vector<int> result;
        while (!minHeap.empty()) {
            result.push_back(minHeap.top().second);
            minHeap.pop();
        }

        // 注意:题目允许任意顺序,所以不需要排序
        return result;
    }
};

贪心算法

第六十五题:买卖股票的最佳时机

题干 :给定数组 pricesprices[i]为第 i天股票价格),只能选一天买入、未来某天卖出,求最大利润(无利润返回0)。

C++ 复制代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int min_price=INT_MAX;
        int max_profit=0;
        for(int price:prices)
        {
            if(price<min_price)
            {
                min_price=price;
            }
            else
            {
                max_profit=max(max_profit,price-min_price);
            }
        }
        return max_profit;
    }
};

第六十六题:跳跃游戏

题干 :非负整数数组 nums(每个元素表示当前位置最大跳跃长度),初始在第一个下标,判断能否到达最后一个下标(能则返回 true,否则 false)。

C++ 复制代码
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int max_reach = 0;
        int n = nums.size();
        
        for (int i = 0; i < n; ++i) {
            // 如果当前位置已不可达,返回false
            if (i > max_reach) return false;
            
            // 更新最远可达位置
            max_reach = max(max_reach, i + nums[i]);
            
            // 如果已能到达或超过最后一个位置,提前返回true
            if (max_reach >= n - 1) return true;
        }
        
        // 理论上不会执行到这里,但语法安全
        return true;
    }
};

第六十七题:跳跃游戏 II

题干 :0索引整数数组 nums(每个元素 nums[i]表示从 i向后最大跳跃长度),初始在 0下标,返回到达最后一个下标(n-1)的最小跳跃次数(保证可到达)。

C++ 复制代码
class Solution {
public:
    int jump(vector<int>& nums) {
        int n = nums.size();
        int jumps = 0;
        int current_end = 0;
        int farthest = 0;
        
        for (int i = 0; i < n - 1; ++i) {  // 注意:不需要遍历最后一个元素
            farthest = max(farthest, i + nums[i]);
            
            if (i == current_end) {
                jumps++;
                current_end = farthest;
                
                if (current_end >= n - 1) {
                    break;  // 已经能到达终点,提前结束
                }
            }
        }
        
        return jumps;
    }
};

第六十八题:划分字母区间

题干 :字符串 s(仅小写字母),划分为尽可能多的片段,同一字母最多出现在一个片段中。返回各片段的长度列表(拼接后与原字符串一致)。

C++ 复制代码
class Solution {
public:
    vector<int> partitionLabels(string s) {
        // 1. 记录每个字符最后出现的位置
        unordered_map<char, int> lastPos;
        for (int i = 0; i < s.size(); i++) {
            lastPos[s[i]] = i;
        }

        vector<int> result;
        int start = 0;  // 当前片段的起始位置
        int end = 0;    // 当前片段的结束位置(动态扩展)

        // 2. 遍历字符串,贪心划分
        for (int i = 0; i < s.size(); i++) {
            end = max(end, lastPos[s[i]]);  // 扩展当前片段的右边界
            if (i == end) {                 // 当前位置是片段终点
                result.push_back(end - start + 1);  // 记录片段长度
                start = end + 1;             // 更新下一个片段的起始位置
            }
        }

        return result; 
    }
};

动态规划

第六十九题:爬楼梯

给定整数 n(需到达的楼顶台阶数,每次可爬1或2阶),求爬到楼顶的不同方法数

C++ 复制代码
class Solution {
public:
    int climbStairs(int n) {
        if(n<=2)
            return n;
        int a=1,b=2,c;
        for(int i=3;i<=n;i++)
        {
            c=a+b;
            a=b;
            b=c;
        }
        return b;
    }
};

第七十题: 杨辉三角

给定非负整数 numRows,生成杨辉三角的前 numRows(每个数等于其左上方和右上方数的和)。

C++ 复制代码
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> result;
        for(int i=0;i<numRows;i++)
        {
           vector<int> row(i + 1, 1); // 初始化全为1,长度为 i+1
            for (int j = 1; j < i; j++) { // 从第二个元素到倒数第二个元素
                row[j] = result[i-1][j-1] + result[i-1][j];
            }
            result.push_back(row);
        }
        return result;
    }
};

第七十一题: 打家劫舍

给定非负整数数组 nums(每间房屋的金额),相邻房屋不能同时偷窃,求不触发警报的最大偷窃金额

C++ 复制代码
class Solution {
public:
    int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==1) return nums[0];
        vector<int> f(n,0);
        vector<int> g(n,0);

        f[0]=nums[0];
        g[0]=0;
        for(int i=1;i<n;i++)
        {
            f[i]=g[i-1]+nums[i];
            g[i]=max(f[i-1],g[i-1]);
        }
        return  max(f[n-1],g[n-1]);
    }
};

第七十二题: 完全平方数

给定整数 n,求和为 n的完全平方数的最少数量(完全平方数:如1、4、9、16等,即整数的平方)。

C++ 复制代码
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1,INT_MAX);
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j*j<=i;j++)
            {
                dp[i]=min(dp[i],dp[i-j*j]+1);
            }
        }
        return dp[n];
    }
};

第七十三题:零钱兑换

给定硬币数组 coins(不同面额,数量无限)和总金额 amount,求凑出总金额的最少硬币个数(无法凑出则返回-1)。

C++ 复制代码
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // dp[i] 表示凑出金额 i 所需的最少硬币数
        vector<int> dp(amount + 1, amount + 1); // 初始化为一个不可能达到的值
        dp[0] = 0; // 凑出0元需要0个硬币

        // 遍历每个金额
        for (int i = 1; i <= amount; ++i) {
            // 尝试每一种硬币
            for (int coin : coins) {
                if (i >= coin) {
                    dp[i] = min(dp[i], dp[i - coin] + 1);
                }
            }
        }

        // 返回结果,若仍为初始值说明无法凑出
        return dp[amount] == amount + 1 ? -1 : dp[amount];
    }
};

第七十四题: 单词拆分

给定字符串 s和字符串列表 wordDict(字典,单词可重复使用),判断 s是否能被字典中一个或多个单词拼接而成

C++ 复制代码
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        int n = s.size();
        vector<bool> dp(n + 1, false);
        dp[0] = true; // 空字符串可拼接

        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && wordSet.find(s.substr(j, i - j)) != wordSet.end()) {
                    dp[i] = true;
                    break; // 找到一种拼法即可
                }
            }
        }

        return dp[n];
    }
};

第七十五题:最长递增子序列

给定整数数组 nums,找到其中最长严格递增子序列的长度(子序列不改变元素相对顺序,可删除元素)。

C++ 复制代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> dp(n,1);
        int ret=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(nums[j]<nums[i])
                    dp[i]=max(dp[i],dp[j]+1);
                ret=max(ret,dp[i]);
            }
        }
        return ret;
    }
};

第七十六题: 乘积最大子数组

给定整数数组 nums,找出乘积最大的非空连续子数组,并返回其乘积(子数组至少含一个数字)。

C++ 复制代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if (nums.empty()) return 0;

        int maxProd = nums[0];  // 全局最大乘积
        int currMax = nums[0];  // 以当前元素结尾的最大乘积
        int currMin = nums[0];  // 以当前元素结尾的最小乘积

        for (int i = 1; i < nums.size(); ++i) {
            // 保存上一轮的最大/最小值,用于当前计算
            int tempMax = currMax;
            int tempMin = currMin;

            // 当前位置的最大乘积 = max(当前数, 当前数×之前最大, 当前数×之前最小)
            currMax = max({nums[i], nums[i] * tempMax, nums[i] * tempMin});
            // 当前位置的最小乘积 = min(当前数, 当前数×之前最大, 当前数×之前最小)
            currMin = min({nums[i], nums[i] * tempMax, nums[i] * tempMin});

            // 更新全局最大值
            maxProd = max(maxProd, currMax);
        }

        return maxProd;
    }
};

第七十七题:分割等和子集

给定只包含正整数的非空数组 nums,判断是否可将其分割成两个元素和相等的子集

C++ 复制代码
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for (int num : nums) sum += num;
        
        // 总和为奇数,无法平分
        if (sum % 2 != 0) return false;
        
        int target = sum / 2;
        vector<bool> dp(target + 1, false);
        dp[0] = true; // 和为0总是可以达成(空集)
        
        for (int num : nums) {
            // 从后往前遍历,避免重复使用当前数字
            for (int j = target; j >= num; j--) {
                dp[j] = dp[j] || dp[j - num];
            }
        }
        
        return dp[target];
    }
};

多维动态规划

第七十八题:不同路径

题干:机器人从 m×n网格左上角出发,每次仅能向下或向右移动一步,求到达右下角的不同路径总数。

C++ 复制代码
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m, vector<int>(n, 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];
    }
};

第七十九题: 最小路径和

题干 :给定含非负整数的 m×n网格 grid,机器人每次只能向下或向右移动一步,求从左上角到右下角的路径中,数字总和最小的路径的和。

C++ 复制代码
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<int> dp(n, 0);
        
        // 初始化第一行
        dp[0] = grid[0][0];
        for (int j = 1; j < n; j++) {
            dp[j] = dp[j-1] + grid[0][j];
        }
        
        // 逐行更新
        for (int i = 1; i < m; i++) {
            dp[0] += grid[i][0];  // 第一列只能从上往下
            for (int j = 1; j < n; j++) {
                dp[j] = min(dp[j], dp[j-1]) + grid[i][j];
            }
        }
        
        return dp[n-1];
    }
};

第八十题:最长回文子串

题干:给定字符串 s,找到其中最长的回文子串(回文:正读和反读一致的字符串)。

C++ 复制代码
class Solution {
public:
    string longestPalindrome(string s) {
        int begin=0,len=0,n=s.size();
        for(int i=0;i<n;i++)
        {
            int left=i,right=i;
            while(left>=0&&right<n&&s[left]==s[right])
            {
                left--;
                right++;
            }
            if(right-left-1>len)
            {
                begin=left+1;
                len=right-left-1;
            }
             left=i,right=i+1;
            while(left>=0&&right<n&&s[left]==s[right])
            {
                left--;
                right++;
            }
            if(right-left-1>len)
            {
                begin=left+1;
                len=right-left-1;
            }
        }
        return s.substr(begin,len);
    }
};

第八十一题:最长公共子序列

题干 :给定两个字符串 text1text2,返回它们的最长公共子序列的长度。子序列定义为原字符串不改变字符相对顺序、删除部分字符(或不删)得到的新字符串;公共子序列是两个字符串共有的子序列。

C++ 复制代码
class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m=text1.size();
        int n=text2.size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
            {
                if (text1[i - 1] == text2[j - 1]) {
                    // 当前字符相等,LCS 长度加 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[m][n];
    }
};

第八十二题:编辑距离

题干 :给定两个单词 word1word2,求将 word1转换成 word2所需的最少操作数。允许的操作:插入一个字符、删除一个字符、替换一个字符。

C++ 复制代码
class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.length(), n = word2.length();
        vector<int> dp(n + 1, 0);
        
        // 初始化第一行
        for (int j = 0; j <= n; j++) dp[j] = j;
        
        for (int i = 1; i <= m; i++) {
            int prev = dp[0];  // 保存左上角的值
            dp[0] = i;  // 当前行的第一列
            
            for (int j = 1; j <= n; j++) {
                int temp = dp[j];  // 保存当前值,作为下一次的prev
                
                if (word1[i-1] == word2[j-1]) {
                    dp[j] = prev;
                } else {
                    dp[j] = min({dp[j],     // 删除(即上一行的值)
                                 dp[j-1],   // 插入(即当前行前一列的值)
                                 prev       // 替换(即左上角的值)
                                }) + 1;
                }
                prev = temp;
            }
        }
        
        return dp[n];
    }
};

一些技巧题

第八十三题: 只出现一次的数字

题干 :给定非空整数数组 nums(除一个元素仅出现一次外,其余元素均出现两次),找出该只出现一次的元素。要求线性时间复杂度、常量额外空间。

C++ 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
       int result=0;
       for(int num:nums) 
       {
        result^=num;
       }
       return result;
    }
};

第八十四题:颜色分类

题干 :给定含 n个元素的数组 nums(元素为 012,分别表示红、白、蓝),原地 排序使相同颜色相邻且按红(0)、白(1)、蓝(2)顺序排列。不允许使用库内置 sort函数,进阶要求常数空间、一趟扫描。

C++ 复制代码
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int left = 0;      // 指向下一个 0 的位置
        int right = nums.size() - 1; // 指向下一个 2 的位置
        int i = 0;         // 当前处理位置

        while (i <= right) {
            if (nums[i] == 0) {
                swap(nums[i], nums[left]);
                left++;
                i++; // 交换后 i 位置一定是 1 或刚放来的 0,安全前进
            } else if (nums[i] == 1) {
                i++; // 中间区域,无需交换
            } else { // nums[i] == 2
                swap(nums[i], nums[right]);
                right--; // 交换后 i 位置的值未检查,不能 i++
            }
        }
    }
};
相关推荐
cccyi72 小时前
【C++ 脚手架】cpp-httplib 与 websocketpp 库的介绍与使用
c++·websocket·http
故事和你912 小时前
洛谷-入门6-函数与结构体
开发语言·数据结构·c++·算法·动态规划
老四啊laosi2 小时前
[C++进阶] 21. 红黑树
c++·红黑树
像素猎人2 小时前
蓝桥杯OJ716【限定第一步和最后一步爬台阶的经典例题】【动态规划】
c++·算法·动态规划
Q741_1472 小时前
每日一题 力扣 3474. 字典序最小的生成字符串 贪心 字符串 C++ 题解
c++·算法·leetcode·贪心
人道领域2 小时前
LeetCode【刷题日记】:螺旋矩阵逆向全过程,边界缩进优化
算法·leetcode·矩阵
小此方2 小时前
Re:从零开始的 C++ STL篇(九)AVL树太“较真”,红黑树更“现实”:一文讲透工程中的平衡之道
开发语言·数据结构·c++·算法·stl
im_AMBER2 小时前
Leetcode 150 最小路径和 | 最长回文子串
数据结构·算法·leetcode
进击的荆棘2 小时前
C++起始之路——二叉搜索树
数据结构·c++·stl