第一章_数组_Part2

有序数组的平方

题目描述

给你一个按 非递减顺序 排序的整数数组 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]

解题思路

暴力排序

核心思路:依次遍历数组元素,将每个元素都进行平方,然后进行排序即可。

时间复杂度:O(n + nlogn)

双指针法

核心思路:由于数组是有序的,但是数组元素包含负数,平方后可能会改变原有的顺序,因此可以采用双指针法进行问题解决。

具体步骤:

  • 初始化数组用于保存新排列后的平方值
  • 由于数组是有序的,因此在开方后,最大的值只会出现在左右两侧,因此可以将第一个指针指向最左侧元素,第二个指针指向最右侧元素,从左右两侧向中间不断逼近
  • 比较左右指针指向元素平方后的值,由于题目提到了从小到大排序,因此需要将较大的值放在数组最后的位置,依次执行,最后返回平方后的数组即可。

时间复杂度:O(n)

代码实现

测试地址:https://leetcode.cn/problems/squares-of-a-sorted-array/

暴力实现:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        // 遍历输入数组的每个元素
        for (int i = 0; i < nums.size(); i++) {
            // 将每个元素替换为其平方
            nums[i] *= nums[i];
        }
        // 使用标准库中的sort函数对数组进行排序
        sort(nums.begin(), nums.end());
        // 返回修改后的数组
        return nums;
    }
};

双指针实现:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        // 初始化结果数组,大小与输入数组相同,初始值为0
        vector<int> result(nums.size(), 0);
        // k用于从结果数组的末尾开始填充
        int k = nums.size() - 1;
        // 使用双指针技术,i从数组开始处迭代,j从数组末尾迭代
        for (int i = 0, j = nums.size() - 1; i <= j;) {
            // 比较两个指针所指元素的平方值,将较大的平方值放到结果数组的末尾
            if ((nums[i] * nums[i]) > (nums[j] * nums[j])) {
                // 如果i指向的元素的平方大于j指向的元素的平方,
                // 则将i指向的元素的平方赋值给结果数组的当前末尾位置,
                // 然后移动i指针向右一位,k指针向左一位
                result[k--] = nums[i] * nums[i];
                i++;
            } else {
                // 如果j指向的元素的平方大于等于i指向的元素的平方,
                // 则将j指向的元素的平方赋值给结果数组的当前末尾位置,
                // 然后移动j指针向左一位,k指针向左一位
                result[k--] = nums[j] * nums[j];
                j--;
            }
        }
        // 返回填充完成的结果数组
        return result;
    }
};

长度最小的子字符串

题目描述

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:

  • 输入:s = 7, nums = [2,3,1,2,4,3]
  • 输出:2
  • 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

提示:

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

解题思路

暴力

总体思路:

通过暴力方法寻找一个最短的子数组,使得子数组中的元素总和至少为 target。它通过两层循环,外层循环确定子数组的起始位置,内层循环扩展子数组的结束位置,直到找到一个满足条件的子数组。一旦找到,它会记录下当前找到的满足条件的最短子数组的长度,并尝试找到更短的一个,直到遍历完整个数组。

时空复杂度:

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

滑动窗口

滑动窗口介绍:通过不断调节子序列起始位置和终止位置,从而得到我们想要的结果。

总体思路:

实现滑动窗口,满足窗口内包含的子序列和>=给定的条件值s

窗口起始位置移动:如果当前窗口已经大余或者等于s,将窗口向右侧移动,即缩小窗口范围

窗口终止位置移动:依次遍历数组的元素并进行累加操作,直至找到最先满足子序列和>=s的下标位置,即扩大窗口范围。

代码实现

测试地址:https://leetcode.cn/problems/minimum-size-subarray-sum/description/

暴力:

class Solution {
public:
    // 定义一个函数,接收目标值 target 和一个整数数组 nums 作为参数
    int minSubArrayLen(int target, vector<int>& nums) {
        // 初始化 sum 为 0,用来在每次内层循环开始时重置子数组的和
        // result 用来保存最小子数组的长度,初始设置为 INT_MAX,表示最大可能的整数值
        int sum = 0, result = INT_MAX;
        // subLength 用来暂存当前找到的满足条件的子数组的长度
        int subLength = 0;
    
        // 外层循环,i 标记当前子数组的起始位置
        for (int i = 0; i < nums.size(); i++) {
            // 每次开始新的子数组时,重置 sum 为 0
            sum = 0;
            // 内层循环,j 标记当前子数组的结束位置
            for (int j = i; j < nums.size(); j++) {
                // 累加当前元素到 sum 中,扩展当前子数组的范围
                sum += nums[j];
                // 当当前子数组的和大于等于 target 时
                if (sum >= target) {
                    // 计算当前子数组的长度
                    subLength = j - i + 1;
                    // 更新 result 为当前子数组长度和之前 result 中的较小值
                    result = std::min(result, subLength);
                    // 找到符合条件的子数组后跳出内层循环
                    break;
                }
            }
        }
        // 如果没有找到满足条件的子数组,返回 0;否则,返回找到的最短子数组的长度
        return result == INT_MAX ? 0 : result;
    }
};

