数组篇:977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

提示:努力生活,开心、快乐的一天

文章目录

  • 977.有序数组的平方
    • 解题思路
    • 遇到的问题
    • 代码实现
    • 题目总结
  • 209、长度最小的子数组
    • 解题思路
    • 遇到的问题
    • 代码实现
    • 题目总结
  • 59.螺旋矩阵II
    • 解题思路
    • 遇到的问题
    • 代码实现
    • 题目总结
  • 今日心得


977.有序数组的平方

题目链接:977.有序数组的平方

解题思路

  1. 暴力解法 :每个数平方后进行排序
    时间复杂度高:O(nlogn)

  2. 双指针解法 :数组为 非递减顺序整数数组,符合双指针的使用规则。
    思考两个指针如何使用?
    因为数组中是非递减整数数组,可能存在负数,所以平方以后,最大值不是在最左边就是在最右边,肯定不会在中间,所以可以 将两个指针放在数组的开头与结尾
    定义一个新数组, 起始位置(i) 结束位置(j) 平方处理后进行比较,哪个数值大,将哪个值放在新数组的末端(新数组,要求也按 非递减顺序 排序),然后将该指针进行移动, 如果i大, 移动i,即i++ ;如果j大, 移动j,即j--

遇到的问题

  1. 各种排序忘了,需要重新复习,此次暴力解法使用了sort()函数

  2. 新数组每次获取到新的值,都需要对新数字的下标➖1,即(k–)

代码实现

暴力解法

var sortedSquares = function (nums) {
    //暴力解法
    for (let i = 0; i < nums.length; i++) {
        nums[i] = nums[i] * nums[i]
    }
    let newArr = nums.sort((a, b) => { return a - b })
    return newArr
};

双指针解法

var sortedSquares = function (nums) {
    //双指针解法
    let i = 0;
    let k = j = nums.length - 1;
    let arr = new Array(nums.length).fill(0);
    while (i <= j) {
        let left = nums[i] * nums[i]
        let right = nums[j] * nums[j]
        if (left < right) {
            arr[k--] = right;
            j--;
        } else {
            arr[k--] = left;
            i++
        }
    }
    return arr
};

题目总结

非递减顺序整数数组,我理解为递增顺序整数数组,另外该题是对平方值进行排序,其实也就是绝对值,那最大的值一定在数组的两端,所以双指针分别放在数组两端进行,另外重新定义一个新的数据,来承接两个指针对比出的结果,即两者相对大的值,与暴力解法对比,相当于用空间换时间啦!


209、长度最小的子数组

题目链接:209.长度最小的子数组

解题思路

  • 暴力解法:双层for循环,第一层,子数组的末端;第二层循环,子数组的前端,时间复杂度: O(N^2),空间复杂度:O ( 1 )
  • 双指针解法:定义前后两个指针,i指针代表子数组的最后一个元素,j指针代表子数组的第一个元素,所以这也决定了 i 会比j走的更快 ,i每走一步,都向sum(子数组的和)中添加该元素,即 nums[i] ,当 sum>=targe t时,获取此时子数组的长度,即 j-i+1 ;然后sum减去nums[j], j指针前进一步,即 j++

遇到的问题

  • 暴力解法:在第一层循环中,第二层循环前,没有将sum的值归0
  • 双指针解法:获取到一个子数组的长度后,没有将sum总值减去子数组的第一位,也没有将j指针后移一位

代码实现

暴力解法

var minSubArrayLen = function (target, nums) {
    //暴力解法
    let sum = 0;//子数组的和
    let result = Infinity;//子数组的长度,此处定义一个无穷大
    for (let i = 0; i < nums.length; i++) {
        sum = 0//错误点
        for (let j = i; j < nums.length; j++) {
            sum += nums[j]
            if (sum >= target) {
                let newLen = j - i + 1;
                console.log(newLen, 'newLen')
                result = Math.min(result, newLen)
                break;
            }
        }
    }
    return result === Infinity ? 0 : result;
};

