Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
wangAlpha authored May 19, 2022
2 parents a56db3f + 047c4db commit 64693d5
Show file tree
Hide file tree
Showing 36 changed files with 848 additions and 137 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@
33. [二叉树:构造一棵搜索树](./problems/0108.将有序数组转换为二叉搜索树.md)
34. [二叉树:搜索树转成累加树](./problems/0538.把二叉搜索树转换为累加树.md)
35. [二叉树:总结篇!(需要掌握的二叉树技能都在这里了)](./problems/二叉树总结篇.md)

## 回溯算法

题目分类大纲如下:
Expand Down
70 changes: 26 additions & 44 deletions problems/0015.三数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,54 +313,36 @@ func threeSum(nums []int)[][]int{
javaScript:

```js
/**
* @param {number[]} nums
* @return {number[][]}
*/

// 循环内不考虑去重
var threeSum = function(nums) {
const len = nums.length;
if(len < 3) return [];
nums.sort((a, b) => a - b);
const resSet = new Set();
for(let i = 0; i < len - 2; i++) {
if(nums[i] > 0) break;
let l = i + 1, r = len - 1;
while(l < r) {
const sum = nums[i] + nums[l] + nums[r];
if(sum < 0) { l++; continue };
if(sum > 0) { r--; continue };
resSet.add(`${nums[i]},${nums[l]},${nums[r]}`);
l++;
r--;
}
}
return Array.from(resSet).map(i => i.split(","));
};

// 去重优化
var threeSum = function(nums) {
const len = nums.length;
if(len < 3) return [];
nums.sort((a, b) => a - b);
const res = [];
for(let i = 0; i < len - 2; i++) {
if(nums[i] > 0) break;
// a去重
if(i > 0 && nums[i] === nums[i - 1]) continue;
let l = i + 1, r = len - 1;
const res = [], len = nums.length
// 将数组排序
nums.sort((a, b) => a - b)
for (let i = 0; i < len; i++) {
let l = i + 1, r = len - 1, iNum = nums[i]
// 数组排过序,如果第一个数大于0直接返回res
if (iNum > 0) return res
// 去重
if (iNum == nums[i - 1]) continue
while(l < r) {
const sum = nums[i] + nums[l] + nums[r];
if(sum < 0) { l++; continue };
if(sum > 0) { r--; continue };
res.push([nums[i], nums[l], nums[r]])
// b c 去重
while(l < r && nums[l] === nums[++l]);
while(l < r && nums[r] === nums[--r]);
let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
// 三数之和小于0,则左指针向右移动
if (threeSum < 0) l++
else if (threeSum > 0) r--
else {
res.push([iNum, lNum, rNum])
// 去重
while(l < r && nums[l] == nums[l + 1]){
l++
}
while(l < r && nums[r] == nums[r - 1]) {
r--
}
l++
r--
}
}
}
return res;
return res
};
```
TypeScript:
Expand Down
15 changes: 10 additions & 5 deletions problems/0018.四数之和.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

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

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

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

Expand Down Expand Up @@ -72,15 +72,20 @@ public:
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 这种剪枝是错误的,这道题目target 是任意值
// if (nums[k] > target) {
// return result;
// }
// 剪枝处理
if (nums[k] > target && (nums[k] >= 0 || target >= 0)) {
break; // 这里使用break,统一通过最后的return返回
}
// 去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && (nums[k] + nums[i] >= 0 || target >= 0)) {
break;
}

