Skip to content

Commit 4defb3d

Browse files
2 parents 5da6c06 + 1e9bb56 commit 4defb3d

20 files changed

+499
-85
lines changed

problems/0015.三数之和.md

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -313,54 +313,36 @@ func threeSum(nums []int)[][]int{
313313
javaScript:
314314

315315
```js
316-
/**
317-
* @param {number[]} nums
318-
* @return {number[][]}
319-
*/
320-
321-
// 循环内不考虑去重
322-
var threeSum = function(nums) {
323-
const len = nums.length;
324-
if(len < 3) return [];
325-
nums.sort((a, b) => a - b);
326-
const resSet = new Set();
327-
for(let i = 0; i < len - 2; i++) {
328-
if(nums[i] > 0) break;
329-
let l = i + 1, r = len - 1;
330-
while(l < r) {
331-
const sum = nums[i] + nums[l] + nums[r];
332-
if(sum < 0) { l++; continue };
333-
if(sum > 0) { r--; continue };
334-
resSet.add(`${nums[i]},${nums[l]},${nums[r]}`);
335-
l++;
336-
r--;
337-
}
338-
}
339-
return Array.from(resSet).map(i => i.split(","));
340-
};
341-
342-
// 去重优化
343316
var threeSum = function(nums) {
344-
const len = nums.length;
345-
if(len < 3) return [];
346-
nums.sort((a, b) => a - b);
347-
const res = [];
348-
for(let i = 0; i < len - 2; i++) {
349-
if(nums[i] > 0) break;
350-
// a去重
351-
if(i > 0 && nums[i] === nums[i - 1]) continue;
352-
let l = i + 1, r = len - 1;
317+
const res = [], len = nums.length
318+
// 将数组排序
319+
nums.sort((a, b) => a - b)
320+
for (let i = 0; i < len; i++) {
321+
let l = i + 1, r = len - 1, iNum = nums[i]
322+
// 数组排过序,如果第一个数大于0直接返回res
323+
if (iNum > 0) return res
324+
// 去重
325+
if (iNum == nums[i - 1]) continue
353326
while(l < r) {
354-
const sum = nums[i] + nums[l] + nums[r];
355-
if(sum < 0) { l++; continue };
356-
if(sum > 0) { r--; continue };
357-
res.push([nums[i], nums[l], nums[r]])
358-
// b c 去重
359-
while(l < r && nums[l] === nums[++l]);
360-
while(l < r && nums[r] === nums[--r]);
327+
let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
328+
// 三数之和小于0,则左指针向右移动
329+
if (threeSum < 0) l++
330+
else if (threeSum > 0) r--
331+
else {
332+
res.push([iNum, lNum, rNum])
333+
// 去重
334+
while(l < r && nums[l] == nums[l + 1]){
335+
l++
336+
}
337+
while(l < r && nums[r] == nums[r - 1]) {
338+
r--
339+
}
340+
l++
341+
r--
342+
}
361343
}
362344
}
363-
return res;
345+
return res
364346
};
365347
```
366348
TypeScript:

problems/0018.四数之和.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
四数之和,和[15.三数之和](https://programmercarl.com/0015.三数之和.html)是一个思路,都是使用双指针法, 基本解法就是在[15.三数之和](https://programmercarl.com/0015.三数之和.html) 的基础上再套一层for循环。
3333

34-
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来)
34+
但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是`[-4, -3, -2, -1]``target``-10`,不能因为`-4 > -10`而跳过。但是我们依旧可以去做剪枝,逻辑变成`nums[i] > target && (nums[i] >=0 || target >= 0)`就可以了。
3535

3636
[15.三数之和](https://programmercarl.com/0015.三数之和.html)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
3737

@@ -72,15 +72,20 @@ public:
7272
vector<vector<int>> result;
7373
sort(nums.begin(), nums.end());
7474
for (int k = 0; k < nums.size(); k++) {
75-
// 这种剪枝是错误的,这道题目target 是任意值
76-
// if (nums[k] > target) {
77-
// return result;
78-
// }
75+
// 剪枝处理
76+
if (nums[k] > target && (nums[k] >= 0 || target >= 0)) {
77+
break; // 这里使用break,统一通过最后的return返回
78+
}
7979
// 去重
8080
if (k > 0 && nums[k] == nums[k - 1]) {
8181
continue;
8282
}
8383
for (int i = k + 1; i < nums.size(); i++) {
84+
// 2级剪枝处理
85+
if (nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0)) {
86+
break;
87+
}
88+
8489
// 正确去重方法
8590
if (i > k + 1 && nums[i] == nums[i - 1]) {
8691
continue;

problems/0056.合并区间.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,26 @@ var merge = function(intervals) {
266266
};
267267
```
268268

269+
### TypeScript
270+
271+
```typescript
272+
function merge(intervals: number[][]): number[][] {
273+
const resArr: number[][] = [];
274+
intervals.sort((a, b) => a[0] - b[0]);
275+
resArr[0] = [...intervals[0]]; // 避免修改原intervals
276+
for (let i = 1, length = intervals.length; i < length; i++) {
277+
let interval: number[] = intervals[i];
278+
let last: number[] = resArr[resArr.length - 1];
279+
if (interval[0] <= last[1]) {
280+
last[1] = Math.max(interval[1], last[1]);
281+
} else {
282+
resArr.push([...intervals[i]]);
283+
}
284+
}
285+
return resArr;
286+
};
287+
```
288+
269289

270290

271291
-----------------------

problems/0059.螺旋矩阵II.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。
3232

33-
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里哪里有问题,改了那里这里又跑不起来了。
33+
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里那里有问题,改了那里这里又跑不起来了。
3434

3535
大家还记得我们在这篇文章[数组:每次遇到二分法,都是一看就会,一写就废](https://programmercarl.com/0704.二分查找.html)中讲解了二分法,提到如果要写出正确的二分法一定要坚持**循环不变量原则**
3636

@@ -47,7 +47,7 @@
4747

4848
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是**一进循环深似海,从此offer是路人**
4949

50-
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开又闭的原则,这样这一圈才能按照统一的规则画下来。
50+
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
5151

5252
那么我按照左闭右开的原则,来画一圈,大家看一下:
5353

@@ -59,7 +59,7 @@
5959

6060
一些同学做这道题目之所以一直写不好,代码越写越乱。
6161

62-
就是因为在画每一条边的时候,一会左开又闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
62+
就是因为在画每一条边的时候,一会左开右闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
6363

6464
代码如下,已经详细注释了每一步的目的,可以看出while循环里判断的情况是很多的,代码里处理的原则也是统一的左闭右开。
6565

problems/0131.分割回文串.md

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -454,31 +454,36 @@ var partition = function(s) {
454454

455455
```typescript
456456
function partition(s: string): string[][] {
457-
function isPalindromeStr(s: string, left: number, right: number): boolean {
458-
while (left < right) {
459-
if (s[left++] !== s[right--]) {
460-
return false;
457+
const res: string[][] = []
458+
const path: string[] = []
459+
const isHuiwen = (
460+
str: string,
461+
startIndex: number,
462+
endIndex: number
463+
): boolean => {
464+
for (; startIndex < endIndex; startIndex++, endIndex--) {
465+
if (str[startIndex] !== str[endIndex]) {
466+
return false
461467
}
462468
}
463-
return true;
469+
return true
464470
}
465-
function backTracking(s: string, startIndex: number, route: string[]): void {
466-
let length: number = s.length;
467-
if (length === startIndex) {
468-
resArr.push(route.slice());
469-
return;
471+
const rec = (str: string, index: number): void => {
472+
if (index >= str.length) {
473+
res.push([...path])
474+
return
470475
}
471-
for (let i = startIndex; i < length; i++) {
472-
if (isPalindromeStr(s, startIndex, i)) {
473-
route.push(s.slice(startIndex, i + 1));
474-
backTracking(s, i + 1, route);
475-
route.pop();
476+
for (let i = index; i < str.length; i++) {
477+
if (!isHuiwen(str, index, i)) {
478+
continue
476479
}
480+
path.push(str.substring(index, i + 1))
481+
rec(str, i + 1)
482+
path.pop()
477483
}
478484
}
479-
const resArr: string[][] = [];
480-
backTracking(s, 0, []);
481-
return resArr;
485+
rec(s, 0)
486+
return res
482487
};
483488
```
484489

problems/0134.加油站.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ function canCompleteCircuit(gas: number[], cost: number[]): number {
408408

409409
### C
410410

411+
贪心算法:方法一
412+
413+
411414
```c
412415
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
413416
int curSum = 0;
@@ -437,5 +440,36 @@ int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
437440
}
438441
```
439442
443+
贪心算法:方法二
444+
```c
445+
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
446+
int curSum = 0;
447+
int totalSum = 0;
448+
int start = 0;
449+
450+
int i;
451+
for(i = 0; i < gasSize; ++i) {
452+
// 当前i站中加油量与耗油量的差
453+
int diff = gas[i] - cost[i];
454+
455+
curSum += diff;
456+
totalSum += diff;
457+
458+
// 若0到i的加油量都为负,则开始位置应为i+1
459+
if(curSum < 0) {
460+
curSum = 0;
461+
// 当i + 1 == gasSize时,totalSum < 0(此时i为gasSize - 1),油车不可能返回原点
462+
start = i + 1;
463+
}
464+
}
465+
466+
// 若总和小于0,加油车无论如何都无法返回原点。返回-1
467+
if(totalSum < 0)
468+
return -1;
469+
470+
return start;
471+
}
472+
```
473+
440474
-----------------------
441475
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>

problems/0150.逆波兰表达式求值.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public:
109109
};
110110
```
111111
112-
# 题外话
112+
## 题外话
113113
114114
我们习惯看到的表达式都是中缀表达式,因为符合我们的习惯,但是中缀表达式对于计算机来说就不是很友好了。
115115
@@ -128,7 +128,7 @@ public:
128128
129129
130130
131-
# 其他语言版本
131+
## 其他语言版本
132132
133133
java:
134134

problems/0203.移除链表元素.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,38 @@ public:
145145
146146
## 其他语言版本
147147
C:
148+
用原来的链表操作:
149+
```c
150+
struct ListNode* removeElements(struct ListNode* head, int val){
151+
struct ListNode* temp;
152+
// 当头结点存在并且头结点的值等于val时
153+
while(head && head->val == val) {
154+
temp = head;
155+
// 将新的头结点设置为head->next并删除原来的头结点
156+
head = head->next;
157+
free(temp);
158+
}
159+
160+
struct ListNode *cur = head;
161+
// 当cur存在并且cur->next存在时
162+
// 此解法需要判断cur存在因为cur指向head。若head本身为NULL或者原链表中元素都为val的话,cur也会为NULL
163+
while(cur && (temp = cur->next)) {
164+
// 若cur->next的值等于val
165+
if(temp->val == val) {
166+
// 将cur->next设置为cur->next->next并删除cur->next
167+
cur->next = temp->next;
168+
free(temp);
169+
}
170+
// 若cur->next不等于val,则将cur后移一位
171+
else
172+
cur = cur->next;
173+
}
174+
175+
// 返回头结点
176+
return head;
177+
}
178+
```
179+
设置一个虚拟头结点:
148180
```c
149181
/**
150182
* Definition for singly-linked list.

problems/0209.长度最小的子数组.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public:
112112

113113
**一些录友会疑惑为什么时间复杂度是O(n)**
114114

115-
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
115+
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
116116

117117
## 相关题目推荐
118118

problems/0309.最佳买卖股票时机含冷冻期.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ class Solution {
214214

215215
for (int i = 2; i <= prices.length; i++) {
216216
/*
217-
dp[i][0] 第i天未持有股票收益;
218-
dp[i][1] 第i天持有股票收益;
217+
dp[i][0] 第i天持有股票收益;
218+
dp[i][1] 第i天不持有股票收益;
219219
情况一:第i天是冷静期,不能以dp[i-1][1]购买股票,所以以dp[i - 2][1]买股票,没问题
220220
情况二:第i天不是冷静期,理论上应该以dp[i-1][1]购买股票,但是第i天不是冷静期说明,第i-1天没有卖出股票,
221221
则dp[i-1][1]=dp[i-2][1],所以可以用dp[i-2][1]买股票,没问题

0 commit comments

Comments
 (0)