leetcode代码随想录数组篇

704.二分查找:

由于数组是严格升序 (无重复或有重复均可,但有序),我们可以使用二分查找高效定位目标值。

核心思想:

  • 设置两个指针 leftright,分别指向数组的起始和末尾。

  • 每次取中间位置 mid = left + (right - left) / 2(避免整型溢出)。

  • 比较 nums[mid]target

    • 若相等 → 找到目标,返回 mid
    • nums[mid] > target → 目标在左半部分,令 right = mid - 1
    • nums[mid] < target → 目标在右半部分,令 left = mid + 1
  • left > right 时,说明未找到,返回 -1

    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){
    right = mid -1;
    }else if(nums[mid]<target){
    left = mid+1;
    }else return mid;
    }
    return -1;
    }
    };

27.移除数组:

解题思路:双指针(快慢指针)

这是一个经典的 原地修改数组 问题,非常适合使用 双指针技巧

核心思想:

  • 使用两个指针:

    • 快指针 fast:遍历整个数组,逐个检查每个元素。
    • 慢指针 slow:指向"下一个应该保留的位置"。
  • nums[fast] != val 时,说明这个元素需要保留,就把它复制到 nums[slow],然后 slow++

  • 最终,slow 的值就是新数组的长度。

    class Solution {
    public:
    int removeElement(vector<int>& nums, int val) {
    int slow = 0;
    for (int fast = 0; fast < nums.size(); ++fast) {
    if (nums[fast] != val) {
    nums[slow++] = nums[fast];
    }
    }
    return slow;
    }
    };

977.有序数组的平方

解题思路:双指针(从两端向中间)

关键观察:

  • 原数组是升序排列,但包含负数。
  • 平方后,最大值一定出现在两端(最左或最右),因为绝对值最大的数在两端。
  • 因此,我们可以使用双指针分别指向首尾 ,比较它们的平方,从结果数组的末尾开始填入较大者

算法步骤:

  1. 创建一个长度为 n 的结果数组 result
  2. 初始化:
    • 左指针 i = 0
    • 右指针 j = n - 1
    • 填充位置 k = n - 1(从后往前填)
  3. i <= j 时循环:
    • 如果 nums[i]^2 <= nums[j]^2,说明右边更大,把 nums[j]^2 放入 result[k]j--
    • 否则,把 nums[i]^2 放入 result[k]i++
    • 每次操作后 k--
  4. 返回 result

💡 这种"逆向填充"保证了结果数组天然有序(从小到大),无需额外排序。

209. 长度最小的子数组

解题思路:滑动窗口(双指针)

为什么能用滑动窗口?

  • 所有元素 > 0 → 窗口扩大时,和单调递增;窗口缩小时,和单调递减。
  • 因此,一旦当前窗口和 ≥ s,就可以尝试 收缩左边界 来寻找更短的满足条件的子数组。

算法流程:

  1. 初始化:

    • i = 0:窗口左边界
    • sum = 0:当前窗口和
    • result = INT32_MAX:记录最小长度
  2. 右指针 j 从 0 遍历到末尾:

    • nums[j] 加入窗口(sum += nums[j]
    • 只要 sum >= s,就不断收缩左边界while 循环):
      • 计算当前窗口长度 j - i + 1
      • 更新 result
      • 减去 nums[i] 并右移 i
  3. 最终若 result 仍为初始值,说明无解,返回 0;否则返回 result

    class Solution {
    public:
    int minSubArrayLen(int target, vector<int>& nums) {
    int result = INT32_MAX;
    int sum = 0;
    int sublen = 0;
    int i = 0;
    for(int j = 0; j < nums.size();j++){
    sum += nums[j];
    while(sum>=target){
    sublen = j-i+1;
    result = result < sublen ? result:sublen;
    sum -=nums[i++];
    }
    }
    return result == INT32_MAX?0:result;
    }
    };

59螺旋矩阵

给定一个正整数 n,生成一个 n × n 的矩阵,使得矩阵中的元素从 1 到 n²顺时针螺旋顺序 填充。

核心观察:螺旋 = 一圈一圈地填

想象你在画一个正方形的"回"字:

  • 先画最外层(第1圈),
  • 再画里面一层(第2圈),
  • 直到中心(如果 n 是奇数,中心剩一个格子)。

每一圈都由 四条边 组成:

  1. 上边:从左 → 右
  2. 右边:从上 → 下
  3. 下边:从右 → 左
  4. 左边:从下 → 上

✅ 关键技巧:每条边都采用"左闭右开"区间(即包含起点,不包含终点),这样四个角不会重复填写!

算法设计要点

1. 圈数

  • 总共要循环 loop = n / 2 圈。
  • 例如:n=5 → loop=2(填两圈),中间 (2,2) 单独处理。

2. 每圈的起始位置

  • 第1圈:从 (0, 0) 开始
  • 第2圈:从 (1, 1) 开始
  • 第k圈:从 (k-1, k-1) 开始 → 用 startx, starty 记录

3. 每条边的边界控制

  • offset 控制"右边界"或"下边界"。
  • 第1圈:右边到 n - 1(即 n - offset,offset=1)
  • 第2圈:右边到 n - 2(offset=2)
  • 所以每次循环后 offset++

