【算法打卡day19(2026-03-11 周三)算法:打家劫舍-DP,双指针,二分查找,滑动窗口,方向控制,前缀和 】8个题

- 第 185 篇 -
Date: 2026 - 03- 11 | 周三
Author: 郑龙浩(仟墨)
算法:打家劫舍-DP,双指针,二分查找,滑动窗口,方向控制,前缀和

2026-03-11 | 算法打卡 day19

所有题:

  1. 力扣704-二分查找
  2. 力扣27-移除元素
  3. 力扣977-有序数组的平方
  4. 力扣209-长度最小的子数组(滑动窗口)
  5. 力扣198-打家劫舍
  6. 力扣213-打家劫舍II.cpp
  7. 力扣59-螺旋矩阵
  8. 卡网58-区间和

文章目录

按照「代码随想录」从第1个开始刷

今日遇到的算法:

  • 二分查找
  • 打家劫舍 - DP算法
  • 滑动窗口
  • 双指针
  • 前缀和

1-力扣704-二分查找

好久没做二分查找的题了,今天回顾一下基础的代码吧

「简单」

【题目】

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1

你必须编写一个具有 O(log n) 时间复杂度的算法。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

【思路】

就是最基本的代码

【代码】

cpp 复制代码
/* 【1-力扣704-二分查找】
Author:郑龙浩
Date:2026-03-11*/
#include "bits/stdc++.h"
using namespace std;
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int len = nums.size();
        int left = 0, right = len - 1, mid;
        while (left <= right) {
            mid = (left + right) / 2;
            if (target < nums[mid]) right = mid - 1;
            else if (target > nums[mid]) left = mid + 1;
            else return mid;
        }
        return -1; // 如果找不到,就返回-1
    }
};
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    return 0;
}

2-力扣27-移除元素

【题目】

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

  • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
  • 返回 k

用户评测:

评测机将使用以下代码测试您的解决方案:

cpp 复制代码
int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
                            // 它以不等于 val 的值排序。

int k = removeElement(nums, val); // 调用你的实现

assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有的断言都通过,你的解决方案将会 通过

示例 1:

输入: ums = [3,2,2,3], val = 3
输出: 2, nums = [2,2,_,_]

**解释:**你的函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。

你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

示例 2:

输入: nums = [0,1,2,2,3,0,4,2], val = 2
输出: 5, nums = [0,1,4,0,3,_,_,_]

**解释:**你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。

注意这五个元素可以任意顺序返回。

你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

【思路】双指针法 + 二分查找

「暴力解法」就是从下标0开始遍历,遇到val就将val后面的所有的元素往前移动1格,直到遍历到最后

这道题不应该用暴力,而应该用双指针或者二分查找

【代码1】双指针法

设置两个指针「快指针」和「慢指针」

  • 快指针:通过 for (int fastIndex : nums)隐式定义,遍历数组中的每个元素
    • 目的:找到不等于val的元素,将其复制到慢指针位置
  • 慢指针:指向需要更改的元素(除了慢指针和快指针指向同一个元素时相当于自己给自己赋值)
    • 指向应该存放有效元素的位置
cpp 复制代码
/* 2-力扣27-移除元素
方法2:双指针法
Author:郑龙浩
Date:2026-03-11*/
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int len = nums.size();  // 获取数组原始长度
        int slowIndex = 0;      // 慢指针:指向下一个应该存放有效元素的位置
        
        // 遍历数组中的每个元素
        for (int fastIndex : nums) {
            // 当前元素不是要移除的值
            if (fastIndex != val) {  
                // 将非val元素放到慢指针位置,然后慢指针前进
                nums[slowIndex++] = fastIndex;
            }
            // 如果当前元素等于val,跳过不处理,只移动快指针(隐式移动)
        }
        
        // 慢指针最后的位置就是移除val后数组的有效长度
        return slowIndex;
    }
};

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    return 0;
}

【代码2】二分查找法(用C++的二分函数 + copy函数整体复制)

这道题可以用二分查找去做 + copy 复制后面的元素到前面

  1. 对所有元素进行排序
    • 这样做的目的是将 所有的 val 都放到一起,方便统一val的数量
    • 题目中没有要求nums中的数据是不能更改顺序的,所以可以排序,如果要求了,就不可以了
  2. 找到第一个val的位置first 和 找到最后一个val的位置的后面last
    • end - first 就是val数量
  3. 然后计算出去掉val后的元素数量 nums.size() - (last - first)
  4. 然后将[last, end)的所有元素复制到以first开头的位置,也就是复制到第一个val的位置将其覆盖
cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        auto begin = nums.begin(), end = nums.end();
        sort(begin, end); // 先给数据进行排序
        auto first = lower_bound(begin, end, val); // 从找到[begin, end)中,第一个val的位置
        auto last = upper_bound(begin, end, val); // 从找到[begin, end)中,最后一个val位置的后面1个位置(不是最后一个val本身的位置,而是在后面)
        // 移除val元素后的数量
        int cnt = nums.size() - (last - first); // last - first 计算的是val数量,nums.size() - (last - first) 计算的是去掉所有val后,剩余的元素个数
        
        // copy作用,将区间 [last, end) 的所有的元素 copy 到 first 为起始的位置
        copy(last, end, first);
        return cnt;
    }
};

3-力扣977-有序数组的平方

【题目】

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入: nums = [-4,-1,0,3,10]
输出: [0,1,9,16,100]
解释: 平方后,数组变为 [16,1,0,9,100]

排序后,数组变为 [0,1,9,16,100]

示例 2:

输入: nums = [-7,-3,2,3,11]
输出: [4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

【思路】

简单的排序,直接sor

【代码】

cpp 复制代码
/* 3-力扣977-有序数组的平方
算法:没算法,就是sort
Author:郑龙浩
Date:2026-03-11*/
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len = nums.size();
        for (int i = 0; i < len; i++) {
            nums[i] *= nums[i];
        }
        sort(nums.begin(), nums.end());
        return nums;
    }
};
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    return 0;
}

4-力扣209-长度最小的子数组(滑动窗口)

该题需要复习,在看题解之间我并没有想出「滑动窗口」

其实我还有一个思路就是「前缀和」,等会用「滑动窗口」写完后,我再用「前缀和」做一遍

但实际上会了「滑动窗口」后,应该不会再用「前缀和」去做了,因为非常麻烦

【题目】

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0

示例 1:

复制代码
**输入:** `target = 7, nums = [2,3,1,2,4,3]`

**输出:**2

**解释:**子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

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

示例 3:

输入: target = 11, nums = [1,1,1,1,1,1,1,1]

**输出:**0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 104
【思路】
【代码】
cpp 复制代码
/* 4-力扣209-长度最小的子数组
算法:滑动窗口
Author:郑龙浩
Date:2026-03-11*/
#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len = nums.size();
        int left = 0; // 窗口的开始下标(窗口左边框)
        int sum = 0, winLen = 0; // 窗口的和,窗口的长度
        int minLen = INT_MAX; // 最短窗口
        for (int right = 0; right < len; right++) { // right 表示窗口的结束下标(窗口右边框)
            sum += nums[right]; // 加上窗口的最后一个元素
            winLen ++; // 窗口长度++
            // 只要窗口和 >= target,就将窗口左边框向右移动1个(left++),且窗口和sum减去一个左边框 nums[left]& 窗口长--(winLen--),如果此时窗口和sum依然>=target,那么就继续循环,否则直接结束循环
            while (sum >= target) {
                minLen = min(minLen, winLen); // 留下最小的窗口长度(不要将这一行写到后面的代码中,因为进行sum -= nums[left++] 和 winLen--后并不能保证当时的是 sum >= target 的,而写在while循环体的开头,就可以保证此时的一定是sum >= target的,winLen也是可以保证是正确的长度
                sum -= nums[left++]; // 窗口和去掉一个左边框 & 左边框向右移动
                winLen--; // 窗口长度--
            }
        }
        return minLen == INT_MAX ? 0 : minLen; // 如果minLen == INT_MAX的话,就说明minLen没有进行过修改,那么不存在符合条件的子数组,根据题目要求,应该返回0
    }
};
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    return 0;
}

5-力扣198-打家劫舍

【题目】

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]

**输出:**4

**解释:**偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。

偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入: [2,7,9,3,1]

**输出:**12

**解释:**偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。

偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400
【思路】

这道题算法是DP

为什么确定是DP呢?

因为
这道题我认为最关键思考点是:当前房屋偷或不偷取决于当前房屋的「前面第1个房屋」或「前面第2个房屋」是否被偷

所以当前状态和前面的状态会有一种依赖关系,这种依赖关系就是DP的【递推公式】

【代码】
cpp 复制代码
/* 【1-力扣198-打家劫舍】

算法:DP - 打家劫舍

Author:郑龙浩
Date:2026-03-11
用时:没记录

这样的写法是效率是最优的,但是空间上稍有浪费,其实可以去掉dp数组,直接用变量代替中间的变化过程
*/

