重拾C++之菜鸟刷算法第一篇---数组

本文介绍了C++中避免二分查找溢出的技巧,探讨了快慢指针在移除元素问题中的应用,以及如何用双指针解决有序数组的平方和长度最小子数组问题。最后展示了如何生成螺旋矩阵II。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组

一、二分查找

知识点:

  • int middle = itBegin + (itEnd - itBegin)/ 2; // 防止溢出

在 C++ 中,如果两个整数相加的结果超过了整数类型的表示范围,就会发生溢出,导致结果不再是预期的值。

考虑到二分查找中常用到的 (itBegin + itEnd) / 2,如果 itBeginitEnd 都是很大的正整数时,它们的和可能超过整数类型的最大表示范围,导致溢出。

而使用 (itBegin + (itEnd - itBegin) / 2) 这样的写法,可以避免这个问题。首先,通过 itEnd - itBegin 计算出两个迭代器之间的距离,然后再除以2。这样,即使 itBeginitEnd 很大,它们之间的差值仍然是正数,不会导致溢出。

  • middle - 1, middle + 1;

假如有一堆数字牌,从最左边到最右边,这些数字是按照从小到大的顺序排列的。我们要找的数字就叫做 "target"。

现在,我们先看最中间的一张牌的数字,这张牌的位置就是 middle。如果这张牌上的数字比我们要找的数字 "target" 大,那么我们就知道 "target" 只可能在这张牌的左边,因为数字是按照从小到大的顺序排列的。

所以,我们把牌堆缩小到左边一半,把最右边的牌移到 middle - 1 的位置。这样,我们就把寻找的范围缩小了一半,继续找。

这样做的好处有两点:

1. 防止出现无限循环

如果有两张相邻的牌上的数字相等且都等于目标数字,我们每次猜中间的数字都不变,就陷入了无限循环,就像玩一个不停的游戏一样。

2. 提高搜索效率

如果目标数字比中间的数字小,那就说明目标数字在这张牌的左边。

题目

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

题解

class Solution {
public:
    int search(vector<int>& nums, int target) {
        // 求vector长度 .size()
        int itBegin = 0;
        int itEnd = nums.size() - 1;
        while (itBegin <= itEnd) {
            int middle = itBegin + (itEnd - itBegin)/ 2; // 防止溢出
            if (target < nums[middle]) 
            {
                itEnd = middle - 1;
            }
            else if (target > nums[middle])
            {
                itBegin = middle + 1;
            }
            else{
                return middle;
            }
        }
        return -1; 
    }
};

二、移除元素

知识点

这是一个快慢指针的问题,我想到了这个题解,但是没有想到快慢指针也可以用数字表示。

反正fast往前冲,slow只有符合条件的时候才赋值往前移动。

题目

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
​
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
​
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

题解

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

三、有序数组的平方

知识点

  • 双指针 (使用条件,需要构造一个新数组)

题目

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

题解

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result(nums.size(), 0); // vector的构造函数
        int begin = 0;
        int end = nums.size() - 1; 
        int Final = nums.size() - 1; 
        while(begin <= end){ // 这里的end和begin都是闭区间可以取等号
            if(nums[end] * nums[end] > nums[begin] * nums[begin]){
                result[Final] = nums[end] * nums[end];
                end --;
                Final --;
            }
            else{
                result[Final] = nums[begin] * nums[begin];
                begin ++;
                Final --;
            }
        }
        return result;
    }
};

四、长度最小的子数组

知识点

  • 滑动窗口

  1. 滑动窗口内容

  2. 滑动窗口的起始位置

  3. 滑动窗口的结束位置

题目

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

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

题解

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int subLength = 0;
        int result = INT32_MAX;// 记录最大数
        int sum = 0; // 用来记录子序列总和
        int i = 0; // 用来记录起始指针位置
        for (int j = 0; j < nums.size(); j++){// 滑动窗口的结束位置移动规则
            sum += nums[j];
            while(sum >= target)
            {
                subLength = j - i + 1; // 滑动窗口的内容
                result = result > subLength ? subLength:result;
                sum -= nums[i++]; // 滑动窗口的起始位置移动规则
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

五、螺旋矩阵 II

知识点

  • 不变性原则,规则是具有确定性的(比如开区间和闭区间,如果你决定了遍历都是【闭区间,开区间),那么请仔细确定接下来的步骤也应该遵守这个原则)

题目

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

题解

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> Matrix(n, vector<int>(n, 0));
        int startX = 0;
        int startY = 0;
        int offset = 1;
        int count = 1;
        int k = 0;
        int i, j;
        while(k <= n/2){
            j = startY;
            i = startX;
​
            for (j = startY; j < n - offset; j++){
                Matrix[startX][j] = count++;
            }
            for (i = startX; i < n - offset; i++){
                Matrix[i][j] = count++;
            }
            for (; j > startY; j--){
                Matrix[i][j] = count++;
            }
            for (; i > startX; i--){
                Matrix[i][j] = count++;
            }
            k++;
            startX++;
            startY++;
            offset++;
        }
        if(n % 2 == 1){
            Matrix[n/2][n/2] = count;
        }
        return Matrix;
    }
};

题目整理来自{代码随想录},今天就到这啦~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值