977.有序数组的平方
1、首次代码(AC)
class Solution {
public int[] sortedSquares(int[] nums) {
int slowIndex = 0;
for(int fastIndex = 0;fastIndex < nums.length;fastIndex++){
nums[slowIndex] = nums[fastIndex] * nums[fastIndex];
slowIndex++;
}
int temp = 0;
for(int i = 0;i < nums.length; i++){
for(int j = i+1 ; j < nums.length ;j++){//遍历少一个就行
if(nums[i] > nums[j]){
temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums;
}
}
2、二次代码
class Solution {
public int[] sortedSquares(int[] nums) {
int leftIndex = 0,rightIndex = nums.length - 1;
int[] newNums = new int[nums.length];
int index = nums.length - 1;
while(leftIndex <= rightIndex){
if(nums[leftIndex]*nums[leftIndex] > nums[rightIndex]*nums[rightIndex]){
newNums[index--] = nums[leftIndex] * nums[leftIndex++];
}else {
newNums[index--] = nums[rightIndex] * nums[rightIndex--];
}
}
return newNums;
}
}
3、分析
(1)首次代码用的是最容易想到的思路,即用同向双指针构造一个新的数组(快指针用来遍历,慢指针用来指向新数组的新元素的下标),新数组存取的是原数组元素的平方,然后再用冒泡排序将新数组按照非递减顺序重排。
优点:实现简单。
缺点:时间复杂度爆了。
(2)为了降低时间复杂度,**我们可以发现元素的最大值总是出现在数组的左右端,类似于一个开口向上的二次函数。从这里入手的话我们可以将数组从左右两端开始遍历,即使用相向的双指针指向数组两端,通过比较大小将其从新数组的末端开始往前赋值。**赋值过后将数组的两个指针往中间的方向移动,直至左右指针重合,由此我们可以得出while循环的条件(leftIndex <= rightIndex),之所以要用“=”是因为当左右指针相等的时候,数组中间的元素还没有进行平方赋到新数组里面,此时不管是哪个方向的指针取得该元素的下标都行,此方法大大降低了代码的时间复杂度。
总结:该方法的精髓就是相向双指针可以直接取到我们构成新数组的逆序元素,即不必再另外排序。
209.长度最小的子数组
1、首次代码(BC)
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum = 0;
int temp = nums.length;
for(int fastIndex = 0 ;fastIndex < nums.length; fastIndex++){
for(int slowIndex = fastIndex + 1;slowIndex < nums.length; slowIndex++){
sum = nums[fastIndex] + nums[slowIndex] + sum;
if(sum >= target){
if(sum < temp){
temp = slowIndex - fastIndex + 1;
}else{
temp = slowIndex - fastIndex + 1;
sum = 0;
continue;
}
}else if(sum < target && slowIndex == nums.length -1){
return 0 ;
}
}
}
return temp;
}
}
2、二次代码
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int result = Integer.MAX_VALUE;
int sum = 0;
for(int right = 0;right < nums.length ;right++){
sum += nums[right];
while(sum >= target){
result = Math.min(result,right - left + 1);
sum -= nums[left++];
}
}
return result == Integer.MAX_VALUE? 0:result;
}
}
3、分析
(1)从同向双指针入手,很容易通过暴力解法用两个for循环进行遍历并加上判断条件解题,但是时间复杂度也爆了,这里需要用到**滑动窗口(慢指针并不只是指向新数组的新元素的下标,里面还加了判断用来决定是否需要更新慢指针)。**其实滑动窗口更像是暴力解法的优化,它也是同向双指针的使用。
(2)result指的是连续子数组的长度。由于需要排查连续子数组的最小长度,所以在初始化时将其赋为整型最大值。快指针(right)指向滑动窗口的右端,使用for循环时快指针负责将数组元素进行叠加。慢指针指向滑动窗口的左端,当快指针的位置满足其和大于等于target的时候,使用内置函数Math.min()先确定当前的连续子数组的最小长度,然后慢指针用sum减去自己指向元素的值,此减法用来应付[1,1,1,1,1,100] && target=100的情况,以此循环求得长度最小。
总结:慢指针先暂定一个位置,当快指针达到窗口右端边界后并且也暂定后,慢指针再向右端移动,直至确定长度最小的连续子数组,最后返回长度right - left + 1。
59.螺旋矩阵II
1、首次代码(BC)
class Solution {
public int[][] generateMatrix(int n) {
int[][] a = new int[n][n];
int count = 1;
for(int i=0; i<n-1 ;i++){
a[0][i] = count++;//
}
for(int j=0; j<n-1 ;j++){
a[j][n-1] = count++;//
}
for(int k=n-1; k>0 ; k--){
a[n-1][k] = count++;//
}
for(int x=n-1; x>0 ; x--){
a[x][0] = count++;
}
return a;
}
}
2、二次代码
class Solution {
public int[][] generateMatrix(int n) {
int[][] a = new int[n][n];
int loop = 0;//循环次数
int start = 0;
int count = 1;
int i,j;
while(loop++ < n/2){
for( i=start; i<n-loop; i++){
a[start][i] = count++;
}
for( j=start; j<n-loop; j++){
a[j][i] = count++;
}
for(; i>=loop; i--){
a[j][i] = count++;
}
for(; j>=loop; j--){
a[j][i] = count++;
}
start++;
}
if(n % 2 == 1){
a[start][start] = n*n;
}
return a;
}
}
3、分析
(1)看到题目,画了个图,通过循环不变量原则(循环规定的规则)进行矩阵的模拟,在首次代码实现的过程中一直在琢磨:
1、如何确定循环个数将循环的第二次开启?
2、开启之后使用什么条件停止(while循环的条件书写)?
3、当然还有未曾想到的奇偶n的实现
4、以及for循环中平常未曾注意的 i 和 j 的阶段性使用。
(2)观察矩阵,我们可以知道,每个循环的开始都是由(1,1),(2,2)…(n,n)开始,于是我们可以在对横纵坐标进行初始化的时候将其初始化为变量start,通过在循环末尾将start++便可以开启下一次循环。
(3)通过演算,我们可以知道循环次数由n决定(因为只有n一个参数,遇事不决就考虑n)。当n为奇数时,循环次数是n/2次循环再加上中间那个点;当n为偶数时,循环次数是n/2。于是我们可以得出while的条件的书写等式:loop == n/2 。当loop小于n/2时,while循环一直执行。
这里将loop初始化为0,当执行第一次判断之后loop++,这是因为我们在for循环中书写终止条件的时候需要对边界进行约束,在第n次循环约束n个元素的遍历。于是loop除了作为循环次数,还作为遍历边界时的约束条件如:i<n-loop,i>=loop,以此进行等长度的螺旋矩阵。
(4)通过(3)分清奇偶数的书写后,奇数矩阵中间点可以直接用 if 判断来实现补充。
(5)因为该数组是二维数组,所以在初始化变量时不能用四个字母表示变量,这样会割裂了二维数组行列之间的联系。我们在定义变量的时候应该用两个字母 i 和 j 表示,i 和 j 在++之后的值,仍可以作为后续循环的初始条件进行使用。