目录
上面这篇对数组讲解很详细!可以看看
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
题目翻译成人话就是 :找至多包含两种元素的最长子串,返回其长度
思路
用滑动窗口,经典题目是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
核心思路
参看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
同样的思路
注意点
- 用一个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 拜拜~