第二天记录
卡哥文章与题目的传送门 :
1. 977-有序数组的平方
- 法一: 暴力求解
class Solution{
// 复杂度 O(n) + O(nlogn)
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
- 法二: 双指针法遍历数组两端,构造出一个归并排序
class Solution{
/**
* 双指针法处理,将原先的数组处理为一个归并数组排序
* 因为最大的数值只会出现在两边!
* 但也引起了新的思考 -- 什么样的情况下使用双指针会更好呢?
* 个人觉得应该是 想将双重循环放到一个循环里的时候,就可以考虑双指针操作
* 复杂度 O(n)
*/
public int[] sortedSquares(int[] nums) {
int size = nums.length;
int left = 0; // 左边的指针
int right = size - 1; // 右边的指针
int ans[] = new int[size]; // 新开辟的数组
int index = size - 1; // 填充用的下标
int leftSquare, rightSquare; // 左右指针的平方
while (left <= right){ // 左右指针移动后,新指定的数字还未被处理,在下面只会有一个指针处理该数字
// 归并排序思想,左右同时向中间走,遇到大的放里面
leftSquare = nums[left] * nums[left];
rightSquare = nums[right] * nums[right];
if (leftSquare < rightSquare){
ans[index--] = rightSquare;
right--;
}
else {
ans[index--] = leftSquare;
left++;
}
}
return ans;
}
}
2. 209-长度最小的子数组
- 法一: 暴力双循环(就不在此展示了,因为偷了懒没有写…)
- 法二: 滑动窗口解法 复杂度 - O(n)
- 思路及代码如下 :
/**
* 滑动窗口法处理
* 先找到第一个目标窗口 (一左一右两个指针构成的内部数组,也叫做窗口)
* 左边的指针开始向右,缩编窗口,找第一个不符合目标窗口的下标
* 左边下标不动,开始滑动右边窗口,继续寻找新的目标窗口
* 每次找到目标窗口后,就记录窗口的长度,不断更新当前找到的最小长度
*/
class Solution {
public static int minSubArrayLen(int target, int[] nums) {
int start = 0, end = 0; // 窗口起始下标
int minLen = 999999; // 记录最小窗口长度
int sum = 0;
int currentLen = 0;
for (; end < nums.length; end++) {
sum += nums[end];
while (sum >= target && start <= end){
currentLen = end - start;
if ( currentLen < minLen) minLen = currentLen;
sum -= nums[start];
start++;
}
}
return minLen == 999999 ? 0 : minLen + 1;
}
}
- 前几次提交的时候,发现总是到第17个测试样例过不去,最后发现错误出在返回值的问题竟然是:返回的不是 minLen …(救命哇!SOS!)
3. 59-螺旋矩阵 Ⅱ
- 初始思路
螺旋数组的填充正好就是 → ↓ ↑ ← 四个方向绕圈填充
所以对四个方向判断填充就行,中途还在想是否递归也能处理这个问题
- 错误代码如下
// 能看到哪里错误了吗?
class Solution {
public static int[][] generateMatrix(int n) {
int size = n - 1;
int nums[][] = new int[size][size];
int x = 0, y = 0;
// 行走顺序 - 右下左上
for (int i = 1; i <= n * n; i++) {
// → 这样看来,是不是能递归处理?
if (y < size && nums[x][y+1] == 0) { nums[x][y++] = i; }
// ↓
else if (x < size && nums[x+1][y] == 0) { nums[x++][y] = i; }
// ←
else if (y < size && nums[x][y-1] == 0) { nums[x][y--] = i; }
// ↑
else if (x > size && nums[x--][y] == 0) { nums[x--][y] = i ; }
}
return nums;
}
}
-
答案揭晓
- 下标越界(在写正确代码的时候,才明晰错在了哪里。。。)
- 在第一次到达向上填充的时候,
就出现了问题,本该一路向上的,由于一开始的 右行 if条件满足,没法到向上的条件
-
正确思路
- 对每一行进行循环处理,同时保证循环处理时的处理情况是一样的!
个人理解卡哥说的循环不变量
就是保证每次循环的基准条件不变,比如这题,填充每一行元素时,
改行的最后一位元素放开,形成一个 [ ) 左闭右开区间
每一行处理的模式和始末情况大致相似,对其进行统一处理,无须添加特判
- 思路及代码如下
/**
* 正确代码
* 控制每一条边的循环,将大循环的颗粒度放到一圈上
* 每一边选择左闭右开的处理区间
* 一圈一圈用循环进行处理
*/
class Solution {
public static int[][] generateMatrix(int n) {
int ans[][] = new int[n][n];
int start_x = 0, start_y = 0; // 每一圈的起始位置
int count = 1; // 1 开始计数
int offset = 1; // 每一圈边界的偏移量
int circle = 1;
int i = start_x ,j = start_y; // 填充每一条边时的起始位置
for (; circle <= n/2; ){
// 处理 → ↓ ← ↑
for (j = start_y; j < n - offset; j++) { ans[start_x][j] = count++; } // →,赋值到 : 边界 - offset 的下标, [ ) 左闭右开的区间
for (i = start_x; i < n - offset; i++) { ans[i][j] = count++; } // ↓,右行的边处理完后,j的值等于开区间的边界值,也就是下一条边的起点
for ( ; j >= offset; j--) { ans[i][j] = count++; } // ← 要预留出一个偏移量供下一条边处理时使用,且此时是由开区间端走向闭区间的端
for ( ; i >= offset; i--) { ans[i][j] = count++; } // ↑
// 四个循环都走完代表一整圈被赋值完成,开始进入下一圈
// 需要更新的值有 - 1. 更新当前待处理圈的起始坐标 2. 偏移量发生改变
start_x ++;
start_y ++;
offset ++;
circle ++; // 进入下一圈
}
// 处理奇数情况下,最中心的位置
if ( n % 2 != 0) { ans[start_x][start_y] = count ;}
return ans;
}
}
- 注意点
- 左上两边的处理,判断条件需要注意
- 圈数控制
补充
- java中min函数用法 Math.min(reslut,subL);
- java定义int最大值 int reslut=Integer.MAX_VALUE;
- 矩阵,对角线就可以形成一个正方形,每一圈处理左上和右下两个位置,所以圈数为 n/2
- 对开闭区间的概念理解还是不清晰
- 下次调用库函数的时候,可以适当考虑库函数的复杂度和实现原理