滑动窗口:

class Solution {
public:
    // 定义一个函数,接收目标值 target 和一个整数数组 nums 作为参数
    int minSubArrayLen(int target, vector<int>& nums) {
        // 初始化 sum 为 0,用来存储子数组的和
        // result 用来保存最小子数组的长度,初始设置为 INT_MAX,表示最大可能的整数值
        // i 用来标记当前考虑的子数组的起始位置
        int sum = 0, result = INT_MAX, i = 0;
        // subLength 用来暂存当前找到的满足条件的子数组的长度
        int subLength = 0;
    
        // 遍历 nums 数组,j 用来标记当前考虑的子数组的结束位置
        for (int j = 0; j < nums.size(); j++) {
            // 累加当前元素到 sum 中,扩展当前子数组的范围
            sum += nums[j];
        
            // 当当前子数组的和大于等于 target 时,尝试缩小子数组的范围来寻找更短的满足条件的子数组
            while (sum >= target) {
                // 计算当前子数组的长度
                subLength = j - i + 1;
                // 更新 result 为当前子数组长度和之前 result 中的较小值
                result = min(result, subLength);
                // 从子数组的和中减去当前考虑的子数组的开始元素,并移动起始位置指针
                sum -= nums[i++];
            }
        }
        // 如果没有找到满足条件的子数组,返回 0;否则,返回找到的最短子数组的长度
        return result == INT_MAX ? 0 : result;
    }
};

螺旋矩阵

题目描述

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:

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

解题思路

原则:循环不变量原则,即每个区间处理边界的方式都是一致的,本题中可以按照左闭右开的原则

总体思路:模拟顺时针画矩阵的过程

  • 填充上行从左到右
  • 模拟右列从上到下
  • 模拟下行从右到左
  • 模拟左列从下到上

实现细节

  • 如果n是偶数的话,循环的圈数为 n / 2
  • 如果n是奇数的话,可以先进行偶数的循环,最后单独考虑最后一个中点位置
  • 因为每圈都会固定缩小区间范围,因此可以引入一个参数offset方便判断边界

时空复杂度分析

  • 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
  • 空间复杂度 O(1)

代码实现

测试地址:https://leetcode.cn/problems/spiral-matrix-ii/description/

class Solution {
public:
    // 函数生成一个n x n的螺旋矩阵
    vector<vector<int>> generateMatrix(int n) {
        // 初始化一个n x n的矩阵,初始值为0
        vector<vector<int>> res(n, vector<int>(n, 0));
        // 定义起始坐标
        int startx = 0, starty = 0;
        // 定义偏移量,用于控制每一层填充的边界
        int offset = 1;
        // 定义填充的起始数字
        int count = 1;
        // 定义循环的圈数,每一圈对应矩阵中的一层螺旋
        int loop = n / 2;
        // 如果n为奇数,矩阵中心点的坐标
        int mid = n / 2;
        // 定义循环中使用的索引变量
        int i, j;
        // 循环填充每一层螺旋
        while (loop--) {
            // 从左上角开始
            i = startx, j = starty;
            // 从左到右填充上行
            for (j; j < n - offset; j++) {
                res[i][j] = count++;
            }
            // 从上到下填充右列
            for (i; i < n - offset; i++) {
                res[i][j] = count++;
            }
            // 从右到左填充下行
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            // 从下到上填充左列
            for (; i > startx; i--) {
                res[i][j] = count++;
            }
            // 移动到下一层螺旋的起始位置
            startx++, starty++;
            // 更新偏移量,准备填充下一层螺旋
            offset += 1;
        }
        // 如果n为奇数,填充矩阵的中心点
        if (n % 2) {
            res[mid][mid] = count;
        }
        // 返回生成的螺旋矩阵
        return res;
    }
};

  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值