#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    int rob(vector<int>& nums) {
        int len = nums.size(); // 房屋数量

        // 我第一次写这道题的时候忘记判断特殊情况了
        if (len == 1) return nums[0]; // 如果不写的话,当len只有1的时候,执行dp[1] = max(nums[0], nums[1])是会报错的

        vector <int> dp(len, 0); // dp[i]含义:0~i个房屋(包含i)偷窃到的最高金额
        // 初始化
        dp[0] = nums[0]; // 0~0只有房间0一个,所以必须要偷
        dp[1] = max(nums[0], nums[1]); // 0~1有房间0和房间1两个房间,因为不能连着偷,所以只能选择两者最大的一个去偷

        for (int i = 2; i < len; i++) {
            // i - 1房间 与 i房间,只能选择偷其中一个,如果投了房间i,就不能偷房间i - 1,如果偷了房间i-1,就不能偷房间i
            // dp[i - 2] + nums[i]: 偷房间i,且将0~i-2之间偷窃到的最大价值 + 房间i最大价值,得出偷0~i房间的最大价值
            // dp[i - 1]: 不偷房间i,此时dp[i - 1]就是不偷的时候,可以偷到i - 1房间
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }

        return dp[len - 1];
    }
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    return 0;
}

6-力扣213-打家劫舍II.cpp

【题目】

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

**输入:**nums = [2,3,2]

**输出:**3

**解释:**你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

**输入:**nums = [1,2,3,1]

**输出:**4

**解释:**你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。

偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:

**输入:**nums = [1,2,3]

**输出:**3

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000
【思路】

这道题和「1-力扣198-打家劫舍」非常的像

但是多了一个条件,就是房屋是连着的,也就是房屋0和房屋len-1是相邻的,那么在第一版打家劫舍中房屋0和房屋len-1可以一起偷,在这一版中就不可以一起偷了,而是只能选择偷其中一个

【最大的疑惑】练成环形之后最大的难点在于,不知道如何初始化 ,起始点dp[0]dp[1]到底该如何确定呢

Eg: nums[5] = {2,7,9,3,1};

针对这三种情况的思路描述:

  1. 去掉首尾房屋,只考虑中间房屋[7, 9 3]

    • 对应数组下标1到3,即nums[1]=7, nums[2]=9, nums[3]=3,只计算这三个房屋的最大收益。
  2. 去掉首房屋,考虑其他房屋[7, 9, 3, 1]

    • 对应数组下标1到4,即nums[1]=7, nums[2]=9, nums[3]=3, nums[4]=1,计算这四个房屋的最大收益。
  3. 去掉尾房屋,考虑其他房屋[2, 7, 9, 3]

    • 对应数组下标0到3,即nums[0]=2, nums[1]=7, nums[2]=9, nums[3]=3,计算这四个房屋的最大收益。

在实际代码实现中,由于情况1是情况2和情况3的交集(即中间房屋部分),因此只需计算情况2和情况3的最大值即可覆盖所有可能的最优解

【我的疑惑1】

第一次写的错误的代码

cpp 复制代码
class Solution {
public:
    int rob(vector<int>& nums) {
        int len = nums.size();
        if (len == 1) return nums[0];
        vector <int> dp(len, 0);

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

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

        // 因为是循环的,房屋len - 1下一个就是房屋0,所以单独再计算一次房屋0
        dp[0] = max(dp[len - 2] + nums[0], dp[len - 1]);
        // 因为是循环的,从房屋len - 1,也能偷到房屋1,所以也要再单独计算一次房屋1
        dp[1] = max(dp[len - 1] + nums[1], dp[0]);

        return dp[len - 1];
    }
};

