前言
- 只用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;
}
}
3727

被折叠的 条评论
为什么被折叠?



