leetcode.209 长度最小的子数组
题目描述:
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]
是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4] 输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
进阶:
- 如果你已经实现
O(n)
时间复杂度的解法, 请尝试设计一个O(n log(n))
时间复杂度的解法。
暴力解法
思路:题目要求找出数组中其总和大于等于 target
的长度最小的 连续子数组,用两层for循环实现
直接创建一个int类型的变量sum,连续的sum+=nums[i],然后删减一些前边或后边的元素,看是否仍然满足条件,以达到连续子数组的数组长度最小。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int result=Integer.MAX_VALUE; //最终的结果
int subLength=0; //也即子序列的长度
for(int i=0;i<nums.length;i++){//i为子序列的起点下标
int sum=0; //子序列的数值之和
for(int j=i;j<nums.length;j++){//j为子序列的终点下标
sum+=nums[j];
if(sum>=target){
subLength=j-i+1;
result=result<subLength?result:subLength; //不断比较,找符合条件最短的子序列
break;
}
}
}
return result==Integer.MAX_VALUE?0:result;
}
}
滑动窗口: 将O(n2)暴力解法降为O(n)
解题思路:题目需要的是大于等于target的连续的子数组,考虑用滑动窗口,不断调节子序列的首位指针使长度最小
在暴力解法中,一个for循环是滑动窗口的起始位置,另一个for循环是滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
为了避免再次陷入暴力解法的怪圈,使用滑动窗口只有一个for循环时,for循环的索引 i 就定义为滑动窗口的终点。那么滑动窗口的起点怎么定义并移动?以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:
最后找到 4,3 是最短距离。
其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。
这里窗口是满足其和>=target的长度最小的连续子数组,窗口的结束位置是for循环的int i,窗口的起点是每次跟着 i 递增而慢慢变大的left。(int left=0不能放在for循环的第一行,要放在其外面)
//双指针法:左右指针围起来的是一个连续子数组,故又称为滑动指针
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int result=Integer.MAX_VALUE; //最终的结果,也即子序列的长度
int sum=0; //子序列的数值之和
int left=0; //这个不能放在for循环里边
for(int i=0;i<nums.length;i++){//i为子序列的下标终点
sum +=nums[i];
while(sum>=target){//left为子序列的下标终点
result=Math.min(result,i-left+1);
sum -=nums[left++];
}
}
return result==Integer.MAX_VALUE?0:result;
}
}
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
leetcode.59 螺旋矩阵
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
解题思路:本题并不涉及到什么算法,就是模拟走一圈的过程,但却十分考察对代码的掌控能力。首要的是定区间(左闭右闭或左闭右开)
//方法一
class Solution {
//左闭右闭区间,使用四个变量(left, right, top, bottom)来跟踪矩阵的边界
public int[][] generateMatrix(int n) {
int[][] matrix=new int[n][n];
int total=n*n;
int count=1;
int left=0,right=n-1,top=0,bottom=n-1;
while(count<=total){
for(int i=left;i<=right;i++){
matrix[top][i]=count++;
}
top++;
for(int i=top;i<=bottom;i++){
matrix[i][right]=count++;
}
right--;
for(int i=right;i>=left;i--){
matrix[bottom][i]=count++;
}
bottom--;
for(int i=bottom;i>=top;i--){
matrix[i][left]=count++;
}
left++;
}
return matrix;
}
}
//方法2 坚持每条边左闭右开的原则
class Solution {
public int[][] generateMatrix(int n) {
int loop=0;// 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处
int[][] res=new int[n][n];
int start=0; //定义每循环一个圈的起始位置
int count=1; 用来给矩阵中每一个空格填充的数字
int i, j; //二维数组元素的下标索引
while(loop++ < n/2){
// 下面开始的四个for就是模拟转了一圈
//模拟上侧从左到右(左闭右开)
for(j=start;j<n-loop;j++){
res[start][j]=count++;
}
//模拟右侧从上到下(左闭右开)
for(i=start;i<n-loop;i++){
res[i][j]=count++;
}
//模拟下侧从右到左(左闭右开)
for(;j>=loop;j--){
res[i][j]=count++;
}
//模拟左侧从下到上(左闭右开)
for(;i>=loop;i--){
res[i][j]=count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
start++;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if(n%2==1){
res[start][start]=count; // 矩阵中间的位置,例如:n为3, 中间的位置坐标就是(1,1),n为5,中间位置坐标就是(2, 2)
}
return res;
}
}