0%

力扣top100-普通数组


普通数组

53.最大子数组和

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

示例 1:

1
2
3
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

1
2
输入:nums = [1]
输出:1

示例 3:

1
2
输入:nums = [5,4,-1,7,8]
输出:23

方法一:

1.使用动态规划,定义**dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]**。

2.确定递推公式,dp[i]只有两个方向可以推出来:

  • dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

3.初始化:dp[i]是依赖于dp[i - 1]的状态,dp[0]就是递推公式的基础。

  • dp[0] = nums[0]

4.确定遍历顺序

递推公式中dp[i]依赖于dp[i - 1]的状态,需要从前向后遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int maxSubArray(int[] nums) {
if (nums.length == 0) {
return 0;
}
//dp[i]表示以i为结尾的最大子数组和
int[] dp = new int[nums.length];
dp[0] = nums[0];
int ans = dp[0];
for (int i = 1; i < nums.length; i++) {
dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
ans = Math.max(dp[i], ans);
}
return ans;
}
}

56.合并数组

56.合并数组

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

示例 1:

1
2
3
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

1
2
3
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

将列表中的区间按照左端点升序排序。然后将第一个区间加入 merged 数组中,并按顺序依次考虑之后的每个区间:

  • 如果当前区间的左端点在数组 merged 中最后一个区间的右端点之后,那么它们不会重合,我们可以直接将这个区间加入数组 merged 的末尾;
  • 否则,它们重合,我们需要用当前区间的右端点更新数组 merged 中最后一个区间的右端点,将其置为二者的较大值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public int[][] merge(int[][] intervals) {
if (intervals.length == 0) {
return new int[0][2];
}
//根据左端点排序
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
List<int[]> ans = new ArrayList<int[]>();
int num = 0;
for (int i = 0; i < intervals.length; i++) {
int l = intervals[i][0], r = intervals[i][1];
//第一次加入到ans,或者是区间的左端点大于前一个区间的右端点,两个区间不相交
if (i == 0 || ans.get(ans.size() - 1)[1] < l) {
ans.add(new int[]{l, r});
} else {
//两个区间相交,由于之前已经排过序了,左端点是不用改变的,
//只需要比较右端点谁大,取最大值即可
ans.get(ans.size() - 1)[1] = Math.max(r, ans.get(ans.size() - 1)[1]);
}
}
//将list转成二维数组
return ans.toArray(new int[ans.size()][2]);
}
}

189.旋转数组

189.旋转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

1
2
3
4
5
6
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

方法一:使用额外的数组
我们可以使用额外的数组来将每个元素放至正确的位置。用 n 表示数组的长度,我们遍历原数组,将原数组下标为 i 的元素放至新数组下标为 (i + k)  mod  n 的位置,最后将新数组拷贝至原数组即可。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] tmp = new int[n];
for (int i = 0; i < n; i++) {
//k可能比n大
tmp[(i + k) % n] = nums[i];
}
//复制数组
System.arraycopy(tmp, 0, nums, 0, n);
}
}

方法二:数组反转

我们可以先将所有元素翻转,这样尾部的 k  mod  n个元素就被移至数组头部,然后我们再翻转[0, k mod n − 1] 区间的元素和 [k mod n, n − 1] 区间的元素即能得到最后的答案。

image-20240408142548964
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public void rotate(int[] nums, int k) {
k = 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. 除自身以外数组的乘积

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

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

不要使用除法,且在 O(*n*) 时间复杂度内完成此题。

示例 1:

1
2
输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

1
2
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

左侧所有数字的乘积和右侧所有数字的乘积(即前缀与后缀)相乘得到答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] l = new int[n];
int[] r = new int[n];
//前缀积 第一个数没有前缀积,初始化为1
l[0] = 1;
for (int i = 1; i < n; i++) {
l[i] = nums[i - 1] * l[i - 1];
}
//后缀积 最后一个数没有后缀积,初始化为1
r[n - 1] = 1;
for (int i = n - 2; i >= 0; i--) {
r[i] = nums[i + 1] * r[i + 1];
}
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = l[i] * r[i];
}
return ans;
}
}

41. 缺失的第一个正数

41. 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

1
2
3
输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

示例 2:

1
2
3
输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例 3:

1
2
3
输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

我们可以对数组进行一次遍历,对于遍历到的数 x=nums[i],如果 x∈[1,N],我们就知道 x应当出现在数组中的 x−1的位置,因此交换 nums[i]和 nums[x−1],这样 x 就出现在了正确的位置。在完成交换后,新的 nums[i]可能还在[1, N]的范围内,我们需要继续进行交换操作,直到 x∉[1,N]。

注意到上面的方法可能会陷入死循环。如果 nums[i] 恰好与 nums[x−1] 相等,那么就会无限交换下去。此时我们有 nums[i]=x=nums[x−1],说明 x 已经出现在了正确的位置。因此我们可以跳出循环,开始遍历下一个数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; i++) {
//nums[num[i] - 1] == nums[i]时跳出循环
while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
int temp = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = temp;
}
}
for (int i = 0; i < n; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
//如果运行到这一步,说明前面n个数据是按照1-n的顺序的,所以返回n+1
return n + 1;
}
}