代码随想录算法训练营Day02
长度最小的子数组
暴力解法和滑动窗口的思想
- 暴力解法
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int minLength = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) { // 起始位置
int sum = 0;
for (int j = i; j < nums.length; j++) { // 向后遍历找到结束位置
sum += nums[j];
if (sum >= target) {
minLength = Math.min(minLength, j - i + 1);
break; // 找到一个满足条件的子数组后,跳出内层循环,继续检查下一个起始位置
}
}
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
}
- 滑动窗口
当窗口不满足要求时,end指针向右拓宽边界,满足要求时,start指针向右缩小边界
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int start = 0;
int sum = 0;
int result = Integer.MAX_VALUE;
for (int end = 0; end < nums.length; end++) {// 这个end是结束的位置,和前面的暴力解法区分
sum += nums[end];
while (sum >= target) {
int length = end - start + 1;//为什么加1?假设指向同一个元素,子数组的长度为1
sum = sum - nums[start++];
result = result <= length ? result : length;
}
}
return result==Integer.MAX_VALUE?0:result;
}
}
螺旋矩阵
控制好好循环条件即可,搞懂参数表示什么
一条边的最后一个元素,是当前边处理,还是作为下一条边的第一个元素来处理?应该是左闭右开,否则无法统一循环条件
class Solution {
public int[][] generateMatrix(int n) {
int startX = 0;
int startY = 0;//i和j的起始位置
int offset = 1;//左闭右开,所以需要一个这样的变量来控制边界
int count = 1;//存入数组的数据
int loop = 1;//循环的轮数,画图找规律,需要循环n/2次
int[][] nums = new int[n][n];
while(loop<=n/2){
for(int j = startY;j<n-offset;j++){
nums[startX][j] = count++;
}
for(int i = startX;i<n-offset;i++){
nums[i][n-offset] = count++;
}
for(int j = n-offset;j>startY;j--){
nums[n-offset][j] = count++;
}
for(int i = n-offset;i>startX;i--){
nums[i][startY] = count++;
}
startX++;
startY++;
loop++;
offset++;
}
if(n%2 != 0){
nums[n/2][n/2] = n*n;
}
return nums;
}
}
区间和
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。
输出每个指定区间内元素的总和。
- 暴力解法
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] nums = new int[n];
for(int i = 0;i<n;i++){
nums[i] = sc.nextInt();
}
int a = sc.nextInt();
int b = sc.nextInt();
while(a<=b){
int sum = 0;
for(int i = a;i<=b;i++){
sum+=nums[i];
}
System.out.println(sum);
a = sc.nextInt();
b = sc.nextInt();
}
}
- 前缀和
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] nums = new int[n];
for(int i = 0;i<n;i++){
nums[i] = sc.nextInt();
}
//下面的数组也可以和上面合成一步
int[] sums = new int[n];
sums[0] = nums[0];
for(int i = 1;i<n;i++){
sums[i] = sums[i-1] + nums[i];
}
int a = sc.nextInt();
int b = sc.nextInt();
while(a<=b){
int sum = a==0?sums[b]:sums[b]-sums[a-1];//注意这里的a-1
System.out.println(sum);
a = sc.nextInt();
b = sc.nextInt();
}
}
}
开发商分配土地
将带权重的矩阵分成两部分,使得两部分权重的差尽可能的小
也是考察前缀和,这里要分清行和列,行用i表示,列用j表示
- 前缀和
public class Solution3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int sum = 0;
int result = Integer.MAX_VALUE;
int[][] nums = new int[n][m];
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
nums[i][j] = sc.nextInt();
sum+=nums[i][j];
}
}
int[] horizontal = new int[n];//每一行的元素之和
int[] vertical = new int[m];//每一列的元素之和
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
horizontal[i] += nums[i][j];
}
}
for(int j = 0; j < m; j++){
for(int i = 0; i < n; i++){
vertical[j] += nums[i][j];
}
}
//上面的代码构建了矩阵还有前缀和所需要的sums数组
int horizontalCut = 0;//这个表示前面i行的和
for(int i = 0; i < n; i++){
horizontalCut += horizontal[i];
result = Math.min(result, Math.abs((sum-horizontalCut)-horizontalCut));
}
int verticalCut = 0;
for(int j = 0; j < m; j++){
verticalCut +=vertical[j];
result = Math.min(result, Math.abs((sum-verticalCut)-verticalCut));
}
System.out.println(result);
}
}
- 暴力算法
public static int minLandValueDifference(int n, int m, int[][] grid) {
int totalValue = 0;
// 计算总土地价值
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
totalValue += grid[i][j];
}
}
int minDiff = Integer.MAX_VALUE;
// 枚举横向分割线
for (int row = 0; row < n - 1; row++) { // 分割线在第 row 行下方
int topValue = 0;
for (int i = 0; i <= row; i++) {
for (int j = 0; j < m; j++) {
topValue += grid[i][j];
}
}
int bottomValue = totalValue - topValue;
minDiff = Math.min(minDiff, Math.abs(topValue - bottomValue));
}
// 枚举纵向分割线
for (int col = 0; col < m - 1; col++) { // 分割线在第 col 列右侧
int leftValue = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= col; j++) {
leftValue += grid[i][j];
}
}
int rightValue = totalValue - leftValue;
minDiff = Math.min(minDiff, Math.abs(leftValue - rightValue));
}
return minDiff;
}
前缀和的做法,不需要每次都遍历整个矩阵,只需要遍历两个数组即可,时间复杂度会小很多

浙公网安备 33010602011771号