算法刷题 2(双指针+滑动窗口+螺旋矩阵)

本文详细介绍了Java中涉及数组的几个经典问题,如有序数组平方、长度最小子数组、滑动窗口技巧以及螺旋矩阵的生成,通过实例演示了双指针和滑动窗口的使用,并提供了相关LeetCode题目链接和解题思路.
摘要由CSDN通过智能技术生成

目录

1、有序数组的平方 977

思路

注意点

2、长度最小的子数组 209

思路

注意点

拓展题目 水果成篮 904

思路

滑动窗口总结

3、螺旋矩阵 59

核心思路

拓展题目 螺旋矩阵 54

注意点

4、相关题目推荐




java数组完全解析(干货满满)-CSDN博客

上面这篇对数组讲解很详细!可以看看

1、有序数组的平方 977

力扣题目链接

思路

有序数组,往双指针想一下

思路不难:双指针头尾,弄个新数组,谁平方大就优先放到新数组的最后,然后向里面收缩直到左右节点相交

    int left = 0;
    int right = nums.length - 1;
    int[] res = new int[nums.length];//这里不能减一
    int p = nums.length - 1;
    while (left <= right){
        if (nums[left] * nums[left] < nums[right] * nums[right]){
            res[p] = nums[right] * nums[right];
            p--;
            right--;
        }else{
            res[p] = nums[left] * nums[left];
            p--;
            left++;
        }
    }
    return res;

注意点

int[] res = new int[nums.length];//这里不能减一


2、长度最小的子数组 209

力扣题目链接

长度最小的连续子数组: 还是想到双指针,但是这题连续子数组,也可称为滑动窗口

思路

  • 暴力解法:两个for,一个控头,一个往后跑
  • 巧妙:只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置,然后起始位置根据sum和target比较向后移动
  • 滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)