我之前的理解,在偷完之后,再判断房屋0能否偷,毕竟有递推公式的话,就意味着dp[0] = max(dp[len - 2) + nums[0], dp[len - 1)

第一次的dp[0] = nums[0]只是因为没有前面的dp[len - 2] 和 dp[len -1]的数据而做出的选择

上面是错误理解

现在的理解:

房屋0被偷过后,是不可以再偷的了,所以不需要再根据len - 1和len-2去更新dp[0]偷第二次房屋0

而是只需要避免房屋0和房屋len-1被同时偷就可以了

【我的疑惑2】

还有一个疑惑

如果偷的起始点是3而不是0在本题中也是可以的

那么从3开始偷和从0开始,最终的最大金额为什么还会是一样的呢

假设从3开始偷了,因为是环形dp[0]时,dp[0]就不可能是之前从0开始偷的时候的nums[0]了,我不理解

或者这么说,既然从0开始和从1开始的结果是不一样的

那么从2开始的结果怎么保证是和前面一样的呢

假设说我计算的是从3开始偷,和从4开始偷,然后也是保证最后一个房屋不偷这种情况,取两者最大值

那么算出来的最大值和之前分别从0开始偷和从1开始偷算出来的最大值是否是一样的

,你无需管复不复杂,我只是想知道一个结论

不要啰嗦,语言简洁,逻辑清晰

【AI回复】

结论:是一样的。理由如下:

  1. 数学上等价

    • 从0开始(去掉尾)和从1开始(去掉首)这两个子问题已经覆盖了所有可能的最优解

    • 最优解必然属于以下两类之一:不偷房屋0,或不偷房屋len-1

    • 从2、3、4...等中间位置开始,其最优解必然包含在以上两类中

  2. 反证法

    • 假设存在一个从k开始的最优解(k≠0且k≠1)

    • 这个解要么不偷房屋0,要么不偷房屋len-1

    • 如果不偷房屋0,它被"从1开始"的子问题包含

    • 如果不偷房屋len-1,它被"从0开始"的子问题包含

    • 因此这个解已被我们的两种分法包含

  3. 实际例子

    • 对于[2,7,9,3,1]

    • 从0开始(去掉尾):考虑[2,7,9,3],最优为7+3=10

    • 从1开始(去掉首):考虑[7,9,3,1],最优为9+1=10

    • 从2开始(考虑[9,3,1,2]):最优为9+2=11,但这个方案同时偷了房屋2(9)和房屋0(2),在环形中这是相邻的,不允许

    • 从3开始(考虑[3,1,2,7]):最优为7+3=10,与从0开始或从1开始一致

最终结论:从任意位置开始计算,只要保证不偷与其相邻的房屋,最终得到的最优解都与从0开始、从1开始两种分法得到的最优解相同。选择0和1作为分界点是因为这是最简单的分法,能保证完备性。

【代码】
cpp 复制代码
/* 【2-力扣213-打家劫舍II】

算法:DP - 打家劫舍

Author:郑龙浩
Date:2026-03-11
用时:


*/

#include "bits/stdc++.h"
using namespace std;
/* 错误代码
class Solution {
public:
    int rob(vector<int>& nums) {
        int len = nums.size();
        if (len == 1) return nums[0];
        vector <int> dp(len, 0);

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

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

        // 因为是循环的,房屋len - 1下一个就是房屋0,所以单独再计算一次房屋0
        dp[0] = max(dp[len - 2] + nums[0], dp[len - 1]);
        // 因为是循环的,从房屋len - 1,也能偷到房屋1,所以也要再单独计算一次房屋1
        dp[1] = max(dp[len - 1] + nums[1], dp[0]);

        return dp[len - 1];
    }
};
*/
class Solution {
public:
    int rob(vector<int>& nums) {
        int len = nums.size();

        if (len == 1) return nums[0]; // 如果只有1个房屋的话,执行后面的代码会越界访问
        if (len == 2) return max(nums[0], nums[1]); // 如果只有两个房屋的话,也会出现越界访问 (如果len是2,那么只有房屋0和1,那么函数判断的就是0~0和1~1,后面再执行start + 1的时候会越界访问的,所以干脆就在前面提前将这种情况避免)
        int ans1 = robRange(nums, 0, len - 2);
        int ans2 = robRange(nums, 1, len - 1);
        return max(ans1, ans2);
    }
    int robRange(vector <int>& nums, int start, int end) {
        int len = nums.size();
        vector <int> dp(len, 0);
        dp[start] = nums[start]; // 只有1个房屋的话,只能偷这一个
        dp[start + 1] = max(nums[start], nums[start + 1]); // 2个,偷最大

        for (int i = start + 2; i <= end; i++) {
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[end];
    }
};
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    return 0;
}

7-力扣59-螺旋矩阵

【题目】

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

示例 1:

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

示例 2:

**输入:**n = 1
输出: [[1]]

提示:

  • 1 <= n <= 20
【思路】

我印象中之前做过一个题叫做「蛇形走位」,类似于当前的这种题

我的想法是:

  1. 创建一个表示"右、下、左、上"四个方向的数组 directions

  2. 从起始位置 (0, 0)开始,初始方向为"右"。

  3. 进行 n * n次循环。每次循环,在当前位置填入数字,然后根据direction计算出下一个方向(方向保持不变,直到遇到边界或者曾经填充过这个位置才会改变f

  4. 如果下一个位置超出了矩阵边界,或已经被填充过,就切换到 directions中的下一个方向(方向索引循环递增),然后重新计算下一个位置。

  5. 更新当前位置,继续循环,直到矩阵完全填充。

方向切换机制是:当需要转向时,就取 directions中的下一个方向;当执行到"上"方向后再次需要转向时,就回到"右"重新开始,如此循环。

【代码】
cpp 复制代码
/* 7-力扣59-螺旋矩阵

算法:DP - 打家劫舍
修改小毛病的时间太长了
Author:郑龙浩
Date:2026-03-11
用时:24min
*/

#include "bits/stdc++.h"
using namespace std;

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int direction[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // direction: 右->下->左->上
        vector <vector <int>> nums(n, vector <int> (n, 0)); // 初始化为0,没有填充的位置,就赋为0
        int N = n * n;
        int x = 0, y = 0;
        int nextX, nextY, next = 0;
        for (int i = 1; i <= N; i++) {
            
            nums[x][y] = i;
            // 预计的下一个位置
            nextX = x + direction[next][0];
            nextY = y + direction[next][1];
            // 如果下一个位置已经越界或者访问过,就切换方向
            if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= n // 检查下一个位置是否越界,如果下一个位置是越界的,就切换方向
                                        || nums[nextX][nextY] != 0) { // 检查下一个位置是否访问过,如果下一个位置是访问过的,就切换方向
                next = (next + 1) % 4; // 切换方向 (让位置在 0 ~ 3 之间循环,3后面就是1)
                nextX = x + direction[next][0];
                nextY = y + direction[next][1];
            }
            x = nextX, y = nextY; // 将(x, y)更新为下一个要填充的位置
        }
        return nums;
    }
};

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    Solution sol;
    auto nums = sol.generateMatrix(3);
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            cout << nums[i][j] << ' ';
        }
        cout << '\n';
    }
    return 0;
}

