代码随想录打卡|day02

学习目标:数组相关算法的学习

1.长度最小的子数组

力扣题目链接
题目描述:给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0

暴力法(时间复杂度:O(n^2))

思路:设定一个满足输出条件的数组的最小长度变量minLen,使用双重for循环实现查找长度最小的子数组的功能。外层的for循环用于获取下标为i的元素,内层的循环功能分为:1.计算从i到nums.length-1的所有元素的和sum;2.判断sum与target的大小关系,若sum>=target,则更新minLen=j - i + 1(j为内层循环的变量),反之则跳出当前循环。即通过外层for循环获取子数组的起始下标,通过内层循环获取子数组的结束下标。

代码如下(提交到力扣会超时):

//暴力法
class Solution{
    public int minSubArrayLen(int val ,int[] nums){
        //定义子数组的长度
        int minLen = 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 >= val))
                    continue; //此处不能写break,否则会导致检测不彻底
                if(!(minLen >= j - i + 1))
                    break;
                minLen = j - i + 1;
            }
        }
        return minLen == Integer.MAX_VALUE ? 0 :minLen;
    }
}
滑动窗口法(时间复杂度:O(n))

思路:我们在一个数组之中查找满足值大于target的最小子数组,首先我们定义最小子数组的左下标leftIndex,随后定义子数组的右下标rightIndex,随后动态调整leftIndex和rightIndex从而找到最小子数组。其具体流程为:首先,在for循环之中动态的更新rightIndex,并不断累加当前leftIndex到rightIndex的元素和sum,当sum>=target的时候,我们判断数组长度result和(rightIndex - leftIndex + 1)的大小关系,仅当result大的时候,我们执行result = rightIndex - leftIndex + 1,(此步骤在前面的判断外)将sum的值减去当前leftIndex对应的元素,并将leftIndex的值增加1,从而不断缩小窗口范围。

代码如下:

// 滑动窗口法
class Solution{
    public int minSubArrayLen(int target , int[] nums){
        int leftIndex = 0 ;
        int rightIndex = 0;
        int sum = 0 ;
        int result = Integer.MAX_VALUE;
        for(int i = 0 ; i < nums.length ; i++){
            rightIndex = i ;
            sum += nums[i];
            while(sum >= target){
                if(result > (rightIndex - leftIndex + 1))
                    result = rightIndex - leftIndex + 1;
                sum = sum - nums[leftIndex];
                leftIndex += 1;
            } 
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

螺旋矩阵 II

力扣题目链接

题目描述:给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

思路:此题目不涉及算法,本质是一个模拟转圈的过程,所以需要对上、下、左、右四个方向的边界做严格的限定。我们遵循上→右→下→左的顺序,依次对每个部分进行填充。
填充的代码如下:

class Solution{
    public int[][] generateMatrix(int n){
        // 定义每次填充的开始位置
        int startX = 0;
        int startY = 0;
        // 定义变量用于标明数据填充到了矩阵的第几层的
        int loop = 0 ;
        // 定义变量用于标明数据填充时的边的切换误差值
        int offset = 1 ;
        // 填充值的定义
        int count = 1;
        int i,j;
        int[][] finalMatrix = new int[n][n];
        while(loop < n/2){
            // 对上边进行填充
            for(j = startY , i =startX ; j < n - offset ; j++){
                finalMatrix[i][j] = count++;
            }

            // 对右边进行填充
            for( ; i < n - offset ; i++){
                finalMatrix[i][j] = count++;
            }

            // 对下列的数据进行填充
            for(; j > startY ; j--){
                finalMatrix[i][j] = count++;
            }

            // 对左列数据进行填充

            for(; i > startX ; i--){
                finalMatrix[i][j] = count++;
            }

            startX++;
            startY++;
            loop++;
            offset++;
        }
        // 当n为奇数的时候,对矩阵的中心点进行填充
        if(n % 2 == 1)
            finalMatrix[startX][startY] = count;
        return finalMatrix;
    }
}

开发商购买土地

题目链接
题目描述:在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。

现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分

暴力法(O(n^2))

思路:分别对行划分和列划分的结果进行计算,即在行划分的时候,计算每次划分到每一行行尾的时候前面所有元素的累加和count,用数组所有元素的和sum - 2*count即可获取按照不同的行划分出来之后A、B两公司所获土地的价格差值,随后获取最小的价格差值即可。列划分的原理同行划分。

代码如下:

// import java.util.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 m = sc.nextInt();
    int[][] matrix = new int[n][m];
    // 所有土地的总价值
    int sum = 0;
    // int min
    int result = Integer.MAX_VALUE;
    for(int i = 0 ; i < n ; i++){
        for(int j = 0 ; j < m ; j ++){
            matrix[i][j] = sc.nextInt();
            sum += matrix[i][j]; 
        }
    }

    // 计算横向划分的最小差值
    int count = 0 ;
    for(int i = 0 ; i < n ; i++){
        for(int j = 0 ; j < m ; j ++)
            count += matrix[i][j];
        result = Math.min(result,Math.abs(sum - 2*count));
    }

    // 计算纵向的最小差值
    count = 0 ;
    for(int j = 0 ; j < m ; j ++){
        for(int i = 0 ; i < n ; i++)
            count += matrix[i][j];
        
        result = Math.min(result,Math.abs(sum - 2*count));
    }
    
    System.out.println(result);
    sc.close();

    }
}
前缀和(O(n^2))

思路:前缀和的就是将按行划分和按列划分的每次的结果分别存储到与之对应的数组集合中。例如对行进行计算,第一行数据所有的结果存储在row[1],第一行和第二行的所有元素和存储在row[2]…,一直到最后一行,随后对row进行遍历,获取A、B两公司所获土地的价格差值最小的划分方法即可。

代码如下:

import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        
        int m = sc.nextInt();
        int n = sc.nextInt();
        int[][] matrix = new int[m][n];
        int sum = 0;
        int result = Integer.MAX_VALUE;

