Skip to content

Commit b65aeac

Browse files
author
xin
committed
leetcode题解
1 parent 1df1677 commit b65aeac

File tree

40 files changed

+1324
-81
lines changed

40 files changed

+1324
-81
lines changed

content/leetcode-problems/.DS_Store

6 KB
Binary file not shown.

content/leetcode-problems/3sum-closest.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ draft = false
3636

3737
## 解题思路
3838

39-
排序+双指针,和[3Sum](/leetcode-problems/3sum)思路一样,只是求的是最接近target的三数之和,维护最接近的和ans和最小差值minDiff,当总和更靠近时更新ans和minDiff
39+
排序+双指针,和[3Sum](/leetcode-problems/3sum)思路一样,只是求的是最接近target的三数之和,维护最接近的和`ans`和最小差值`minDiff`,当总和更靠近时更新`ans``minDiff`
4040

4141
## 代码
4242

content/leetcode-problems/3sum.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
6262

6363
以第一层循环为例,枚举过的一个三元组是`(-1, 0, 1)`,下一次枚举的元素也是-1,也会枚举到相同的三元组是`(-1, 0, 1)`。因此需要跳到下一个不相同的元素,也就是0。
6464

65-
此时时间复杂度依然是$O(n^3)$,可以发现,如果固定a,找到a + b + c = 0,当第二重循环往后枚举一个元素 b′ 时,由于 b′ > b,所以那么满足 a + b′ + c′ = 0的c′ 一定小于c,即 c′一定在c的左侧。因此可以将第二重和第三重循环用对撞指针实现。
65+
此时时间复杂度依然是$O(n^3)$,可以发现,如果固定`a`,找到`a + b + c = 0`,当第二重循环往后枚举一个元素` b′` 时,由于`b′ > b`,所以那么满足` a + b′ + c′ = 0``c′` 一定小于`c`,即 `c′`一定在`c`的左侧。因此可以将第二重和第三重循环用对撞指针实现。
6666

6767
当需要枚举数组中的两个元素时,如果发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从$O(n^2)$减少至 $O(n)$。
6868

content/leetcode-problems/4sum.md

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
+++
2+
title = '18. 四数之和'
3+
date = 2024-05-01T10:14:47+08:00
4+
draft = false
5+
6+
+++
7+
8+
## [题目](https://leetcode.cn/problems/4sum/)
9+
10+
给你一个由 `n` 个整数组成的数组 `nums` ,和一个目标值 `target` 。请你找出并返回满足下述全部条件且**不重复**的四元组 `[nums[a], nums[b], nums[c], nums[d]]` (若两个四元组元素一一对应,则认为两个四元组重复):
11+
12+
- `0 <= a, b, c, d < n`
13+
- `a``b``c``d` **互不相同**
14+
- `nums[a] + nums[b] + nums[c] + nums[d] == target`
15+
16+
你可以按 **任意顺序** 返回答案 。
17+
18+
**示例 1:**
19+
20+
```
21+
输入:nums = [1,0,-1,0,-2,2], target = 0
22+
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
23+
```
24+
25+
**示例 2:**
26+
27+
```
28+
输入:nums = [2,2,2,2,2], target = 8
29+
输出:[[2,2,2,2]]
30+
```
31+
32+
**提示:**
33+
34+
- `1 <= nums.length <= 200`
35+
- `-109 <= nums[i] <= 109`
36+
- `-109 <= target <= 109`
37+
38+
## 解题思路
39+
40+
方法一:排序+双指针,在[3Sum](/leetcode-problems/3sum)的基础上多套一层循环。第一层循环固定第一个元素,第二层循环固定第二个元素,循环内部用对撞指针找出剩余两个元素。时间复杂度:$O(n^3)$,空间复杂度:$O(logn)$,取决于排序所需的空间。
41+
42+
方法二:分治法,将问题分成若干子问题,对子问题求解后将解合并。4Sum -> n-3个3Sum,3Sum -> n-2个2Sum,2Sum用对撞指针得到解。延展到求kSum。
43+
44+
## 代码
45+
46+
方法一
47+
48+
```go
49+
func fourSum(nums []int, target int) [][]int {
50+
var ans [][]int
51+
n := len(nums)
52+
sort.Ints(nums)
53+
for i := 0; i < n-3; i++ {
54+
if i > 0 && nums[i] == nums[i-1] {
55+
continue
56+
}
57+
for j := i+1; j < n-2; j++ {
58+
if j > i+1 && nums[j] == nums[j-1] {
59+
continue
60+
}
61+
l, r := j+1, n-1
62+
for l < r {
63+
s := nums[i] + nums[j] + nums[l] + nums[r]
64+
if s == target {
65+
ans = append(ans, []int{nums[i], nums[j], nums[l], nums[r]})
66+
lv, rv := nums[l], nums[r]
67+
for l < r && nums[l] == lv {
68+
l++
69+
}
70+
for l < r && nums[r] == rv {
71+
r--
72+
}
73+
} else if s < target {
74+
l++
75+
} else {
76+
r--
77+
}
78+
}
79+
}
80+
}
81+
return ans
82+
}
83+
```
84+
85+
方法二
86+
87+
```go
88+
func fourSum(nums []int, target int) [][]int {
89+
sort.Ints(nums)
90+
return kSum(nums, target, 4)
91+
}
92+
93+
func kSum(nums []int, target int, k int) [][]int {
94+
// 剪枝
95+
if !(nums[0]*k <= target && target <= nums[len(nums)-1]*k) {
96+
return [][]int{}
97+
}
98+
99+
if k == 2 {
100+
return twoSum(nums, target)
101+
}
102+
103+
var ans [][]int
104+
for i := 0; i < len(nums)-k+1; i++ {
105+
if i > 0 && nums[i] == nums[i-1] {
106+
continue
107+
}
108+
for _, res := range kSum(nums[i+1:], target-nums[i], k-1) {
109+
ans = append(ans, append([]int{nums[i]}, res...))
110+
}
111+
}
112+
return ans
113+
}
114+
115+
func twoSum(nums []int, target int) [][]int {
116+
var ans [][]int
117+
l, r := 0, len(nums)-1
118+
for l < r {
119+
s := nums[l] + nums[r]
120+
if s == target {
121+
ans = append(ans, []int{nums[l], nums[r]})
122+
lv, rv := nums[l], nums[r]
123+
for l < r && nums[l] == lv {
124+
l++
125+
}
126+
for l < r && nums[r] == rv {
127+
r--
128+
}
129+
} else if s < target {
130+
l++
131+
} else {
132+
r--
133+
}
134+
}
135+
return ans
136+
}
137+
```
138+

