代码随想录算法训练营第二天 | 977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋矩阵Ⅱ
977. 有序数组的平方
题目链接:977.有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
文章讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
视频讲解:https://www.bilibili.com/video/BV1QB4y1D7ep
思路
最直接的想法,把数组分为两个部分,小于零的部分以及大于等于零的部分,分别求平方,然后将小于零的子数组反转,最后将两个子数组归并。
看了卡哥的教程后,发现还可以采用双指针法。定义一个新数组result,和A数组一样大小,当前位置k指向result数组的终止位置。同时对于数组A,用两个指针i、j分别指向数组的起始位置和终止位置。
如果A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
如果A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
C++实现
// 自己的实现思路
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> neg_parts, pos_parts;
for(int i = 0;i<nums.size();i++){
if(nums[i] < 0){
neg_parts.emplace_back(nums[i] * nums[i]);
}
else{
pos_parts.emplace_back(nums[i] * nums[i]);
}
}
reverse(neg_parts.begin(), neg_parts.end());
vector<int> results;
int neg = 0, pos = 0;
while(neg < neg_parts.size() && pos < pos_parts.size()){
if(neg_parts[neg] < pos_parts[pos]){
results.emplace_back(neg_parts[neg++]);
}
else{
results.emplace_back(pos_parts[pos++]);
}
}
while(neg < neg_parts.size()) results.emplace_back(neg_parts[neg++]);
while(pos < pos_parts.size()) results.emplace_back(pos_parts[pos++]);
return results;
}
};
// 双指针法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for(int i = 0;i<nums.size();i++){
nums[i] = nums[i] * nums[i];
}
vector<int> result(nums.size(), 0);
int i = 0, j = nums.size() - 1, k = result.size() - 1;
while(i <= j){
if(nums[i] < nums[j]){
result[k--] = nums[j--];
}
else{
// nums[i] >= nums[j]
result[k--] = nums[i++];
}
}
return result;
}
};
209. 长度最小的子数组
题目链接:209.长度最小的子数组
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。**如果不存在符合条件的子数组,返回 0
。
文章讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
思路
看到题目的第一个想法是,能够采用动态规划的方式?想了一下,递推公式不知道咋推,不大靠谱。
应该还是用双指针来解决。题目已经说明数组里都是正整数,那么可以定义一前一后两个指针i和j,当指针内的和小于target时,j++,当指针内的和大于target时,i++,指针内的和等于target时,记录下j - i + 1的值,然后再让j++。
看错了,题目条件是大于等于target,所以当指针内的和小于target时,j++,当指针内的和大于等于target时,记录当前j - i + 1的值,然后i++。
卡哥的文章讲解里说这是滑动数组,我觉得这里的滑动数组和双指针是一个东西。。
C++实现
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0, j = 0;
int curr_sum = nums[i];
int min_length = nums.size() + 1;
while(j < nums.size()){
if(curr_sum < target){
j++;
if(j >= nums.size()) break;
curr_sum += nums[j];
}
else{
min_length = min(min_length, j - i + 1);
curr_sum -= nums[i];
i++;
}
}
return (min_length == nums.size() + 1) ? 0 : min_length;
}
};
59. 螺旋矩阵Ⅱ
题目链接:59.螺旋矩阵Ⅱ
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
文章讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html
视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
思路
确定一个方向数组,按序存储右、下、左、上四个方向。按照当前的方向将数字元素填入数组,如果继续沿着当前方向会导致”撞墙“,即超过矩阵边界或者与已填入数字元素冲突,则根据方向数组改换方向。
C++实现
class Solution {
private:
vector<pair<int,int>> dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
bool isLegal(pair<int,int> curr, pair<int,int> dir, vector<vector<int>>& matrix){
// 判断是否撞墙或冲突
int n = matrix.size();
int new_i = curr.first + dir.first, new_j = curr.second + dir.second;
if(new_i < 0 || new_i >= n || new_j < 0 || new_j >= n) return false;
if(matrix[new_i][new_j] != 0) return false;
return true;
}
public:
vector<vector<int>> generateMatrix(int n) {
int square_n = n * n;
vector<vector<int>> matrix(n, vector<int>(n, 0));
pair<int,int> curr = {0, 0};
int curr_dir = 0;
for(int i = 1;i<=square_n;i++){
matrix[curr.first][curr.second] = i;
int step = 0;
while(!isLegal(curr, dirs[curr_dir], matrix) && step < 4){
curr_dir = (curr_dir + 1) % 4;
step++;
}
curr.first += dirs[curr_dir].first;
curr.second += dirs[curr_dir].second;
}
return matrix;
}
};
总结
对数组部分做一个小总结。
数组是存放在连续内存空间上的相同类型数据的集合。数组是一种非常常用的数据结构,插入和删除的时间复杂度都为O(n),查找的时间复杂度为O(1)。
这两天刷的数组题目主要包括这几个部分:二分、双指针、滑动数组、模拟。
二分题目在处理时需要注意区间的定义,有许多题目是基础二分的变体,需要多刷题把握。
双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
滑动窗口在处理时需要注意如何移动窗口的起始位置,滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置,从而将O(n^2)的暴力解法降为O(n)。
对于模拟而言,我认为还是需要多练。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!