Hot100中的:普通数组专题

前言

  • 只用java

53.最大子数组和

关键信息一句话总结:

负数前缀对后面没有贡献,直接扔掉。如果前面是负收益,那就重开。

  • 分析:寻找最大的连续的子数组的和,联想之前的滚动数组,可以维护一个利润count,如果count < 0 说明我们正在亏损,count > 0 说明我们正在增加,当count < 0 时我们停止维护,而是重新开始累计连续数组元素的和,直到它让我们亏损
class Solution {
    public int maxSubArray(int[] nums) {
        // max有可能是负的 所以不是所有的max都设置为0
        int max = nums[0];
        // 滚动数组法
        int count = 0;
        for(int i = 0; i < nums.length; i ++ ) {
            count += nums[i];
            max = Math.max(max, count);
            if(count < 0) {
                count = 0;
            } 
        }
        return max;
    }
}
  • 反思:没有考虑数组元素有可能是负数,所以max不能初始化为0,求这种连续的最大子数组的和,不能使用滑动窗口,滑动窗口不太适用于这种决策型结果,它更适合有约束条件的题目,比如不重复的连续字符串,最长或最短

56.合并区间

关键信息一句话总结:

先按左端点排序,再用一个区间不断向右合并重叠区间。

  • 分析1:我们需要合并每个区间,就是数学上的取并集,那么我们需要从最小的区间开始,从左往右开始合并,首先想到我们要排序,排序的规则是区间数组的第0个元素的大小,那么第二个问题就是如何合并,区间数组A的第1个元素比区间数组B的第0个元素大并且比B的第1个元素小,这个时候A和B可以合并为A数组的第0个元素 + B数组的第1个元素
  • 分析2:List的类型是引用类型,int[]也是引用类型,包装类也是引用类型,而基础类型就不行
  • 分析3:res.toArray(new int[res.size()][]),把res转化成int[res.size()][]数组

解法1

class Solution {
    public int[][] merge(int[][] intervals) {
        // 为什么会想到排序
        // 因为在数轴上从左到右依次排序更好合并
        Arrays.sort(intervals, (o1,o2) -> {
            return o1[0] - o2[0];
        });
        List<int[]> result = new ArrayList<>();
        int left = intervals[0][0];
        int right = intervals[0][1];
        for(int i = 1; i < intervals.length; i++) {
            int mergeLeft = intervals[i][0];
            int mergeRight = intervals[i][1];
            if(mergeLeft <= right) {
                // 注意数最大值 万一mergeRight小于right呢
                right = Math.max(mergeRight, right);
            } else {
                result.add(new int[]{left, right});
                left = mergeLeft;
                right = mergeRight;
            }
        }
        // 因为最后一个区间没有它的合并区间
        result.add(new int[]{left, right});
        // 列表转数组的方法
        return result.toArray(new int[result.size()][]);
    }
}

本人的解法

class Solution {
    public int[][] merge(int[][] intervals) {
        // 1.排序
        Arrays.sort(intervals, (o1, o2) -> {
            return o1[0] - o2[0];
        });
        // 记录结果 里面装的是已经合并的区间
        List<int[]> res = new ArrayList<>();
        // 2.重叠
        for (int i = 0; i < intervals.length; i++) {
            if (res.isEmpty() || res.get(res.size() - 1)[1] < intervals[i][0]) {
                res.add(new int[] { intervals[i][0], intervals[i][1] });
            } else {
                res.get(res.size() - 1)[1] = Math.max(res.get(res.size() - 1)[1], intervals[i][1]);
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}
  • 我没分析到需要排序,同时合并的时候,不应该原数组上继续合并,而应该是在合并后的结果res中的区间上与没有合并的interval进行合并,而不是只在interval上合并

189.轮转数组

关键信息一句话总结:

取模是为了消除转圈;三次翻转是先把尾巴搬到头,再分别把两段的顺序翻回来。

  • 分析:将数组的元素向右轮转,超出数组长度就会从数组的0号位进行轮转,那么自然而然地相当需要装桶,比如向右移动n,那就相当于移动了k = n % nums.length,因为会有循环,如果不取余的话,会循环转圈,可以将全部数组翻转一次,将后面的元素放到前面去,前面的元素放到后面去,因为我们的k能够保证后面的元素能够经过第二次翻转后变成前面的元素了,并且不会超出这个数组,因为我们已经装桶了,而第三次翻转 我们将之前前面的元素还原,保持顺序
class Solution {
    public void rotate(int[] nums, int k) {
        // 翻转
        int n = nums.length;
        k = k % n;
        // 1.全部翻转
        reverse(nums, 0, n - 1);
        // 2.翻转前k个
        reverse(nums, 0, k - 1);
        // 3.翻转剩余部分
        reverse(nums, k, n - 1);
    }

    /**
        双指针翻转数组
    */
    private void reverse(int[] nums, int left, int right) {
        while(left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left++;
            right--;
        }
    }
}

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

关键信息一句话总结:

分别计算每个位置的“左侧乘积”和“右侧乘积”,两者相乘即为答案(不能用除法)。

  • 分析:先用暴力想问题,循环的时候把当前数跳过就行了,不断相乘就可以了,但是怎么优化呢,可以分为左右部分乘积,相乘就可以了,这道题其实有点像接雨水那道题,利用前缀和的思想,先把左半部分的乘积记录下来,再从右边开始扫描,将右半部分的乘积乘进去,这就得到了最终的乘积
class Solution {
    public int[] productExceptSelf(int[] nums) {
        // 其实就是前缀和的思想
        int n = nums.length;
        int[] answer = new int[n];
        // 1.left
        answer[0] = 1;
        for(int i = 1; i < n; i++) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }
        // 2.right
        int rightSum = 1;
        for(int i = n - 1; i >= 0; i--) {
            answer[i] = answer[i] * rightSum;
            rightSum *= nums[i];
        }
        return answer;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值