content/leetcode-problems/container-with-most-water.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ draft = false
3838

3939
## 解题思路
4040

41-
面积S = (j - i) * min(h[i], h[j]), j > i
42-
在(1, n)中找出使面积S最大的(i, j),最简单的方法就是遍历(i, j)的所有可能并比较所有面积。
41+
面积`S = (j - i) * min(h[i], h[j]), j > i`
42+
`(1, n)`中找出使面积S最大的`(i, j)`,最简单的方法就是遍历`(i, j)`的所有可能并比较所有面积。
4343

4444
发现以下规律
4545

46-
1. 当h[i] < h[j]时,不需要计算(i, j-1), (i, j-2), ...,因为它们的面积一定小于(i, j)的面积,因为宽度变小,高度一定<=h[i]。所以下一步应该检查(i+1, j)的面积。
47-
2. 同理h[i] >= h[j]时,不需要计算(i+1, j), (i+2, j), ..., 所以下一步应该检查(i, j-1)的面积。
46+
1. `h[i] < h[j]`时,不需要计算`(i, j-1), (i, j-2), ...`,因为它们的面积一定小于`(i, j)`的面积,因为宽度变小,高度一定<=`h[i]`。所以下一步应该检查`(i+1, j)`的面积。
47+
2. 同理`h[i] >= h[j]`时,不需要计算`(i+1, j), (i+2, j), ...`, 所以下一步应该检查`(i, j-1)`的面积。
4848

