day2:977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵II
977.有序数组的平方
思路
需要采用时间复杂度为O(n)的算法,则不能采用sort排序算法,需要边排序边平方。但是时间复杂度为O(n)的排序算法不知道有哪些。所以这道题不会。
结果
FAILED!(┬┬﹏┬┬)
反思
没有想到先利用题目原有的升序规则。
代码
//双指针 class Solution { public: vector<int> sortedSquares(vector<int>& nums) { int n = nums.size(); int negative = -1; for (int i = 0; i < n; ++i) { if (nums[i] < 0) { negative = i;//最小的负数 } else { break; } } vector<int> ans; int i = negative, j = negative + 1; while (i >= 0 || j < n) {//或,没有都超出范围就继续执行。 if (i < 0) {//负数没有了 ans.push_back(nums[j] * nums[j]); ++j; } else if (j == n) {//正数没有了 ans.push_back(nums[i] * nums[i]); --i; } else if (nums[i] * nums[i] < nums[j] * nums[j]) { ans.push_back(nums[i] * nums[i]);//放小的 --i; } else { ans.push_back(nums[j] * nums[j]); ++j; } } return ans; } }; //解法2 class Solution { public: vector<int> sortedSquares(vector<int>& A) { int k = A.size() - 1; vector<int> result(A.size(), 0); for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素 if (A[i] * A[i] < A[j] * A[j]) { result[k--] = A[j] * A[j]; j--; } else { result[k--] = A[i] * A[i]; i++; } } return result; } };
总结
双指针
双指针的写法,解法2更简单,解法1while更复杂,解法1用的容器push_back,通用性更高吧。day1:27题的也是头尾双指针。
209.长度最小的子数组
思路
像一个数学问题。如果是常规思路,就是一个长度一个长度的计算,每个长度的子数组总和都算出来,看到哪个长度的时候超过target,但是这样计算量是不是太大了。
解法1:将从每个元素开始的总和超过target的子数组长度记录下来,最后取最小值。(如果超过n则返回0)
解法2:同理,但是不计算每个最小值,计算从第一个开始的子数组最小长度然后以此为区间向后平移,如果区间总和超过target,则减小区间看能不能满足条件,如果能,则更新区间长度,接着向后平移。(比解法1应该要快一些)
结果
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int min = 0,sum = 0, n = nums.size(); int i = 0; for(; i < n; ++i){ sum += nums[i]; if(sum >= target){ min = i+1; break; } } if (i == n){ return 0; } else{ i = 1,sum = 0; } while(i<n){ for(int j = i; j < i+min; j++){ sum += nums[j]; } if(sum > target){ min--; } else{ i++; } } return min; } };
反思
-
有点儿进步,因为while循环里面用if-else,避免重复检测实现了,使代码更精简。
-
min的赋值太麻烦了,直接给个最大值即可让他在循环里自己缩小。
-
sum值忘记在循环尾里面置0了。
-
这方法不行,每个元素还是操作了很多次,超时了,而且逻辑有错误,j<i+min超范围了,min更新应该用min = min < j - i?min:j-i;
代码
class Solution { public: int minSubArrayLen(int s, vector<int>& nums) { int result = INT32_MAX; int sum = 0; // 滑动窗口数值之和 int i = 0; // 滑动窗口起始位置 int subLength = 0; // 滑动窗口的长度 for (int j = 0; j < nums.size(); j++) { sum += nums[j]; // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件 while (sum >= s) { subLength = (j - i + 1); // 取子序列的长度 result = result < subLength ? result : subLength; sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置) } } // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列 return result == INT32_MAX ? 0 : result; } };
总结
滑动窗口
-
重点是那个sum-=nums[i++], 这样可以一直变更起始位置,保持当前最小长度。(先操作再减)而且while里面只有减到小于s才能退出循环。sum减前面的值在更新result后面,不用担心更新错。
-
i++是先操作再加,
59.螺旋矩阵II
思路
初始化肯定是n*n,然后根据顺序填入数字,主要是二维数组的索引变化条件。把索引全部写出来应该就能找出规律了。
i,j初始值都为1,然后都按顺序增到n再减,在循环内用while,或者for判定哪个递增或者递减的条件。然后只需要在大循环中更新初始值(1,2,3等)和结束值(n,n-1,n-2等)结束循环条件应该是初始值小于结束值。
更新条件为:1、j完成一次递增,则i的初始值加1
2、i完成一次递增,则j的结束值减1
3、j完成一次递减,则i的结束值减1
4、i完成一次递减,则j的开始值加1
结果
嘿嘿,做出来了!😍
class Solution { public: vector<vector<int>> generateMatrix(int n) { vector<vector<int>> nums(n,vector<int>(n)); int i_begin = 0, i_end = n,j_begin = 0, j_end = n; int i = i_begin,j = j_begin, index = 1; while(i_begin<=i_end&&j_begin<=j_end){ while(i == i_begin && j < j_end){ nums[i][j++] = index++; } i_begin++,index--; while(j == j_end && i < i_end){ nums[i++][j-1] = index++; } j_end--,index--; while(i == i_end && j > j_begin){ nums[i-1][--j] = index++; } i_end--,index--; while(j == j_begin && i > i_begin){ nums[--i][j] = index++; } j_begin++,index--; } return nums; } };
反思
感觉有些代码有点多余,应该可以优化。
边界条件试错了很久。
代码
这方法好像没我的好
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; 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++; } // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1) startx++; starty++; // offset 控制每一圈里每一条边遍历的长度 offset += 1; } // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值(n/2有余数) if (n % 2) { res[mid][mid] = count; } return res; } };
总结
边界条件
主要还是考虑边界条件别出错和重复了。