【没事儿看两道Leetcode系列】100热题之数组

数组

53. 最大子数组和

image-20250123131800629

普通做法:O(n^2) 双层循环,找到所有可能的子数组和其求和,依次与最大数组求和比较是否更新。

我选择动态规划做法,不研究分治了,贪心也不错而且写起来还简单。

简单来说就是从数组开头开始依次加每个元素,如果当前数组和为负数就可以把这部分数组和全部舍弃掉,从0开始累加下一个数组元素。因为左侧部分如果求和是负数,那么加到右侧部分只可能会让右侧的数组总和变得更小,只有舍弃掉负数部分才能取到最大值。

还有一种额外情况是形如:[-1] 这样的数组。数组中只有一个元素而且是负数,最大和就是-1而不是0,所以我们找不到比0大的数组最大和。所以我们可以设定初始数组值为一个非常小的负数,先判断当前数组和是否大于最大值,再判断当前数组和是否小于零,小于零舍弃,继续遍历。

class Solution {
    public int maxSubArray(int[] nums) {
        int sum = 0, pre = 0, temp_sum = 0, max_sum = -10000;
        for (int i = 0; i < nums.length; i++) {
            temp_sum += nums[i];
            if (max_sum < temp_sum)
                max_sum = temp_sum;
            if (temp_sum < 0)
                temp_sum = 0;
        }
        return max_sum;
    }
}

56. 合并区间

image-20250123132227721

普通做法:可以用一个大数组记录所有区间包含的所有元素,再把这个大数组转化成若干个区间的形式。

进阶做法:先对所有的区间进行排序(按左侧起点大小顺序排序。这里难点在于 Comparator 的语法),而后对于两个区间项前一项的终点>后一项的起点的情况就可以合并。合并结果是 [前一项的起点,Math.max(前一项的终点,后一项的终点)].

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals.length == 0)
            return new int[0][2];

        Arrays.sort(intervals, new Comparator<int[]>() {
            public int compare(int[] interval1, int[] interval2) {
                return interval1[0] - interval2[0];
            }
        });

        List<int[]> result = new ArrayList<int[]>();
        for (int i = 0; i < intervals.length; i++) {
            if (result.size() == 0 || result.get(result.size() - 1)[1] < intervals[i][0]) {
                result.add(new int[] { intervals[i][0], intervals[i][1] });
            } else
                result.get(result.size() - 1)[1] = Math.max(intervals[i][1], result.get(result.size() - 1)[1]);
        }

        return result.toArray(new int[result.size()][]);
    }
}

189. 轮转数组

image-20250123132832212

普通做法:开辟一块新的数组区间,把原数组 i 位置元素放到新数组 (i+k)%nums.length 的位置。时间复杂度 O(n) 空间复杂度 O(n).

reverse 做法:数组轮转其实相当于整个数组 reverse 一下,然后 [0, k-1] 区间的子数组 reverse 一下,[k, nums.length-1] 区间的子数组 reverse 一下。这个非常巧妙,最好记住。

class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length <= 1)
            return;
        k %= (nums.length);
        reverse(nums, 0, nums.length - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.length - 1);
    }

    public void reverse(int[] nums, int L, int R) {
        while (L < R) {
            int temp = nums[L];
            nums[L] = nums[R];
            nums[R] = temp;
            L++;
            R--;
        }
    }
}

还有一种环状替换方法感兴趣的同学可以研究一下。

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

image-20250123133144493

普通做法:O(n^2),对于新数组每个元素,用原数组所有元素(除对应位置上的元素)遍历相乘获得。或者说是这个元素左侧的所有元素遍历乘积(L[i])*右侧所有元素遍历乘积(R[i])。

除法做法(当然题里不让用hh):先让数组所有元素相乘,然后新数组每个元素=原数组所有元素乘积/原数组该位置上的元素。这里有一种情况需要考虑:原数组中某个元素=0的情况,比如原数组是 [-1,1,0,-3,3],那么计算新数组 index=2 位置上的元素就不能用原数组所有元素相乘再/0 计算了,乘的时候就要跳过当前位置元素=0的情况。

空间复杂度 O(1) 做法:其实就是再普通做法左右列表乘积的基础上稍做改动。我们可以发现,第i个元素的左侧列表元素乘积=第 i-1 个元素的左侧列表乘积=*第i-1个元素。这样我们就不需要存储每个元素的左侧列表右侧列表了,可以直接用一个 L 变量一个 R 变量迭代获取所有元素的左侧和右侧列表乘积。

思路不难,不过还是要自己能想到才行。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        // left
        int[] ans = new int[nums.length];
        ans[0] = 1;
        for (int i = 1; i < nums.length; i++) {
            ans[i] = ans[i - 1] * nums[i - 1];
        }

        // right
        int R = 1;
        for (int i = nums.length - 2; i >= 0; i--) {
            R *= nums[i + 1];
            ans[i] *= R;
        }

        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰海宽松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值