从小白到高手 |代码随想录| day2|数组part02

参考资料:代码随想录

前言

本文是算法学习记录系列贴第二篇,主要还是围绕数组的相关知识进行解题。关于数组的基本定义、特点以及常用的二分法和双指针,感兴趣的朋友可以移步至本系列贴第一篇进行了解🤞
ps: 本系列算法题主要来源于力扣,使用Java语言进行编程。


leetcode题目

977.有序数组的平方

题目描述

题目链接
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 :

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

解题记录

提取关键词:非递减顺序排序
思路:题目要求返回每个数字的平方经过非递减顺序排序后组成的新数组。
看到题目时,我首先想到的方法是先对数组中每个数都进行平方,即遍历数组,把每个位置上的数都换成原数字的平方,然后再对这个数组进行排序。

class Solution {
    /**
    思路:先获取平方之后的数组,然后进行排序
     */
    public int[] sortedSquares(int[] nums) {
        int[] resArr = new int[nums.length];
        for (int i = 0; i < nums.length; ++i) {
            resArr[i] = nums[i] * nums[i];
        }
        Arrays.sort(resArr);
        return resArr;
    }
}


当然,这种方法有点小暴力的意思,感觉可以更优雅一些,所以我想到了另一种方法,就是用上一篇文章中提过的双指针了。我们可以构造两个指针,分别从数组的左右两边往中间移动,然后把左右的数进行平方之后取较大的数落入新数组之中,取了哪边的数,哪边的指针就往前走一步。
当然,落入新数组的时候是从后往前落(毕竟是取较大的数哈哈哈),那为什么不是取较小的数从前往后落呢?这是因为数组中可能包含负数,在经过平方之后的比较中,取得的较小的数不一定是数组中最小,比如示例中的数组 [-4,-1,0,3,10],第一次比较的结果是较小的数16落在新数组的第一个位置,第二次比较时较小的数是1,但是1又比16小,不能落在第二个位置,这样就出现问题了。而取较大的数从后往前落就不会有这个问题。

class Solution {
    /**
    思路:双指针,一左一右,平方之后比较大小,取较大的那个落入新的数组中,被取值的那边向前移动
     */
    public int[] sortedSquares(int[] nums) {
        //左指针
        int left = 0;
        //右指针
        int n = nums.length-1;
        // 新数组
        int [] res = new int[n+1];
        //新数组的下标
        int index = n;
        
        while(left<=n){
            //当左边小于右边,区间有效
            int leftNum = nums[left];
            int rightNum = nums[n];
            //比较大小
            if(leftNum*leftNum<rightNum*rightNum){
                res[index] = rightNum*rightNum;
                //如果右边的比较大,取右边的值落入新的数组,同时右边的指针向前移动一位
                n--;
            }else{
                res[index] = leftNum*leftNum;
                left++;
            }
            //不管怎么样,落入新数组的位置总是向左边移动
            index--;
        }
        return res;
    }
}

209.长度最小的子数组

题目描述

题目链接
给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续
子数组
[numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 :

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

解题记录

滑动窗口,它来了!!
滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。本质上也是双指针。
在使用滑动窗口时,需要注意的点是:

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

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

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

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

class Solution {

/**
    滑动窗口,其实本质上还是双指针
    预先定义一个结果长度 res = Integer.MAX_VALUE(如果最后res没有变化,说明没有数组中没有符合要求的,返回0)
    遍历数组,
    在遍历的过程中统计区间值的和,如果和大于等于目标值,左指针向前移动
    

 */

 public int minSubArrayLen(int s, int[] nums) {
       //左指针
       int left  = 0;
       //结果长度
       int res = Integer.MAX_VALUE;
       //连续子数组的和
       int sum = 0;
       //遍历数组
       for(int i = 0;i<nums.length;i++){
            //统计当前子数组的和
            sum += nums[i];
            //如果当前子数组的和大于目标值,操作子数组
            while(sum>=s){
                //结果取当前子数组的长度
                res = Math.min(res,i-left+1);
                //子数组的和减去左指针对应的值
                sum = sum-nums[left];
                //左指针往前移动一位
                left++;
            }
       }
       //如果没有对应上的,返回0
       return res==Integer.MAX_VALUE ? 0:res;
    }

}

59. 螺旋矩阵 II

题目描述

题目链接
给你一个正整数 n ,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例 :
在这里插入图片描述

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

解题记录

可以模拟示例中数字的走向完成对矩阵的填充,每次循环就从上下左右方向完成一圈数字的填充,每次循环要把上下左右的边界都往内缩,每次填充数字就加1,当数字到达n^2时结束循环。重点是要理清四个方向上的可用区间,具体来看一下代码实现吧。

class Solution {
    /**
    思路:模拟示例,从上下左右方向填充数据
        因为是二维数组,最后的矩阵肯定是int[n][n],上下左右四个角的坐标记为(0,0)、(0,n-1),(n-1,0),(n-1,n-1)
        以左上角为起始点开始填充
     */
    public int[][] generateMatrix(int n) {
        //上边界
        int t = 0;
        //下边界
        int b = n-1;
        //左边界
        int l = 0;
        //右边界
        int r = n-1;

        //要填充的数组
        int [][] res = new int[n][n];
        //填充的数字,从1开始填充
        int k = 1;
        //最大的数字,n*n
        int max = n*n;

        while(k<=max){
            //当填充的数字小于等于最大值时,循环有效

            for(int i=l;i<=r;i++){
                res[t][i] = k++;//从左到右填充最上面的一行,每次填完一个k就加1
            }
            t++; //然后最上面的一行往下走

            for(int i = t;i<=b;i++){
                res[i][r] = k++;//从上到下填充最右边的一列,每次填完一个k就加1
            }
            r--;//然后最右边的一列往左走

            for(int i = r;i>=l;i--){
                res[b][i] = k++;//从右到左填充最下边的一列,每次填完一个k就加1
            }
            b--;//然后最下边的一行往上走

            for(int i=b;i>=t;i--){
                res[i][l] = k++;//从下到上填充最左边的一列,每次填完一个k就加1
            }
            l++;//然后最左边的一列往右走
        }
        return res;
    }
}

三、总结

可以感受到今天的题比昨天的难一些,主要还是集中在双指针的灵活应用以及处理各种边界问题,特别是螺旋矩阵 II中上下左右四个变量的控制上。加油💪

  • 15
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值