核心起始位置移动代码如下:

    while(sum >= target){
        result = Math.min(result, right - left + 1);
        //你得要更新sum啊
        sum -= nums[left];
        left++;

滑动窗口的原理:是右边先开始走,然后直到窗口内值的总和大于target,此时就开始缩圈,缩圈是为了找到最小值,只要此时总和还大于target,我就一直缩小,缩小到小于target为止在这过程中不断更新最小的长度值,然后右边继续走,如此反复,直到右边碰到边界。这样就保证了可以考虑到最小的情况

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

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?
  • 窗口就是满足其和 ≥ s 的长度最小的 连续 子数组。
  • 窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口起始位置就要向后移动了(也就是该缩小了)。
  • 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,保持窗口内大于sum。
public int minSubArrayLen(int target, int[] nums) {
        int left = 0, right = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;
        //一层for遍历注重点在right指针
        for (; right < nums.length; right++){
            sum += nums[right];//让右指针到初始边界
            while(sum >= target){
                result = Math.min(result, right - left + 1);
                //你得要更新sum啊
                sum -= nums[left];
                left++;
            }
        }
        return result == Integer.MAX_VALUE ? 0: result; 
    }

注意点

别忘了更新sum

Integer.MAX_VALUE

拓展题目:水果成篮 904

904.水果成篮

题目翻译成人话就是 :找至多包含两种元素的最长子串,返回其长度

思路

用滑动窗口,经典题目是sum>target的最短子串,这个相当于反过来,但是要用map,因为有元素,还有他们的值

滑动窗口变体,先动右边,直到超过2个元素;再动左边,去掉对应元素的数量,不过要注意,当数量为0时,这个元素就被删除了

注意点:这里的sum不能放在while里面判断,他要等remove完,回归到2元素的时候才能统计用的是hashmap,好好看看代码理解

int left = 0, right = 0;
        int sum = 0;
        Map<Integer, Integer> mp = new HashMap<Integer, Integer>();
        //滑动窗口变体,先动右边,直到超过2个元素
        for (; right < fruits.length; right++) {
            mp.put(fruits[right], mp.getOrDefault(fruits[right], 0) + 1);
            //如果把计算sum放在这里,会导致size=3的时候也被计算进去了
            while (mp.size() > 2) {
                mp.put(fruits[left], mp.get(fruits[left]) - 1);
                //当数量为0要把这个种类去掉
                if (mp.get(fruits[left]) == 0){
                    mp.remove(fruits[left]);
                }
                left++;
            }
            sum = Math.max(sum, right - left + 1);
        }
        return sum;

滑动窗口总结

!!!2个滑动窗口不同:最大和最小滑窗

  • 关键的区别在于,最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果。因此虽然都是滑窗,但是两者的模板和对应的贪心思路并不一样。
  • 最大滑窗是在迭代右移右边界的过程中更新结果,也就是这个变体,是在左边动完之后或者跳过while时统计,所以计算sum要在while外面,for循环最后
  • 最小滑窗是在迭代右移左边界的过程中更新结果,也就是原来经典例题,这个就是在while循环里面做统计


3、螺旋矩阵 59

59. 螺旋矩阵 II - 力扣(LeetCode)

核心思路

参看labuladong

  • 按要求赋值,然后输出矩阵即可
  • 解题的核心思路是按照右、下、左、上的顺序遍历数组,并使用四个变量圈定未遍历元素的边界。随着螺旋遍历,相应的边界会收缩,直到螺旋遍历完整个数组:

  • 套个循环四个方向迭代
  • 迭代停止:while其实已经控制了迭代次数,增加if边界判断是为了套用通用的公式
  • 采用的是两端闭合的策略,因为走完一条之后就会移一位,所以不需要考虑重复
public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int left_bound = 0, right_bound = n-1;
        int upper_bound = 0, lower_bound = n-1;
        int num = 1;

        //套个循环四个方向迭代
        while (num <= n*n){
            //在顶部从左向右遍历
            //不能让界限一直加啊,得要有个判断限制
            if (upper_bound <= lower_bound){
                for (int i = left_bound; i <= right_bound; i++){//别忘了加等号
                    matrix[upper_bound][i] = num;
                    num++;
                }
                //上边界下移
                upper_bound++;
            }

            if (left_bound <= right_bound){
                // 在右侧从上向下遍历
                for (int i = upper_bound; i <= lower_bound; i++){
                    matrix[i][right_bound] = num;
                    num++;
                }
                right_bound--;
            }

            if (upper_bound <= lower_bound){
                // 在底部从右向左遍历
                for (int i = right_bound; i >= left_bound; i--){
                    matrix[lower_bound][i] = num;
                    num++;
                }
                // 下边界上移
                lower_bound--;
            }

            if (left_bound <= right_bound){
                // 在左侧从下向上遍历
                for (int i = lower_bound; i >= upper_bound; i--) {
                    matrix[i][left_bound] = num;
                    num++;
                }
                left_bound++;
            }
        }
        return matrix;

拓展题目:螺旋矩阵 54

54.螺旋矩阵

同样的思路

注意点
  • 用一个LinkedList来输出
  • 注意二维矩阵的直接length是高度 matrix[0].length是长度
  • 循环条件是res.size() < m * n
  • 注意别忘了if判断条件,还是要加上if的判断条件,虽然正方形不用加,但是长方形会导致输出冗余数据
public List<Integer> spiralOrder(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        int upper_bound = 0, lower_bound = m - 1;
        int left_bound = 0, right_bound = n - 1;
        List<Integer> res = new LinkedList<>();//第二个l大写
        
        //控制遍历条件
        while (res.size() < m * n){
            //还是要加上if的判断条件,虽然正方形不用加,但是长方形会导致输出冗余数据
            if (upper_bound <= lower_bound) {
            // 在顶部从左向右遍历
            for (int j = left_bound; j <= right_bound; j++) {
                res.add(matrix[upper_bound][j]);
            }
            // 上边界下移
            upper_bound++;
        }
        
        if (left_bound <= right_bound) {
            // 在右侧从上向下遍历
            for (int i = upper_bound; i <= lower_bound; i++) {
                res.add(matrix[i][right_bound]);
            }
            // 右边界左移
            right_bound--;
        }
        
        if (upper_bound <= lower_bound) {
            // 在底部从右向左遍历
            for (int j = right_bound; j >= left_bound; j--) {
                res.add(matrix[lower_bound][j]);
            }
            // 下边界上移
            lower_bound--;
        }
        
        if (left_bound <= right_bound) {
            // 在左侧从下向上遍历
            for (int i = lower_bound; i >= upper_bound; i--) {
                res.add(matrix[i][left_bound]);
            }
            // 左边界右移
            left_bound++;
            }
        }
        return res;

4、相关题目推荐

过几天回来做下

5题over 拜拜~

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值