4949
首尾对撞指针,每次高度较低的指针往中间移动,计算面积更新最大面积。
5050

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
+++
2+
title = '31. 下一个排列'
3+
date = 2024-05-01T10:55:07+08:00
4+
draft = false
5+
6+
+++
7+
8+
## [题目](https://leetcode.cn/problems/next-permutation/)
9+
10+
整数数组的一个 **排列** 就是将其所有成员以序列或线性顺序排列。
11+
12+
- 例如,`arr = [1,2,3]` ,以下这些都可以视作 `arr` 的排列:`[1,2,3]``[1,3,2]``[3,1,2]``[2,3,1]`
13+
14+
整数数组的 **下一个排列** 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 **下一个排列** 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
15+
16+
- 例如,`arr = [1,2,3]` 的下一个排列是 `[1,3,2]`
17+
- 类似地,`arr = [2,3,1]` 的下一个排列是 `[3,1,2]`
18+
-`arr = [3,2,1]` 的下一个排列是 `[1,2,3]` ,因为 `[3,2,1]` 不存在一个字典序更大的排列。
19+
20+
给你一个整数数组 `nums` ,找出 `nums` 的下一个排列。
21+
22+
必须**原地**修改,只允许使用额外常数空间。
23+
24+
25+
26+
**示例 1:**
27+
28+
```
29+
输入:nums = [1,2,3]
30+
输出:[1,3,2]
31+
```
32+
33+
**示例 2:**
34+
35+
```
36+
输入:nums = [3,2,1]
37+
输出:[1,2,3]
38+
```
39+
40+
**示例 3:**
41+
42+
```
43+
输入:nums = [1,1,5]
44+
输出:[1,5,1]
45+
```
46+
47+
48+
49+
**提示:**
50+
51+
- `1 <= nums.length <= 100`
52+
- `0 <= nums[i] <= 100`
53+
54+
## 解题思路
55+
56+
如何找到下一个排列。下一个排列是找到一个大于当前排序的字典序,且变大的幅度最小。那么只能将较小的数与较大数做一次原地交换。并且较小数的下标要尽量靠右,较大数也要尽可能小。原地交换以后,还需要将较大数右边的区间按照升序重新排列。这样交换以后,才能生成下一个排列。以排列 [8,9,6,10,7,2] 为例:能找到的符合条件的一对「较小数」与「较大数」的组合为 6 与 7,满足「较小数」尽量靠右,而「较大数」尽可能小。当完成交换后排列变为 [8,9,7,10,6,2],此时我们可以重排原本「较小数」右边的区间,变为 [8,9,7,2,6,10]
57+
58+
1. 找出较小数:较小数后面的区间不存在较小数,所以一定是降序区间,所以从后往前找到第一个不满足降序的元素`nums[i]` ,即不满足`nums[i] >= nums[i+1]`
59+
60+
2. 找出较大数:较大数要尽可能小,因为较小数后面的区间是降序的,所以从后往前找到第一个大于较小数的元素`nums[j]`,交换较小数`nums[i]`和较大数`nums[j]`
61+
3. 后面的区间按照升序重新排列:交换后,后面区间依然是降序的,反转数组就变成升序的。
62+
63+
## 代码
64+
65+
```go
66+
func nextPermutation(nums []int) {
67+
n := len(nums)
68+
if n <= 1 {
69+
return
70+
}
71+
i := n - 2
72+
for i >= 0 && nums[i] >= nums[i+1] {
73+
i--
74+
}
75+
if i >= 0 {
76+
j := n-1
77+
for nums[j] <= nums[i] {
78+
j--
79+
}
80+
nums[i], nums[j] = nums[j], nums[i]
81+
}
82+
reverse(nums[i+1:])
83+
}
84+
85+
func reverse(nums []int) {
86+
l, r := 0, len(nums)-1
87+
for l < r {
88+
nums[l], nums[r] = nums[r], nums[l]
89+
l++
90+
r--
91+
}
92+
}
93+
```
94+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
+++
2+
title = '26. 删除有序数组中的重复项'
3+
date = 2024-05-01T10:38:09+08:00
4+
draft = false
5+
6+
+++
7+
8+
## [题目](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/)
9+
10+
给你一个 **非严格递增排列** 的数组 `nums` ,请你**[ 原地](http://baike.baidu.com/item/原地算法)** 删除重复出现的元素,使每个元素 **只出现一次** ,返回删除后数组的新长度。元素的 **相对顺序** 应该保持 **一致** 。然后返回 `nums` 中唯一元素的个数。
11+
12+
考虑 `nums` 的唯一元素的数量为 `k` ,你需要做以下事情确保你的题解可以被通过:
13+
14+
- 更改数组 `nums` ,使 `nums` 的前 `k` 个元素包含唯一元素,并按照它们最初在 `nums` 中出现的顺序排列。`nums` 的其余元素与 `nums` 的大小不重要。
15+
- 返回 `k`
16+
17+
**判题标准:**
18+
19+
系统会用下面的代码来测试你的题解:
20+
21+
```
22+
int[] nums = [...]; // 输入数组
23+
int[] expectedNums = [...]; // 长度正确的期望答案
24+
25+
int k = removeDuplicates(nums); // 调用
26+
27+
assert k == expectedNums.length;
28+
for (int i = 0; i < k; i++) {
29+
assert nums[i] == expectedNums[i];
30+
}
31+
```
32+
33+
如果所有断言都通过,那么您的题解将被 **通过**
34+
35+
36+
37+
**示例 1:**
38+
39+
```
40+
输入:nums = [1,1,2]
41+
输出:2, nums = [1,2,_]
42+
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
43+
```
44+
45+
**示例 2:**
46+
47+
```
48+
输入:nums = [0,0,1,1,1,2,2,3,3,4]
49+
输出:5, nums = [0,1,2,3,4]
50+
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
51+
```
52+
53+
54+
55+
**提示:**
56+
57+
- `1 <= nums.length <= 3 * 104`
58+
- `-104 <= nums[i] <= 104`
59+
- `nums` 已按 **非严格递增** 排列
60+
61+
## 解题思路
62+
63+
双指针,指针i指向去重数组插入位置,迭代数组,如果值和去重数组最后一个元素`nums[i-1]`不同或者是一开始,则插入到去重数组`nums[i] = num`,然后`i++`
64+
65+
## 代码
66+
67+
```go
68+
func removeDuplicates(nums []int) int {
69+
i := 0
70+
for _, num := range nums {
71+
if i == 0 || num != nums[i-1] {
72+
nums[i] = num
73+
i++
74+
}
75+
}
76+
return i
77+
}
78+
```
79+

0 commit comments

Comments
 (0)