        for(int i = 0 ; i < m ; i++){
            for(int j = 0 ; j < n ; j++){
                matrix[i][j] = sc.nextInt();
                sum += matrix[i][j];
            }
        }

        int count = 0 ;
        //按照行计算前缀和
        int[] row = new int[m];
        for(int i = 0 ; i < m ; i++){
            for(int j = 0 ; j < n ; j++)
                row[i] += matrix[i][j];
        }
         
        count = 0 ;
        //按照列计算前缀和
        int[] col = new int[n];
        for(int j = 0 ; j < n ; j++){
            for(int i = 0 ; i < m ; i++)
                col[j] += matrix[i][j];
        }

        // 计算行划分最小差价
        int rowNum = 0 ;
        for(int i = 0 ; i < m ; i++){
            rowNum += row[i];
            result = Math.min(result , Math.abs(sum - 2*rowNum));
    }
        
        // 计算列划分最小差价
        int colNum = 0;
        for(int i = 0 ; i < n ; i++){
            colNum += col[i];
            result = Math.min(result , Math.abs(sum - 2*colNum));
        }
        
        System.out.println(result);
        sc.close();

    }
}

前缀和的另一种写法:

import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        
        int m = sc.nextInt();
        int n = sc.nextInt();
        int[][] matrix = new int[m][n];
        int sum = 0;
        int result = Integer.MAX_VALUE;

        for(int i = 0 ; i < m ; i++){
            for(int j = 0 ; j < n ; j++){
                matrix[i][j] = sc.nextInt();
                sum += matrix[i][j];
            }
        }

        int count = 0 ;
        //按照行计算前缀和
        int[] row = new int[m];
        for(int i = 0 ; i < m ; i++){
            for(int j = 0 ; j < n ; j++)
                count += matrix[i][j];
            row[i] = count;
        }
         
        count = 0 ;
        //按照列计算前缀和
        int[] col = new int[n];
        for(int j = 0 ; j < n ; j++){
            for(int i = 0 ; i < m ; i++)
                count += matrix[i][j];
            col[j] = count;
        }

        // 计算行划分最小差价

        for(int i = 0 ; i < m ; i++)
            result = Math.min(result , Math.abs(sum - 2*row[i]));
        
        // 计算列划分最小差价

        for(int i = 0 ; i < n ; i++)
            result = Math.min(result , Math.abs(sum - 2*col[i]));
        
        System.out.println(result);
        sc.close();

    }
}

你可能感兴趣的:(代码随想录打卡,算法)