一 LC189.轮转数组
题目要求:
给定一个整数数组
nums
,将数组中的元素向右轮转k
个位置,其中k
是非负数。
思路分析:
我们根据题目不难看出,本题要求我们将数组中每个元素右移k位,当k小于数组长度length时,也就是将nums[i]放到nums[i+k]的位置上,如果元素右移后超出了数组长度,则其需要移动到nums[i+k-length]的位置上。
此外,当k大于数组长度,那么我们不难发现,右移k位与右移k % length位的结果是相同的(此规律k小于length时同样适用)。因此,我们可以通过k = k % n(n为数组长度)来简化该算法。
继续观察k小于数组长度时的情况,我们可以发现以k位划分,前k个元素的相对顺序和剩余k-n个元素的相对顺序是与原数组相同的,且数组的最后一个元素一定位于nums[k-1]的位置上,数组的首个元素一定位于nums[k]的位置上。
那么,如何用代码实现这个规律呢?我们可以使用“三次反转”的思想,也就是分别反转整个数组、 反转前k个元素部分、反转剩余元素部分。
经历过三次反转后的数组,就是我们希望得到的数组了。
完整代码示例:
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
// 防止 k 大于数组长度时进行多余的轮转
k = k % n;
// 第一步:反转整个数组
reverse(nums, 0, n - 1);
// 第二步:反转前 k 个元素
reverse(nums, 0, k - 1);
// 第三步:反转剩余的 n-k 个元素
reverse(nums, k, n - 1);
}
// 反转函数,用于反转数组中指定范围的元素
public static void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
}
二 LC56.合并区间
题目要求:
以数组
intervals
表示若干个区间的集合,其中单个区间为intervals[i] = [starti, endi]
。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
思路分析:
我们可以将题目转化成下面的图片,当输入的两个区间存在交集时,就将这两个区间合并(注意是合并整个区间,而不是交集的部分),并返回所有合并后的区间和未被合并的区间。
不难观察到,判断两个区间是否存在交集的一个快速的方法就是比较一个区间的末尾元素和另一个区间的首个元素的大小,如果首个元素要小于或等于另一个区间的末尾元素,就说明两个区间存在交集。为了简化计算,我们可以先根据每个区间的首个元素大小进行排序,确保比较的次数尽可能少。之后就是一次对区间进行遍历,如果当前区间的起点大于前一个区间的终点,则说明它们不重叠,可以直接将当前区间加入结果集。否则更新前一个区间的终点为两者的较大值,以完成。当遍历结束,我们将结果集转换为一个二维数组作为输出结果即可。
完整代码示例:
class Solution {
public int[][] merge(int[][] intervals) {
// 如果区间列表为空或只有一个区间,直接返回
if (intervals.length <= 1) {
return intervals;
}
// 按区间的起点进行排序
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
// 用于存储合并后的区间
List<int[]> merged = new ArrayList<>();
// 初始化,先将第一个区间放入
int[] currentInterval = intervals[0];
merged.add(currentInterval);
// 遍历剩余的区间
for (int[] interval : intervals) {
// 获取当前合并区间的终点
int currentEnd = currentInterval[1];
// 如果当前区间的起点小于等于上一个区间的终点,说明有重叠,进行合并
if (interval[0] <= currentEnd) {
// 更新当前合并区间的终点为较大的终点
currentInterval[1] = Math.max(currentEnd, interval[1]);
} else {
// 否则,说明没有重叠,更新当前区间并加入结果集
currentInterval = interval;
merged.add(currentInterval);
}
}
// 将合并后的区间列表转换为二维数组返回
return merged.toArray(new int[merged.size()][]);
}
}
三 LC53.最大子数组和
题目要求:
给你一个整数数组
nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
思路分析:
对于本题我们可以使用动态规划来求解。首先,定义一个变量currentSum
表示,如果当前和为负数,我们就从下一个元素重新开始计算子数组的和。同时,定义一个变量 maxSum
来记录最大子数组的和,在遍历数组的过程中不断更新这个最大值。
之后我们遍历数组元素,比较当前元素和当前子数组的和加上当前元素的值,选择较大的值作为新的当前和currentSum
。然后更新maxSum
为当前和与最大子数组的和中较大的一个,确保 maxSum
始终保持最大的子数组和。
完整代码示例:
class Solution {
public int maxSubArray(int[] nums) {
// 初始化最大和为数组的第一个元素
int maxSum = nums[0];
// 当前子数组和也初始化为第一个元素
int currentSum = nums[0];
// 从第二个元素开始遍历
for (int i = 1; i < nums.length; i++) {
// 动态规划转移方程,选择当前元素和当前元素加上之前的子数组和之间的较大值
currentSum = Math.max(nums[i], currentSum + nums[i]);
// 更新最大和
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}