HOT100-普通数组

发布于 2024-02-12  295 次阅读


AI 摘要

文章标题:HOT100-普通数组 正文摘要:该文章主要介绍了四个与普通数组相关的问题和解决方法。 1. 最大子数组和问题:使用动态规划,循环遍历数组元素,如果当前和小于0,则更新指针到当前数,否则继续累加,同时更新最大和。 2. 合并区间问题:对区间的左端点进行排序,然后根据左端点的顺序遍历区间,判断是否存在重合区间,如果不重合,直接加入新的数组集合,如果重合,则更新合并后的右端点。 3. 轮转数组问题:可以简单实现或者进阶实现。简单实现利用求余的方式,创建一个新数组,将原数组元素按照规则放入新数组,并使用System.arraycopy方法将新数组复制到原数组。进阶实现利用三次数组翻转,先整体翻转数组,再对前k个元素进行翻转,最后对剩余元素进行翻转。 4. 除自身以外数组的乘积问题:可以使用两个数组来保存每个元素左边和右边的乘积,然后再将两个数组相乘得到最终结果。或者使用一个数组来保存每个元素左边的乘积,并在计算右边的乘积时就进行乘法运算。 以上是对普通数组相关问题的简要介绍和解决方法的概述。

53. 最大子数组和

class Solution {
    public int maxSubArray(int[] nums) {
        //使用动态规划
        int len = nums.length;
        if(len == 1){
            return nums[0];
        }
        int max = nums[0];//用来存储最大和
        int now = 0;//用来存储当前和
        for(int i = 0;i < len;i++){
            if(now < 0){
                now = nums[i];//如果now小于0,那么更新指针到最新一位,即当前和为当前数
            }else{
                now += nums[i];//如果now大于0,那么可以继续加
            }
            if(now > max){//如果当前和大于最大和 那么更新最大和
                max = now;
                System.out.println("max = " + max);
            }
        }
        return max;
    }
}

56. 合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        //获取数组长度 
        int length = intervals.length;
        //初始化一个新的数组存放合并后的值,但是长度可能存在多余
        int[][] merge = new int[length][2];
        //对数组的左端点进行排序
        Arrays.sort(intervals,(x,y)->{
            return x[0] - y[0];
        });
        //将第一个值直接放入数组初始化
        merge[0] = intervals[0];
        //新数组中最后一个元素下标的位置
        int index = 0;
        //循环遍历
        for(int i = 1; i < length; i++){
            //因为数组中的左端点已经按照从大到小的顺序排列
            //那么当新数组中最后一个右端点如果小于当前元素的左端点,那么一定不重合,
            //直接加入新的数组集合,否则就是一定会有重合需要合并的
            if(intervals[i][0] > merge[index][1]){
                merge[++index] = intervals[i];
            }else{
                //这里要获取两个右端点的最大值作为合并后的右端点,
                //因为左端点排序之后你不知道右端点到底是大是小的。
                merge[index][1] = Math.max(intervals[i][1], merge[index][1]);
            }
        }
        //这里copy新的数组是因为原来的初始化的merge数组是最大长度length,这样如果
        //存在合并的数组之后,那么merge就存在了[0,0]这种给的元素,这样是不符合要求的
        //所以重新复制一个新的数组,长度是index+1是因为index是实际元素的下标,
        //如果变成长度要+1
        return Arrays.copyOf(merge, index+1);
        
    }
}

189. 轮转数组

class Solution {
    public void rotate(int[] nums, int k) {
        /**
            简单实现 直接求余的方式
         */
        /**
        int numlen = nums.length;
        if(numlen == 1){
            return;
        }
        int[] result = new int[numlen];
        for(int i = 0;i < numlen; i++){
            result[(i + k) % numlen] = nums[i];
        }
        System.out.println(result[1]);
        System.arraycopy(result, 0, nums, 0, numlen);
         */
        /**
            进阶实现 使用三次数组翻转实现
        */
        int numlen = nums.length;
        k = k % numlen;//注意给k求余 防止k大于numlen多轮的情况
        if(numlen == 1){
            return;
        }
        reserve(nums, 0, numlen - 1);
        reserve(nums, 0, k - 1);
        reserve(nums, k, numlen - 1);
    }

    
    public void reserve(int[] nums, int left, int right){
        int temp = 0;
        while(left < right){//左右指针翻转数组
            temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left++;
            right--;
        }
    }
}