8-卡码网58-区间和

【题目】

题目描述

给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。

输入描述

第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。

输出描述

输出每个指定区间内元素的总和。

输入示例

复制代码
5
1
2
3
4
5
0 1
1 3

输出示例

复制代码
3
9

提示信息

数据范围:

0 < n <= 100000

【思路】

这就是一个很普通的前缀和

但是对于我来说有点困难的是

输入的时候没有说明输入了多少个区间,也就是没有说明输入了几次 a b

题目没有明确告知有多少个查询。在 C++ 中,可以利用 while (cin >> a >> b)这种写法,它会一直读取直到输入结束(文件结束符 EOF),这就完美解决了未知查询次数的问题

【代码】
cpp 复制代码
/* 8-卡码网58-区间和

算法:前缀和(很普通的那种的前缀和)
Author:郑龙浩
Date:2026-03-11
用时:7min
*/

#include "bits/stdc++.h"
using namespace std;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    int N; cin >> N;
    vector <int> nums(N, 0);
    cin >> nums[0];
    for (int i = 1; i < N; i++) {
        cin >> nums[i];
        nums[i] += nums[i - 1]; // 在输入的时候就计算出了前缀和
    }

    int a, b;
    while (cin >> a >> b) { // 这样就无需考虑有多少个 a b 输入完,会自动停止循环的,之前我还在想如何停止呢,这样就不需要想了
        cout << nums[b] - nums[a - 1] << '\n';
    }
    return 0;
}
相关推荐
@H³M2 小时前
面试_动态规划
面试·职场和发展·动态规划
未来之窗软件服务2 小时前
自己写算法(十)js加密UUID保护解密——东方仙盟化神期
java·javascript·算法·代码加密·东方仙盟算法
样例过了就是过了2 小时前
LeetCode热题100 腐烂的橘子
数据结构·c++·算法·leetcode·bfs
Chan162 小时前
LeetCode 热题 100 | 链表
java·数据结构·spring boot·算法·leetcode·链表·java-ee
程序员小明儿2 小时前
量子计算探秘:从零开始的量子编程与算法之旅 · 第五篇
算法·量子计算
灰色小旋风2 小时前
力扣第九题C++回文数
c++·算法·leetcode
vx-bot5556662 小时前
企业微信ipad协议的增量同步算法与差量更新机制
算法·企业微信·ipad
cpp_25012 小时前
P1359 租用游艇
c++·算法·题解·洛谷·线性dp
Naisu Xu2 小时前
数学笔记:最小二乘法(直线拟合)
笔记·算法·最小二乘法