4. 填充顺序(左闭右开)

复制代码
上边:(startx, starty) → (startx, n - offset - 1)
右边:(startx, n - offset) → (n - offset - 1, n - offset)
下边:(n - offset, n - offset) → (n - offset, starty + 1)
左边:(n - offset, starty) → (startx + 1, starty)

5. 奇数中心处理

  • 如果 n 是奇数,最中心 (mid, mid) 没被填,最后填入 count(此时 count == n²)

💡 为什么用"左闭右开"?

避免四个角被重复填写!

比如 n=3:

  • 如果上边填 [0][0][0][2](包含3),
  • 右边又从 [0][2] 开始填,
  • 那么 [0][2] 就被写了两次!

而"左闭右开":

  • 上边填 [0][0], [0][1](停在 [0][2] 前)

  • 右边从 [0][2] 开始填 → 完美衔接,无重叠!

    vector<vector<int>> generateMatrix(int n) {
    vector<vector<int>> res(n, vector<int>(n, 0));
    int startx = 0, starty = 0; // 每圈起点
    int loop = n / 2; // 圈数
    int mid = n / 2; // 中心点(n为奇数时用)
    int count = 1; // 当前要填的数字
    int offset = 1; // 控制边界收缩

    复制代码
      while (loop--) {
          int i = startx, j = starty;
    
          // 1. 上行:左 → 右(左闭右开)
          for (; j < n - offset; j++) res[i][j] = count++;
    
          // 2. 右列:上 → 下(左闭右开)
          for (; i < n - offset; i++) res[i][j] = count++;
    
          // 3. 下行:右 → 左(左闭右开)
          for (; j > starty; j--)     res[i][j] = count++;
    
          // 4. 左列:下 → 上(左闭右开)
          for (; i > startx; i--)     res[i][j] = count++;
    
          // 进入内圈
          startx++;
          starty++;
          offset++;
      }
    
      // 处理奇数中心
      if (n % 2 == 1) res[mid][mid] = count;
    
      return res;

    }

58. 区间和

复制代码
#include<iostream>
#include<vector>

using namespace std;

int main(){
    int n, a,b;
    cin>>n;
    vector<int>res(n,0);
    vector<int>p(n,0);

    int prevec = 0; 
    for(int i = 0; i<n ; i++){
        cin>>res[i];
        prevec += res[i];
        p[i] = prevec;
    }
    while(cin>>a>>b){
        int sum ;
        if(a == 0) sum = p[b];
        else sum = p[b]-p[a-1];
        cout<<sum<<endl;
    }

}

44. 开发商购买土地

一、题目理解

  • 有一个 n × m 的网格,每个格子有权值(土地价值)。
  • 只能切一刀 :要么横向切 (水平方向,分成上下两块),要么纵向切(垂直方向,分成左右两块)。
  • 切完后,两部分都必须非空(即不能在最外侧切)。
  • 目标:使两部分的总价值之差的绝对值最小

解题思路

  1. 先算出整个网格的总和 sum

  2. 尝试所有合法的横向切割位置 (共 n-1 种):

    • 上半部分和 = 前 k 行之和
    • 下半部分和 = sum − 上半部分和
    • 差 = |sum − 2 × 上半部分和|
  3. 尝试所有合法的纵向切割位置 (共 m-1 种):

    • 左半部分和 = 前 l 列之和
    • 右半部分和 = sum − 左半部分和
    • 差 = |sum − 2 × 左半部分和|
  4. 取所有可能中的最小差。

    #include<iostream>
    #include<vector>
    #include<climits>

    using namespace std;
    int main(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>>vec(n,vector<int>(m,0));
    int sum =0;
    for(int i = 0; i<n;i++){
    for(int j =0; j<m;j++){
    cin>>vec[i][j];
    sum+=vec[i][j];
    }
    }
    //计算横向
    vector<int>vectical(m,0);
    for(int i =0;i<m;i++){
    for(int j =0; j<n;j++){
    vectical[i]+=vec[i][j];
    }
    }
    //计算纵向
    vector<int>horizontal(n,0);
    for(int j =0; j<n;j++){
    for(int i =0;i<m;i++){
    horizontal[j]+=vec[i][j];
    }
    }

    复制代码
     int result =INT_MAX;
     int vectcutsum=0;
     for(int i =0;i<m;i++){
         vectcutsum+=vectical[i];
         result=min(result,abs(sum-vectcutsum-vectcutsum));
     }
    
     int horcutsum=0;
     for(int j =0;j<n;j++){
         horcutsum+=horizontal[j];
         result=min(result,abs(sum-horcutsum-horcutsum));
     }
    
     return result;

    }

相关推荐
董董灿是个攻城狮4 小时前
AI视觉连载8:传统 CV 之边缘检测
算法
AI软著研究员11 小时前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish11 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱12 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者1 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮1 天前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者1 天前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考1 天前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
HXhlx1 天前
CART决策树基本原理
算法·机器学习
Wect1 天前
LeetCode 210. 课程表 II 题解:Kahn算法+DFS 双解法精讲
前端·算法·typescript