题目:209. 长度最小的子数组 - 力扣(LeetCode)
讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili
思路一:暴力两层循环
class Solution {
public:
int minSubArrayLen(int s, vector& nums) {
int result = INT32_MAX; // 最终的结果
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= s) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == INT32_MAX ? 0 : result;
}
};
后面力扣更新了数据,暴力解法已经超时了。
思路二:滑动窗口
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
滑动窗口的本质是双指针,一个指针控制左区间,一个指针控制右区间
用一层for循环代替两层,这个循环中的j是终止位置
滑动窗口的精华:如何移动起始位置
在本题中实现滑动窗口,主要确定如下三点:
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
第一遍错误代码:错误原因:Integer拼写错误;没有考虑不满足要求的情况
public int minSubArrayLen(int target, int[] nums) {
int i=0,j=0; //起始、终止位置
int result = Integer.MAX_VALUE;
int sum = 0;
int subLength = 0;
for( ; j= target){
result = Math.min(subLength,result);
sum -= nums[i]; //移动滑动窗口起始位置
i++;
subLength = j-i+1;
}
}
return result;
}
解决:加一个flag标志位判断是否有满足条件的情况
或者在最后返回时判断
return result == Integer.MAX_VALUE ? 0 : result;
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i=0,j=0; //起始、终止位置
int result = Integer.MAX_VALUE;
int sum = 0;
int subLength = 0;
boolean flag = false;
for( ; j= target){
flag = true;
result = Math.min(subLength,result);
sum -= nums[i]; //移动滑动窗口起始位置
i++;
subLength = j-i+1;
}
}
if(flag)return result;
else return 0;
}
}
一些录友会疑惑为什么时间复杂度是O(n)。
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
补充:java中基本数据类型的最大值
public class PrimitiveTypeMaxValues {
public static void main(String[] args) {
System.out.println("byte 最大值: " + Byte.MAX_VALUE); // 输出: 127
System.out.println("short 最大值: " + Short.MAX_VALUE); // 输出: 32767
System.out.println("int 最大值: " + Integer.MAX_VALUE); // 输出: 2147483647
System.out.println("long 最大值: " + Long.MAX_VALUE); // 输出: 9223372036854775807
System.out.println("float 最大值: " + Float.MAX_VALUE); // 输出: 3.4028235E38
System.out.println("double 最大值: " + Double.MAX_VALUE); // 输出: 1.7976931348623157E308
}
}
final
关键字将其定义为常量。Collections.max()
方法来获取。题目:
59. 螺旋矩阵 IIj
讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
这道题目可以说在面试中出现频率较高的题目,转圈:边界控制,模拟过程,但却十分考察对代码的掌控能力。
如果要写出正确的二分法一定要坚持循环不变量原则。
而求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
由外向内一圈一圈这么画下去。
class Solution {
public int[][] generateMatrix(int n) {
int count = 1;
int startX = 0,startY = 0;
int offset = 1;
int i,j;
int nums[][] = new int[n][n];
for (int rotate = 1; rotate <= n/2; rotate++){
for (j = startY; j < n - offset; j++){
nums[startX][j] = count++;
}
for (i = startX ; i < n - offset; i++){
nums[i][j] = count++;
}
for (; j > startY; j--){
nums[i][j] = count++;
}
for (; i > startX; i--){
nums[i][j] = count++;
}
startX++;
startY++;
offset++;
}
if (n%2==1){ //奇数
nums[n/2][n/2] = count++;
}
return nums;
}
}
58. 区间和(第九期模拟笔试)
数组常用解题技巧:前缀和
前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。
前缀和 在涉及计算区间和的问题时非常有用!
例如,我们要统计 vec[i] 这个数组上的区间和。
我们先做累加,即 p[i] 表示 下标 0 到 i 的 vec[i] 累加 之和。
如图:
如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。
第一次用ACM输入输出模式,并且尝试使用纸笔计算模拟
1. import的地方要注意
2. public class Main{} 不能加()
3. 通过 sc.hasNextInt()来控制循环条件
4. 最后记得关闭scanner
import java.util.Scanner;
public class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
int[] sum = new int[n];
arr[0] = sc.nextInt();
sum[0] = arr[0];
for(int i=1;i
前缀和
这个还没过,下次看
44. 开发商购买土地(第五期模拟笔试)
看到本题,大家如果想暴力求解,应该是 n^3 的时间复杂度,
一个 for 枚举分割线, 嵌套 两个for 去累加区间里的和。
如果本题要求 任何两个行(或者列)之间的数值总和,大家在0058.区间和 的基础上 应该知道怎么求。
就是前缀和的思路,先统计好,前n行的和 q[n],如果要求矩阵 a行 到 b行 之间的总和,那么就 q[b] - q[a - 1]就好。
至于为什么是 a - 1,大家去看 0058.区间和 的分析,使用 前缀和 要注意 区间左右边的开闭情况。
本题也可以使用 前缀和的思路来求解,先将 行方向,和 列方向的和求出来,这样可以方便知道 划分的两个区间的和。
import java.util.Scanner;
public class Main{
public static void main(String args[]){
Scanner sc= new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int subSum = 0;
int totalSum = 0; //所有元素的总和
int arr[][] = new int[n][m];
int row[] = new int[n];
int col[] = new int[m];
int result = Integer.MAX_VALUE;
for(int i = 0; i < m; i++){
arr[0][i] = sc.nextInt();
subSum += arr[0][i];
}
row[0] = subSum;
totalSum += row[0];
for(int i = 1; i < n; i++){
subSum = 0;
for(int j = 0; j < m; j++){
arr[i][j] = sc.nextInt();
subSum += arr[i][j];
}
row[i] = row[i-1] + subSum;
totalSum += row[i];
}
subSum=0;
//求col
for(int i = 0; i < n; i++){
subSum += arr[i][0];
}
col[0] = subSum;
result = Math.min(result, Math.abs( 2 * col[0] - totalSum));
for(int j = 1; j < m; j++){
subSum = 0;
for(int i = 0; i < n; i++){
subSum += arr[i][j];
}
col[i] = col[i-1] + subSum;
//直接进行比较
result = Math.min(result, Math.abs( 2 * col[i] - totalSum));
}
for(int i = 0; i < n; i++){
result = Math.min(result, Math.abs( 2 * col[i] - totalSum));
}
System.out.println(result);
sc.close();
}
}