238. 除自身以外数组的乘积

class Solution {
    public int[] productExceptSelf(int[] nums) {
        /**
        //更加清晰版 可以看到上下三角的求解过程。
        int length = nums.length;

        // L 和 R 分别表示左右两侧的乘积列表
        int[] L = new int[length];
        int[] R = new int[length];

        int[] answer = new int[length];

        // L[i] 为索引 i 左侧所有元素的乘积
        // 对于索引为 '0' 的元素,因为左侧没有元素,所以 L[0] = 1
        L[0] = 1;
        for (int i = 1; i < length; i++) {
            L[i] = nums[i - 1] * L[i - 1];
        }

        // R[i] 为索引 i 右侧所有元素的乘积
        // 对于索引为 'length-1' 的元素,因为右侧没有元素,所以 R[length-1] = 1
        R[length - 1] = 1;
        for (int i = length - 2; i >= 0; i--) {
            R[i] = nums[i + 1] * R[i + 1];
        }

        // 对于索引 i,除 nums[i] 之外其余各元素的乘积就是左侧所有元素的乘积乘以右侧所有元素的乘积
        for (int i = 0; i < length; i++) {
            answer[i] = L[i] * R[i];
        }

        return answer;
         */
        //空间复杂度为O(1)的方法直接在结果数组中操作
        int numlen = nums.length;
        if(numlen == 0) return new int[0];
        if(numlen == 1) return nums;
        int[] result = new int[numlen];
        result[0] = 1;
        int temp = 1;
        for(int i = 1;i < numlen; i++){
            result[i] = result[i - 1] * nums[i - 1];
        }

        for(int i = numlen - 2;i >= 0; i--){
            temp *= nums[i + 1];
            result[i] *= temp;
        }

        return result;
    }
}

41. 缺失的第一个正数

class Solution {
    public int firstMissingPositive(int[] nums) {
        int len = nums.length;

        /**
        要找到缺失的第一个正数,首先要确定边界
        首先如果是小于0或者大于数组长度的数,他们一定是缺位的。
        这个正数一定在[1, len + 1]这个范围内
        为什么?
        当都为负数是 1 就是最大正数
        当都在范围内时 len + 1 就是最小正数
        若有在这个区间中的数
        再一个一个排除
         */
        for(int i = 0;i < len; i++){
            while(nums[i] > 0 && nums[i] < len && nums[nums[i] - 1] != nums[i]){//这里就是找到在区间范围内的数,把他们放对位
                swapNums(nums, nums[i] - 1, i);//即这个数的值就是它的位置
            }
        }

        for(int i = 0;i < len; i++){//找到第一个不对位的数,直接返回就是最小正数
            if(nums[i] != i + 1){
                return i + 1;
            }
        }

        return len + 1;//如果到最后都没返回,证明都在范围内。返回 len + 1。
    }

    public void swapNums(int[] nums, int index1, int index2){
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
}

解题方法都写在注释里了。最后再提需要注意的点。

53注意动态规划的步骤,第一次写很顺利几分钟写完了,再写竟然写不出了。主要是now小于0更新指针到最新一位,否则可以继续加。

56注意两个函数的调用

Arrays.sort(intervals,(x,y)->{
    return x[0] - y[0];
});
return Arrays.copyOf(merge, index+1);

这两个Arrays不熟悉要记。特别sort这样可以根据第一个值排序。

189注意巧妙解法,用三个reserve翻转解决。

238比较难,注意上下三角的面积计算。说白了就是先乘前面的,再乘后面的,最后放在一起乘。想要优化空间复杂度就是在结果数组先乘前面的,再乘后面的,直接返回。

41代码比较简单,要理解这道题目的边界,第一个最小正数的判断步骤以及和边界的关系。从而找到对应值。