day2
算法题
977.有序数组的平方
题目建议: 本题关键在于理解双指针思想
文章讲解:programmercarl.com/0977.%E6%9C…
视频讲解: www.bilibili.com/video/BV1QB…
个人思路: 最简单的方法肯定是暴力法,即将每个数原地平方,然后进行排序,由于排序的时间复杂度最小为O(nlogn),时间复杂度较高,如果希望达到o(n)的复杂度,可发现中间的元素是最小的,距离0的距离最短,并且可以整个数组的元素分为两种,一种为正数,一种为负数,并且由于本身数组有序,因此负数从中间向左开始绝对值开始变大,而正数则从中间向右开始绝对值开始变大,对两组元素进行归并排序操作,由于本身有序,因此只需要执行归并操作的最后一步,时间复杂度为o(n),同时注意两种特殊情况,即全正和全负的场景。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
//printf("%d\n",n);
int start = 0;int end = nums.size() -1;
// int mid = (start + (end - start))/2;
int mid =0;
int find_0 = -1;
for(mid=0;mid<n;mid++)
{
if(nums[mid]>=0) {
find_0 = mid;
break;
}
}
if(find_0 == -1) mid = n;
else mid = find_0;
vector<int> newnums(n);
if(mid == n){ //全负数
for(int j=n-1,k=0;j>=0;j--,k++)
{
newnums[k] = nums[j] * nums[j];
}
return newnums;
}
else if(mid ==0){ //全正数或者只有一个数
for(int j=0;j<n;j++)
{
newnums[j] = nums[j] * nums[j];
}
return newnums;
}
else{
int j=0,k=0,l=0;
for(j=mid -1,k=mid,l=0;j>=0&&k<n;l++)
{
if(nums[j]*nums[j] <nums[k]*nums[k])
{
newnums[l] = nums[j]*nums[j];
j--;
}
else{
//printf("%d %d\n",k, l);
newnums[l] = nums[k]*nums[k];
k++;
}
}
if(j>=0) while(j>=0) {newnums[l] = nums[j]*nums[j];l++;j--;};
if(k<=n-1) while(k<n) {newnums[l] = nums[k]*nums[k];l++;k++;};
return newnums;
}
return newnums;
}
};
看解析后的思路: 可以发现上述代码量偏大,其问题主要在于将数组分为正数和负数,以及两种特殊情况,以及最后当一组数组分配结束后的处理,**同样的思路换一个方向,**由于中间是最小的,那边两边的就一定有一个是最大的,如果从大的数字开始比较,逆向输入到目标数组中,则代码量会大大减少,同时减小边界的判断,bug也会变得更少。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
int l=0,r=n-1;
vector<int> A(n);
int i =n-1;
while(l<=r){
if(nums[r]*nums[r]>=nums[l]*nums[l]){
A[i] =nums[r]*nums[r];
r--;
i--;
}
else{
A[i] =nums[l]*nums[l];
l++;
i--;
}
}
return A;
}
};
209.长度最小的子数组
题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。
文章讲解programmercarl.com/0209.%E9%95…
视频讲解:www.bilibili.com/video/BV1tZ…
个人思路: 本题为很明显的滑动窗口,通过控制左边界和右边界,来保证窗口内部的值符合题目的要求,同时在该基础上不断调整边界使得窗口的大小最小,思路较为简单,但由于最开始对于边界的判断过于混乱,导致debug了很长时间。**整理后的思路:**当窗口内的元素之和大于或者等于目标时,就需要调整左边界,从而减小窗口内元素的和,而当窗口内的元素之和小于目标时,则需要调整右边界,从而增加窗口内元素的大小。
代码1:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int min = nums.size()+1;
int n = nums.size();
int l =0,r=0;
int sum = nums[0]; //当数组的长度为1时,没法进行判断
if(sum>=target) min = 1;
int flag = 0;
while(flag == 0){
if(sum<target)
{
if(r<n-1) //边界为 r = n-2
{
r++;
sum+=nums[r];
}
else{ //边界为 r = n-1 即r已经到达了边界
r++;
flag =1;
}
}
else if(sum>=target)
{
if(l<n-1) //边界为 l = n-2
{
sum-=nums[l];
l++;
}
else{ //此时边界为 l = n-1 即l也到达了边界 同时由于sum>=target 此时长度为1
flag =1;
}
}
printf("sum=%d l=%d r=%d ",sum,l,r);
if(sum>=target&&r-l+1<min)
{
min = r-l+1;
}
printf("min=%d\n",min);
}
if(min==n+1) return 0;
return min;
}
};
**代码2:(代码随想录上面的写法本质上是一样的 ** 代码精简且边界条件更少 值得学习)
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX;
int sum =0;
int i=0;
for(int j=0;j<nums.size();j++) //用于控制右边界增长
{
sum+=nums[j];
while(sum>=target){ //符合条件
int len = j-i+1;
result = len<result?len:result;
sum-=nums[i];
i++; //左边界变小
}
}
return result == INT32_MAX ? 0 : result;
}
};
59.螺旋矩阵II
题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。
文章讲解:programmercarl.com/0059.%E8%9E…
视频讲解:www.bilibili.com/video/BV1SL…
个人思路: 模拟转圈,主要是对边界情况进行判断
出现的问题:
1.对于switch语句不够熟悉,break的用法
2.对于二维容器创建的初始化
3.分清方向,第一维坐标控制上下,第二维控制左右 例如向左为{0,-1}而不是{-1,0}
代码:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> wheel= {{0,1},{1,0},{0,-1},{-1,0}}; //右,下,左,上 注意别写反了
//vector<vector<int>> table(size1, vector<int>(size2, 0)); 二维容器的初始化
vector<vector<int>> newMatrix(n,vector<int>(n,0));
int mi = 0,mj = 0;
int mw = 0;
int limr = n-1; //右边界
int liml = 0; //左边界
int limdown = n-1; //下边界
int limup = 0; //上边界
for(int i=1;i<=n*n;i++)
{
newMatrix[mi][mj] = i;
//printf("mi=%d mj=%d\n",mi,mj);
switch(mw) {
case 0:
{
if(mj+wheel[mw][1]>limr) {mw+=1;mw%=4;limup+=1;} //向右走到尽头,切换方向,同时上边界+1,即第二维坐标
break;
}
case 1:
{
//printf("mw = %d -----%d \n",mw,wheel[mw][0]);
if(mi+wheel[mw][0]>limdown) {mw+=1;mw%=4;limr-=1;} //向下走到尽头,切换方向,同时右边界-1,即第一维坐标
break;
}
case 2:
{
if(mj+wheel[mw][1]<liml) {mw+=1;mw%=4;limdown-=1;} //向左走到尽头,切换方向,同时下边界-1,即第二维坐标
break;
}
case 3:
{
if(mi+wheel[mw][0]<limup) {mw+=1;mw%=4;liml+=1;} //向上走到尽头,切换方向,同时左边界+1,即第一维坐标
break;
}
default:
break;
}
mi += wheel[mw][0];
mj += wheel[mw][1];
}
return newMatrix;
}
};
题解思路: 该题不涉及太多的算法问题,主要是对整个逻辑进行模拟,考察代码能力。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};