双指针解法

var minSubArrayLen = function (target, nums) {
    //双指针
    let i = j = 0;//两个指针,i表示头指针,j表示尾指针
    let sum = 0;//子数组的和
    let result = Infinity;//子数组的长度,此处定义一个无穷大
    for (; i < nums.length; i++) {
        sum += nums[i]
        while (sum >= target) {
            let newLen = i - j + 1;
            result = Math.min(result, newLen);
            sum -= nums[j]//犯错的地方-忘记是while循环,没有给循环条件
            j++//犯错的地方
        }
    }
    return result === Infinity ? 0 : result;
};

题目总结

双指针用法,在此处也称为滑动窗口,所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
    窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
  • 如何移动窗口的起始位置?
    窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
  • 如何移动窗口的结束位置?
    窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
    (图片取自代码随想录)
    数组篇:977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II_第1张图片
    可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

59.螺旋矩阵II

题目链接:59.螺旋矩阵II

解题思路

  • 看完题脑子飘过的全是边界,一堆边界
  • 坚持循环不变量原则
  • 模拟顺时针画矩阵的过程: 填充上行从左到右填充右列从上到下填充下行从右到左填充左列从下到上
  • 每画一条边都要坚持一致的左闭右开原则,所以设置offset = 1;每一边有会少遍历1个值,第二圈就是少遍历1+2个

遇到的问题

  • 判断条件没有加上起始位置的值
  • 第一个for循环报错:Cannot set properties of undefined (setting ‘3’),原因是数组边界溢出,代码需要细心,有误操作导致代码移除

代码实现

var generateMatrix = function (n) {
  let startX = 0;
  let startY = 0;//起始位置
  let loop = Math.floor(n / 2);//需要转的圈数
  let mid = Math.floor(n / 2);//中间位置;n为奇数时
  let count = 1;//填充的数据
  let offset = 1;//因为遵循左闭右开原则,所以每一边有会少遍历1个值,第二圈就是少遍历1+2个
  let res = new Array(n).fill(0).map(() => new Array(n).fill(0));//创建1个二维数组
  while (loop--) {
      let i = startX;
      let j = startY;
      //上行,左——>右,左闭右开 
      //需要从起始位置走,所以需要+startX
      for (; j < startY + n - offset; j++) {
          res[i][j] = count++
      }
      //右列,上——>下,左闭右开 
      for (; i < startX + n - offset; i++) {
          res[i][j] = count++
      }
      //下行,右——>左,左闭右开 
      for (; j > startY; j--) {
          res[i][j] = count++
      }
      //左列,下-—>上,左闭右开 
      for (; i > startX; i--) {
          res[i][j] = count++
      }
      startX++
      startY++
      offset += 2//上一圈的前后两个元素
  }
  if (n % 2 === 1) {
      res[mid][mid] = count
  }
  return res

};

题目总结

  1. 需要耐心的处理好所有的边界值
  2. offset的取值理解:一开始设为1,是因为遵循左闭右开原则,所以每一边有会少遍历1个值,见下图;后来一圈结束,offset+=2,是因为第二圈开始的时候,外层已经有一圈了,外层这一圈有左右(上下)两个值不需要去遍历。
  3. 上行和右列为什么需要在判断条件中+startX和startY?因为这两个的代表每一圈的起始 位置,所有的遍历都是从其实位置开始的,第一圈的时候两个其实位置为(0,0),即便不加 可能也没影响,但 第二圈起始位置为(1,1),不加的话就会报错
  4. 该图的理解非常重要,这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
    ps:图取自代码随想录
    数组篇:977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II_第2张图片

今日心得

好累,今天好忙
今天的题,挺难的,cpu都干烧了,不过也很有收获,理解不好,那就多看几遍,多写几遍 ,一定会有所收获,数组的总结周末再补上吧,加油,欣欣崽

你可能感兴趣的:(算法,算法,矩阵,线性代数)