// 正确去重方法
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
Expand Down
25 changes: 25 additions & 0 deletions problems/0035.搜索插入位置.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,31 @@ func searchInsert(_ nums: [Int], _ target: Int) -> Int {
```


### PHP

```php
// 二分法(1):[左闭右闭]
function searchInsert($nums, $target)
{
$n = count($nums);
$l = 0;
$r = $n - 1;
while ($l <= $r) {
$mid = floor(($l + $r) / 2);
if ($nums[$mid] > $target) {
// 下次搜索在左区间:[$l,$mid-1]
$r = $mid - 1;
} else if ($nums[$mid] < $target) {
// 下次搜索在右区间:[$mid+1,$r]
$l = $mid + 1;
} else {
// 命中返回
return $mid;
}
}
return $r + 1;
}
```


-----------------------
Expand Down
5 changes: 2 additions & 3 deletions problems/0039.组合总和.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,18 +370,17 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
```js
var combinationSum = function(candidates, target) {
const res = [], path = [];
candidates.sort(); // 排序
candidates.sort((a,b)=>a-b); // 排序
backtracking(0, 0);
return res;
function backtracking(j, sum) {
if (sum > target) return;
if (sum === target) {
res.push(Array.from(path));
return;
}
for(let i = j; i < candidates.length; i++ ) {
const n = candidates[i];
if(n > target - sum) continue;
if(n > target - sum) break;
path.push(n);
sum += n;
backtracking(i, sum);
Expand Down
15 changes: 10 additions & 5 deletions problems/0040.组合总和II.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,22 +508,27 @@ func backtracking(startIndex,sum,target int,candidates,trcak []int,res *[][]int)
*/
var combinationSum2 = function(candidates, target) {
const res = []; path = [], len = candidates.length;
candidates.sort();
candidates.sort((a,b)=>a-b);
backtracking(0, 0);
return res;
function backtracking(sum, i) {
if (sum > target) return;
if (sum === target) {
res.push(Array.from(path));
return;
}
let f = -1;
for(let j = i; j < len; j++) {
const n = candidates[j];
if(n > target - sum || n === f) continue;
if(j > i && candidates[j] === candidates[j-1]){
//若当前元素和前一个元素相等
//则本次循环结束,防止出现重复组合
continue;
}
//如果当前元素值大于目标值-总和的值
//由于数组已排序,那么该元素之后的元素必定不满足条件
//直接终止当前层的递归
if(n > target - sum) break;
path.push(n);
sum += n;
f = n;
backtracking(sum, j + 1);
path.pop();
sum -= n;
Expand Down
20 changes: 20 additions & 0 deletions problems/0056.合并区间.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,26 @@ var merge = function(intervals) {
};
```

### TypeScript

```typescript
function merge(intervals: number[][]): number[][] {
const resArr: number[][] = [];
intervals.sort((a, b) => a[0] - b[0]);
resArr[0] = [...intervals[0]]; // 避免修改原intervals
for (let i = 1, length = intervals.length; i < length; i++) {
let interval: number[] = intervals[i];
let last: number[] = resArr[resArr.length - 1];
if (interval[0] <= last[1]) {
last[1] = Math.max(interval[1], last[1]);
} else {
resArr.push([...intervals[i]]);
}
}
return resArr;
};
```



-----------------------
Expand Down
6 changes: 3 additions & 3 deletions problems/0059.螺旋矩阵II.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

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

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

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

Expand All @@ -47,7 +47,7 @@

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

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

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

Expand All @@ -59,7 +59,7 @@

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

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

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

Expand Down
51 changes: 51 additions & 0 deletions problems/0070.爬楼梯.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,58 @@ var climbStairs = function(n) {
};
```

TypeScript

> 爬2

```typescript
function climbStairs(n: number): number {
/**
dp[i]: i阶楼梯的方法种数
dp[1]: 1;
dp[2]: 2;
...
dp[i]: dp[i - 1] + dp[i - 2];
*/
const dp: number[] = [];
dp[1] = 1;
dp[2] = 2;
for (let i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
};
```
> 爬m阶
```typescript
function climbStairs(n: number): number {
/**
一次可以爬m阶
dp[i]: i阶楼梯的方法种数
dp[1]: 1;
dp[2]: 2;
dp[3]: dp[2] + dp[1];
...
dp[i]: dp[i - 1] + dp[i - 2] + ... + dp[max(i - m, 1)]; 从i-1加到max(i-m, 1)
*/
const m: number = 2; // 本题m为2
const dp: number[] = new Array(n + 1).fill(0);
dp[1] = 1;
dp[2] = 2;
for (let i = 3; i <= n; i++) {
const end: number = Math.max(i - m, 1);
for (let j = i - 1; j >= end; j--) {
dp[i] += dp[j];
}
}
return dp[n];
};
```

### C

```c
int climbStairs(int n){
//若n<=2,返回n
Expand Down
41 changes: 23 additions & 18 deletions problems/0131.分割回文串.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,31 +454,36 @@ var partition = function(s) {

```typescript
function partition(s: string): string[][] {
function isPalindromeStr(s: string, left: number, right: number): boolean {
while (left < right) {
if (s[left++] !== s[right--]) {
return false;
const res: string[][] = []
const path: string[] = []
const isHuiwen = (
str: string,
startIndex: number,
endIndex: number
): boolean => {
for (; startIndex < endIndex; startIndex++, endIndex--) {
if (str[startIndex] !== str[endIndex]) {
return false
}
}
return true;
return true
}
function backTracking(s: string, startIndex: number, route: string[]): void {
let length: number = s.length;
if (length === startIndex) {
resArr.push(route.slice());
return;
const rec = (str: string, index: number): void => {
if (index >= str.length) {
res.push([...path])
return
}
for (let i = startIndex; i < length; i++) {
if (isPalindromeStr(s, startIndex, i)) {
route.push(s.slice(startIndex, i + 1));
backTracking(s, i + 1, route);
route.pop();
for (let i = index; i < str.length; i++) {
if (!isHuiwen(str, index, i)) {
continue
}
path.push(str.substring(index, i + 1))
rec(str, i + 1)
path.pop()
}
}
const resArr: string[][] = [];
backTracking(s, 0, []);
return resArr;
rec(s, 0)
return res
};
```

Expand Down
Loading

0 comments on commit 64693d5

Please sign in to comment.