参考资料:代码随想录
前言
本文是算法学习记录系列贴第二篇,主要还是围绕数组的相关知识进行解题。关于数组的基本定义、特点以及常用的二分法和双指针,感兴趣的朋友可以移步至本系列贴第一篇进行了解🤞
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中上下左右四个变量的控制上。加油💪