有序数组的平方
-
给你一个按 非递减顺序 排序的整数数组 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]
-
对应leetcode977题
-
本题我最先想到的是暴力解法,但是具体实现有些遗忘,翻过笔记后成功AC
暴力解法
-
每个数先进行平方,然后利用快速排序
class Solution { public: vector<int> sortedSquares(vector<int>& nums) { for(int i=0 ;i<nums.size();i++){ nums[i]= nums[i] * nums[i]; } sort(nums,0,nums.size()-1); // 快速排序 return nums; } void sort(vector<int> &nums,int low,int high){ if(low<high){ //表的长度大于1 int pivotloc = partition(nums,low,high); //将nums数组一分为二,pivotloc为枢轴元素排好的位置 sort(nums,low,pivotloc-1);//对低子表递归排序 sort(nums,pivotloc+1,high);//对高子表递归排序 } } int partition(vector<int> &nums,int low,int high){ int pivotkey = nums[low]; //设表中第一个元素为枢轴,对表进行划分 while(low<high){ while(low<high&&nums[high]>=pivotkey){ --high; } nums[low]=nums[high];//将比枢轴小的元素移动到左端 while(low<high&&nums[low]<=pivotkey){ ++low; } nums[high]=nums[low];//将比枢轴大的元素移动到右端 } nums[low]=pivotkey;//枢轴元素放到最终位置 return low; } };
-
时间复杂度为O(n + nlogn)
双指针法
-
数组是有序的, 但是负数平方之后可能成为最大数了
-
所以数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间,且大小由中间向两端趋近
-
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
-
定义一个新数组result,和nums数组一样的大小,让k指向result数组终止位置。
-
如果
nums[i] * nums[i] < nums[j] * nums[j]
那么result[k--] = nums[j] * nums[j];
。
-
如果
nums[i] * nums[i] >= nums[j] * nums[j]
那么result[k--] = nums[i] * nums[i];
class Solution { public: vector<int> sortedSquares(vector<int>& nums) { vector<int> result(nums.size(),0) ; int k =result.size()-1; for(int i = 0,j=nums.size()-1;i<=j;){ if(nums[i]*nums[i]<nums[j]*nums[j]){ result[k--]=nums[j]*nums[j]; j--; } else{ //已经包含>=的情况 result[k--]=nums[i]*nums[i]; i++; } } return result; } };
-
时间复杂度为O(n),相对于暴力排序的解法O(n + nlog n)还是提升不少的
长度最小的子数组
给定一个含有 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
-
力扣209题
暴力解法
-
暴力解法是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2)。
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int result = INT32_MAX; // 最终的结果 int sum = 0; // 子序列的数值之和 int subLength = 0; // 子序列的长度 for(int i=0;i<nums.size();i++){ //i为子序列起点 sum=0; //内循环找完更新sum为0;从i++位置重新寻找 for(int j=i;j<nums.size();j++){ sum=sum+nums[j]; //求和 if(sum>=target){ //一旦发现子序列和超过了target,更新result subLength=j-i+1; //取子序列的长度 result = result < subLength ? result : subLength; break;//因为找的是符合条件的最短子序列,一旦符合就跳出内循环,从i++位置重新寻找 } } } // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列 return result == INT32_MAX ? 0 : result; } };
-
时间复杂度:O(n^2)
-
空间复杂度:O(1)
-
但是力扣更新了数据,运行超时
下面是我当时写了的,不过发现还是暴力
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int i=0,j=0,sum=0; int result = INT32_MAX; // 最终的结果 int subLength = 0; // 子序列的长度 while(i<=j&&j<nums.size()){ int temp=sum+nums[j]; if(temp>=target){ subLength=j-i+1; //取子序列的长度 result = result < subLength ? result : subLength; i++; j=i; sum=0; } else{ sum=sum+nums[j]; if(j==nums.size()-1){ i++; j=i-1; sum=0; } j++; } } return result == INT32_MAX ? 0 : result; } };
滑动窗口法
-
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果
-
只用一个for循环,那么这个循环的索引,一定是表示滑动窗口的终止位置。
-
窗口的移动:如果j在移动过程中发现sum>=target,那么首先记录当前窗口长度,然后将窗口缩短(i--),并减去nums[i]的值,减去之后的sum就是当前缩短后的窗口的sum值,并再次判断是否大于target,若大于,则继续记录长度并与前一个长度进行比较,取最小的,然后更新sum值,i++进行移动缩短,若是小于的话,则移动j指针,扩大窗口长度,进行判断,依次类推,直到遍历完成
-
在本题中实现滑动窗口,主要确定如下三点:
-
窗口内是什么?
-
如何移动窗口的起始位置?
-
如何移动窗口的结束位置?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
-
-
可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int i = 0, j = 0, sum = 0;// sum:滑动窗口数值之和 i: 滑动窗口起始位置 int result = INT32_MAX; // 最终的结果,INT32_MAX是最大正整数 int subLength = 0; // 滑动窗口的长度 for (j = 0; j < nums.size(); j++) { sum = sum + nums[j]; // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件 while (sum >= target) { subLength = j - i + 1; // 取子序列的长度 result = result < subLength ? result : subLength; sum = sum - nums[i++];// 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置) } } // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列 return result == INT32_MAX ? 0 : result; } };
-
时间复杂度:O(n)
-
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
-
-
空间复杂度:O(1)
螺旋矩阵
-
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
-
力扣59题
模拟法
而求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
-
填充上行从左到右
-
填充右列从上到下
-
填充下行从右到左
-
填充左列从下到上
由外向内一圈一圈这么画下去。
-
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人。
-
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
-
按照左闭右开规则进行模拟:首先给定一系列初始值,然后循环n/2次(理由:依据对称可以发现),先填入第一行的值,但是第一行最后一个元素不填,因为是左闭右开,然后是填入最后一列,同理,最后一列的最后一个元素不填,再然后是最后一行,从右往左进行填写,第一列的最后一个元素不填,最后就是从下往上填写第一列,第一个元素不填,第一圈结束,然后更新startx,starty,填写内层的值,以此类推,如果n是奇数,循环体内不会进行填写,最后进行单独判断填写
-
所以一定要按照规则进行填入,但是我了解到思路后,还是没能一次性写对!
class Solution { public: vector<vector<int>> generateMatrix(int n) { int startx=0,starty=0; //定义每循环一个圈的起始位置 int i=0,j=0; int offset=1;//需要控制每一条边遍历的长度,每次循环右边界收缩一位 int count=1;//用来给矩阵中每一个空格赋值 int loop=n/2; //每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理 int mid =n/2;// 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2) vector<vector<int>> nums(n,vector<int>(n,0));// 使用vector定义一个二维数组 while(loop--){ i=startx; //i控制行,startx就是行的初试值 j=starty; //j控制列,starty就是列的初始值 // 下面开始的四个for就是模拟转了一圈 // 模拟填充上行从左到右(左闭右开) for(j;j<n-offset;j++){ nums[i][j]=count++; } // 模拟填充右列从上到下(左闭右开) for(i;i<n-offset;i++){ nums[i][j]=count++; } // 模拟填充下行从右到左(左闭右开) for(;j>starty;j--){ nums[i][j]=count++; } // 模拟填充左列从下到上(左闭右开) for(;i>startx;i--){ nums[i][j]=count++; } // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1) startx++; starty++; // offset 控制每一圈里每一条边遍历的长度 offset+=1; } // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值 if(n%2!=0){ nums[mid][mid]=count;//因为count在上述循环中自增过了,所以直接赋值即可 } return nums; } };
-
时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
-
空间复杂度 O(1)
第二种解法
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> nums(n,vector<int>(n,0));// 使用vector定义一个二维数组
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
for(int x =0,y=0,d=0,k=1;k<=n*n;k++){
nums[x][y]=k;
int a = x+dx[d],b=y+dy[d];
if(a<0||a>=n||b<0||b>=n||nums[a][b]){
d=(d+1)%4;
a=x+dx[d],b=y+dy[d];
}
x=a,y=b;
}
return nums;
}
};