代码随想录day2 | Leecode 977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵II

28 阅读6分钟

977.有序数组的平方(双指针)

/**
* @param {number[]} nums
* @return {number[]}
*/
var sortedSquares = function (nums) {
  let len = nums.length;
  const result = new Array(len);// 先创建结果数组,这可以提升速度,防止空间不够内存重新分配
  let left, right;
  for (let i = 0, k = len - 1, j = k; i <= j;) {
    left = nums[i] * nums[i];
    right = nums[j] * nums[j];
    if (left < right) {
      result[k--] = right;
      j--;
    } else {
      result[k--] = left;
      i++;
    }
  }
  return result;
};

977.有序数组的平方.gif

  • let len = nums.length;:初始化变量 len 为数组 nums 的长度。

  • const result = new Array(len);:创建一个长度为 len 的新数组 result

  • let left, right;:定义了两个指针 left 和 right

  • for (let i = 0, k = len - 1, j = k; i <= j; ) {...}:从数组两端开始,向中间遍历。

  • left = nums[i] * nums[i]; 和 right = nums[j] * nums[j];:计算当前左侧和右侧元素的平方。

  • 根据大小将平方值放入 result 数组,并更新索引:

    • 如果右边的平方值较大(left < right),说明右侧的平方值应该放在 result 数组的末尾(k),即将 right 放入 result[k]
    • 如果左边的平方值较大,说明左侧的平方值应该放在 result 数组的末尾(k),即将 left  放入 result[k]
  • return result;:遍历结束后,返回排序后的平方值数组。

这个函数的主要目的是对一个给定的数字数组进行平方操作,并将结果按升序排序。它通过从数组两端向中间遍历的方式,比较每个元素的平方大小,并将其放入结果数组中,最终得到一个平方值升序排列的新数组。

209.长度最小的子数组 (动态窗口)

/**
* @param {number} target
* @param {number[]} nums
* @return {number}
*/
var minSubArrayLen = function (target, nums) {
  const len = nums.length;
  let res = Infinity;
  let sum = 0;
  let left = 0;
  for (let right = 0; right < len; right++) {
    sum += nums[right];
    while (sum >= target) {
      res = Math.min(res, right - left + 1);
      sum -= nums[left];
      left++;
    }
  }
  return res === Infinity ? 0 : res;
}

209.长度最小的子数组.gif

在一个给定的整数数组 nums 中,找到一个子数组,使得这个子数组的和大于或等于给定的目标值 target,并且这个子数组的长度是所有满足条件的子数组中最短的。

在本题中实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

解题的关键在于 窗口的起始位置如何移动,如图所示:

leetcode_209

可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

下面详细解释代码思路:

  1. 首先,获取数组 nums 的长度 len,并初始化三个变量:res 为无穷大,sum 为0,left 为 0。res 用于保存满足条件的最短子数组长度,初始化为无穷大是因为还没有找到任何满足条件的子数组。sum 用于保存当前子数组的和,left 用于指示当前子数组的左边界。
  2. 然后开始一个 for 循环,循环变量 right 从 0 开始,逐步增加到 len-1。这个 for 循环的目的是逐个尝试每个元素作为子数组的右边界。
  3. 在循环体内部,首先将当前元素 nums[right] 添加到 sum 中。这样,sum 就包含了从 nums[0] 到 nums[right] 的所有元素的和。
  4. 接着检查 sum 是否大于或等于 target。如果 sum 大于或等于 target,这意味着找到了一个子数组,其和达到或超过了目标值。此时需要更新 resMath.min(res, right-left+1) 是用来比较当前找到的子数组长度与之前找到的最短子数组长度,并取较小者。这就是更新 res 的思路。这样做是为了确保 res 始终保存着满足条件的最短子数组长度。
  5. 更新 res 之后,在这个 while 循环中,通过 sum -= nums[left] 减少 sum 的值,同时通过 left++ 向右移动子数组的左边界。这意味着我们尝试缩小子数组的范围,看看是否能够找到更短的子数组。
  6. 最后,当 for 循环结束时,res 中将保存满足条件的最短子数组长度。如果没有找到满足条件的子数组,res 将保持为无穷大。所以函数的最后返回值是 res===Infinity?0:res。如果 res 是无穷大,返回 0 表示没有找到子数组,否则返回 res 表示找到的最短子数组长度。

59.螺旋矩阵II

而求解本题依然是要坚持循环不变量原则

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

由外向内一圈一圈这么画下去。

可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人

这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。

那么我按照左闭右开的原则,来画一圈,大家看一下:

这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。

这也是坚持了每条边左闭右开的原则。

就是因为在画每一条边的时候,一会左开右闭,一会左闭右闭,一会又来左闭右开,岂能不乱。

var generateMatrix = function (n) {
  let startX = startY = 0; // 起始位置
  let loop = mid = Math.floor(n / 2); // 旋转圈数 和 中间位置
  let offset = 1; // 控制每一层填充元素个数
  let num = 1; // 更新填充数字
  let i, j;
  const arr = new Array(n).fill(0).map(item => new Array(n));
  while (loop--) {
    for (j = startY; j < n - offset; j++) {
      arr[startX][j] = num++;
    }
    for (i = startX; i < n - offset; i++) {
      arr[i][j] = num++;
    }
    for (; j > startY; j--) {
      arr[i][j] = num++;
    }
    for (; i > startX; i--) {
      arr[i][startY] = num++;
    }
    // 更新起始位置
    startX++;
    startY++;
    // 更新offset
    offset++;
  }
  // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
  if (n % 2 === 1) {
    arr[mid][mid] = num;
  }
  return arr;
};
  • 首先,函数初始化了一些变量:startXstartY作为起始点,loopmid表示循环次数和中间位置,offset控制每一层的填充数量,num是填充的数字,ij是循环使用的索引,arr是最终要返回的矩阵。
  • 利用嵌套的for循环来创建螺旋模式。每一次循环都是一次完整的螺旋线,从外层逐步向里层。
  • while循环中,使用for循环完成四条边的数字填充,接着更新startXstartYoffset以便于下次循环填充下一层。
  • while循环结束后,如果n是奇数,则给中间位置赋予最后一个数字。
  • 最终,函数返回填充完毕的矩阵arr

简而言之,这段JavaScript代码旨在构建一个n x n的矩阵,并以特定的螺旋顺序填充数字。

数组总结.png