代码随想录算法训练营第二天 | 977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

第二天记录

卡哥文章与题目的传送门 :

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
  • 对开闭区间的概念理解还是不清晰
  • 下次调用库函数的时候,可以适当考虑库函数的复杂度和实现原理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值