Merge branch 'master' into master

This commit is contained in:
BruceCat 2021-03-21 12:01:54 +08:00 committed by GitHub
commit c2952f599f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 7354 additions and 443 deletions

View File

@ -181,7 +181,7 @@ Gitee Pages 地址https://labuladong.gitee.io/algo
[PaperJets](https://github.com/PaperJets),
[qy-yang](https://github.com/qy-yang),
[realism0331](https://github.com/realism0331),
[SCUhzs](https://github.com/HuangZiSheng001),
[SCUhzs](https://github.com/brucecat),
[Seaworth](https://github.com/Seaworth),
[shazi4399](https://github.com/shazi4399),
[ShuozheLi](https://github.com/ShuoZheLi/),
@ -190,7 +190,7 @@ Gitee Pages 地址https://labuladong.gitee.io/algo
[Tianhao Zhou](https://github.com/tianhaoz95),
[timmmGZ](https://github.com/timmmGZ),
[tommytim0515](https://github.com/tommytim0515),
[upbin](https://github.com/upbin),
[ucsk](https://github.com/ucsk),
[wadegrc](https://github.com/wadegrc),
[walsvid](https://github.com/walsvid),
[warmingkkk](https://github.com/warmingkkk),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [经典动态规划:最长公共子序列](https://labuladong.gitbook.io/algo)
* [特殊数据结构:单调栈](https://labuladong.gitbook.io/algo)
* [经典动态规划:最长公共子序列](https://labuladong.gitbook.io/algo/)
* [特殊数据结构:单调栈](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -423,48 +423,135 @@ KMP 算法也就是动态规划那点事,我们的公众号文章目录有一
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[MoguCloud](https://github.com/MoguCloud) 提供 实现 strStr() 的 Python 完整代码:
```py
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 边界条件判断
if not needle:
return 0
pat = needle
txt = haystack
M = len(pat)
# dp[状态][字符] = 下个状态
dp = [[0 for _ in range(256)] for _ in pat]
# base case
dp[0][ord(pat[0])] = 1
# 影子状态 X 初始化为 0
X = 0
for j in range(1, M):
for c in range(256):
dp[j][c] = dp[X][c]
dp[j][ord(pat[j])] = j + 1
# 更新影子状态
X = dp[X][ord(pat[j])]
[28.实现 strStr()](https://leetcode-cn.com/problems/implement-strstr)
### python
[MoguCloud](https://github.com/MoguCloud) 提供 实现 strStr() 的 Python 完整代码:
```python
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 边界条件判断
if not needle:
return 0
pat = needle
txt = haystack
M = len(pat)
# dp[状态][字符] = 下个状态
dp = [[0 for _ in range(256)] for _ in pat]
# base case
dp[0][ord(pat[0])] = 1
# 影子状态 X 初始化为 0
X = 0
for j in range(1, M):
for c in range(256):
dp[j][c] = dp[X][c]
dp[j][ord(pat[j])] = j + 1
# 更新影子状态
X = dp[X][ord(pat[j])]
N = len(txt)
# pat 初始状态为 0
j = 0
for i in range(N):
# 计算 pat 的下一个状态
j = dp[j][ord(txt[i])]
# 到达终止态,返回结果
if j == M:
return i - M + 1
# 没到达终止态,匹配失败
return -1
# 计算 pat 的下一个状态
j = dp[j][ord(txt[i])]
# 到达终止态,返回结果
if j == M:
return i - M + 1
# 没到达终止态,匹配失败
return -1
```
### javascript
```js
class KMP {
constructor(pat) {
this.pat = pat;
let m = pat.length;
// dp[状态][字符] = 下个状态 初始化一个m*256的整数矩阵
this.dp = new Array(m);
for (let i = 0; i < m; i++) {
this.dp[i] = new Array(256);
this.dp[i].fill(0, 0, 256);
}
// base case
this.dp[0][this.pat[0].charCodeAt()] = 1;
// 影子状态X 初始为0
let x = 0;
// 构建状态转移图
for (let j = 1; j < m; j++) {
for (let c = 0; c < 256; c++) {
this.dp[j][c] = this.dp[x][c];
}
// dp[][对应的ASCII码]
this.dp[j][this.pat[j].charCodeAt()] = j + 1;
// 更新影子状态
x = this.dp[x][this.pat[j].charCodeAt()]
}
}
search(txt) {
let m = this.pat.length;
let n = txt.length;
// pat的初始态为0
let j = 0;
for (let i = 0; i < n; i++) {
// 计算pat的下一个状态
j = this.dp[j][txt[i].charCodeAt()];
// 到达终止态 返回结果
if (j === m) return i - m + 1;
}
// 没到终止态 匹配失败
return -1;
}
}
/**
* @param {string} haystack
* @param {string} needle
* @return {number}
*/
var strStr = function(haystack, needle) {
if(haystack === ""){
if(needle !== ""){
return -1;
}
return 0;
}
if(needle === ""){
return 0;
}
let kmp = new KMP(needle);
return kmp.search(haystack)
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [40张图解TCP三次握手和四次挥手面试题](https://labuladong.gitbook.io/algo)
* [如何计算完全二叉树的节点数](https://labuladong.gitbook.io/algo)
* [40张图解TCP三次握手和四次挥手面试题](https://labuladong.gitbook.io/algo/)
* [如何计算完全二叉树的节点数](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -20,7 +20,7 @@
**-----------**
上一篇文章 [几道智力题](https://labuladong.gitbook.io/algo) 中讨论到一个有趣的「石头游戏」,通过题目的限制条件,这个游戏是先手必胜的。但是智力题终究是智力题,真正的算法问题肯定不会是投机取巧能搞定的。所以,本文就借石头游戏来讲讲「假设两个人都足够聪明,最后谁会获胜」这一类问题该如何用动态规划算法解决。
上一篇文章 [几道智力题](https://labuladong.gitbook.io/algo/) 中讨论到一个有趣的「石头游戏」,通过题目的限制条件,这个游戏是先手必胜的。但是智力题终究是智力题,真正的算法问题肯定不会是投机取巧能搞定的。所以,本文就借石头游戏来讲讲「假设两个人都足够聪明,最后谁会获胜」这一类问题该如何用动态规划算法解决。
博弈类问题的套路都差不多,下文参考 [这个 YouTube 视频](https://www.youtube.com/watch?v=WxpIHvsu1RI) 的思路讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了这个技巧以后,别人再问你什么俩海盗分宝石,俩人拿硬币的问题,你就告诉别人:我懒得想,直接给你写个算法算一下得了。
@ -207,21 +207,16 @@ int stoneGame(int[] piles) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
* python3版本
### python
由[SCUHZS](https://github.com/brucecat)提供
@ -287,7 +282,9 @@ class Solution:
```
* C++ 版本
### C++ 版本
由 [TCeason](https://github.com/TCeason) 提供
@ -328,3 +325,112 @@ public:
```
### javascript
由[SCUHZS](https://github.com/brucecat)提供
**1、暴力递归解**
```js
/**
* 返回[i,j]上先手所能取得的最优决策的值
* @param piles
* @param i
* @param j
* @return {number|*}
*/
var f=function(piles,i,j) {
if(i===j){ //如果i===j,只有一个元素,那么先手只能选它
return piles[i]
}
//否则 有2种情况
//1 先选i之后在[i+1,j]上后手进行最优选择
//2 先选j之后在[i,j-1]上后手进行最优选择
return Math.max(piles[i]+s(i+1,j),piles[j]+s(i,j-1))
}
/**
*返回[i,j]上后手所能取得的最优决策的值
* @param piles
* @param i
* @param j
* @return {number}
*/
var s=function(piles,i,j) {
if(i===j){ //如果i===j,只有一个元素那么后手没有选只能为0
return 0
}
//对于这种双方都是绝顶聪明的人,数据一开始对于双方都是可见的,那么数据一确定,先后手一确定,那么结果就已经确定了
//先手选的人会把最优解选了,那么剩给后手的只有最差的情况
//所以后手的人虽然能从剩下的之中进行最优决策,但结果确是命中注定的了,只能是最差的
//所以返回[i+1,j] [i,j-1]上进行最优选择的最小值
//这也说明了先手的人在大概率下会赢得游戏(在某些情况下先手必赢,比如本题的情况:具体分析看官方解析)
return Math.min(f(i+1,j),f(i,j-1))
}
/**
*
* @param piles
* @return {boolean}
*/
var stoneGame = function(piles) {
return f(0,piles.length-1)>s(0,piles.length-1) //亚历克斯先选和李后选得到的最大值做比较
};
```
**2、动态规划dp做法**
这里采取的是三维的做法
```js
var stoneGame = function (piles) {
let n = piles.length;
// 初始化一个n*n的矩阵 dp数组
let dp = []
for (let i = 0; i < n; i++) {
dp[i] = []
}
// 在三角区域填充
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
dp[i][j] = [0, 0]
}
}
// 填入base case
for (let i = 0; i < n; i++) {
dp[i][i][0] = piles[i];
dp[i][i][1] = 0;
}
// 斜着遍历数组
for (let l = 2; l <= n; l++) {
for (let i = 0; i <= n - 1; i++) {
let j = l + i - 1;
// 先手选择最左边或最右边的分数
let left = piles[i] + dp[i + 1][j][1];
let right = piles[j] + dp[i][j - 1][1];
// 套用状态转移方程
if (left > right) {
dp[i][j][0] = left;
dp[i][j][1] = dp[i + 1][j][0];
} else {
dp[i][j][0] = right;
dp[i][j][1] = dp[i][j - 1][0];
}
}
}
let res = dp[0][n - 1];
return res[0] - res[1]
};
```
###

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何高效寻找素数](https://labuladong.gitbook.io/algo)
* [动态规划解题套路框架](https://labuladong.gitbook.io/algo)
* [如何高效寻找素数](https://labuladong.gitbook.io/algo/)
* [动态规划解题套路框架](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -146,7 +146,7 @@ dp[i] = dp[i - 1] + 1;
但是,如果要按 `C-V`,还要考虑之前是在哪里 `C-A C-C` 的。
**刚才说了,最优的操作序列一定是 `C-A C-C` 接着若干 `C-V`,所以我们用一个变量 `j` 作为若干 `C-V` 的起点**。那么 `j` 之前的 2 个操作就应该是 `C-A C-C` 了:
```java
public int maxA(int N) {
int[] dp = new int[N + 1];
@ -192,7 +192,7 @@ def dp(n, a_num, copy):
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -200,4 +200,60 @@ def dp(n, a_num, copy):
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
### javascript
[651.四键键盘](https://leetcode-cn.com/problems/4-keys-keyboard)
**1、第一种思路**
```js
let maxA = function (N) {
// 备忘录
let memo = {}
let dp = function (n, a_num, copy) {
if (n <= 0) {
return a_num;
}
let key = n + ',' + a_num + ',' + copy
// 避免计算重叠子问题
if (memo[key] !== undefined) {
return memo[key]
}
memo[key] = Math.max(
dp(n - 1, a_num + 1, copy), // A
dp(n - 1, a_num + copy, copy), // C-V
dp(n - 2, a_num, a_num) // C-A C-C
)
return memo[key]
}
return dp(N, 0, 0)
}
```
**2、第二种思路**
```js
var maxA = function (N) {
let dp = new Array(N + 1);
dp[0] = 0;
for (let i = 1; i <= N; i++) {
// 按A键盘
dp[i] = dp[i - 1] + 1;
for (let j = 2; j < i; j++) {
// 全选 & 复制 dp[j-2],连续粘贴 i - j 次
// 屏幕上共 dp[j - 2] * (i - j + 1) 个 A
dp[i] = Math.max(dp[i], dp[j - 2] * (i - (j - 2) - 1));
}
}
// N 次按键之后最多有几个 A
return dp[N];
}
```

View File

@ -10,8 +10,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [我写了首诗,把滑动窗口算法算法变成了默写题](https://labuladong.gitbook.io/algo)
* [二分查找高效判定子序列](https://labuladong.gitbook.io/algo)
* [我写了首诗,把滑动窗口算法算法变成了默写题](https://labuladong.gitbook.io/algo/)
* [二分查找高效判定子序列](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -287,12 +287,139 @@ bool dp(string& s, int i, string& p, int j) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### javascript
[10.正则表达式匹配](https://leetcode-cn.com/problems/regular-expression-matching/)
```js
var isMatch = function (s, p) {
// 备忘录
let memo = {}
let dp = function (s, i, p, j) {
let m = s.length, n = p.length;
// base case
if (j === n) {
return i === m;
}
if (i === m) {
if ((n - j) % 2 === 1) {
return false;
}
for (; j + 1 < n; j += 2) {
if (p[j + 1] !== '*') {
return false;
}
}
return true;
}
// 记录状态ij消除重叠子问题
let key = i + ',' + j
if (memo[key] !== undefined) {
return memo[key];
}
let res = false;
if (s[i] === p[j] || p[j] === '.') {
// 匹配
if (j < n - 1 && p[j + 1] === '*') {
// 1.1 通配符匹配 0 次或多次
res = dp(s, i, p, j + 2) || dp(s, i + 1, p, j);
} else {
// 1.2 常规匹配1次
res = dp(s, i + 1, p, j + 1);
}
} else {
// 不匹配
if (j < n - 1 && p[j + 1] === '*') {
// 2.1 通配符匹配0次
res = dp(s, i, p, j + 2)
} else {
// 2.2 无法继续匹配
res = false
}
}
// 将当前结果记入备忘录
memo[key] = res;
return res;
}
// 指针 ij 从索引 0 开始移动
return dp(s, 0, p, 0);
};
```
### C++
```c++
class Solution {
public:
map<string, bool> memo;
bool isMatch(string s, string p) {
// 指针 ij 从索引 0 开始移动
return dp(s, 0, p, 0);
}
/* 计算 p[j..] 是否匹配 s[i..] */
bool dp(string& s, int i, string& p, int j) {
int m = s.size(), n = p.size();
// base case
if (j == n) {
return i == m;
}
if (i == m) {
if ((n - j) % 2 == 1) {
return false;
}
for (; j + 1 < n; j += 2) {
if (p[j + 1] != '*') {
return false;
}
}
return true;
}
// 记录状态 (i, j),消除重叠子问题
string key = to_string(i) + "," + to_string(j);
if (memo.count(key)) return memo[key];
bool res = false;
if (s[i] == p[j] || p[j] == '.') {
if (j < n - 1 && p[j + 1] == '*') {
res = dp(s, i, p, j + 2)
|| dp(s, i + 1, p, j);
} else {
res = dp(s, i + 1, p, j + 1);
}
} else {
if (j < n - 1 && p[j + 1] == '*') {
res = dp(s, i, p, j + 2);
} else {
res = false;
}
}
// 将当前结果记入备忘录
memo[key] = res;
return res;
}
};
```

View File

@ -10,8 +10,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [动态规划设计:最大子数组](https://labuladong.gitbook.io/algo)
* [一文学会递归解题](https://labuladong.gitbook.io/algo)
* [动态规划设计:最大子数组](https://labuladong.gitbook.io/algo/)
* [一文学会递归解题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -19,7 +19,7 @@
**-----------**
也许有读者看了前文 [动态规划详解](https://labuladong.gitbook.io/algo),学会了动态规划的套路:找到了问题的「状态」,明确了 `dp` 数组/函数的含义,定义了 base case但是不知道如何确定「选择」也就是不到状态转移的关系依然写不出动态规划解法怎么办
也许有读者看了前文 [动态规划详解](https://labuladong.gitbook.io/algo/),学会了动态规划的套路:找到了问题的「状态」,明确了 `dp` 数组/函数的含义,定义了 base case但是不知道如何确定「选择」也就是不到状态转移的关系依然写不出动态规划解法怎么办
不要担心,动态规划的难点本来就在于寻找正确的状态转移方程,本文就借助经典的「最长递增子序列问题」来讲一讲设计动态规划的通用技巧:**数学归纳思想**。
@ -43,7 +43,7 @@
**我们的定义是这样的:`dp[i]` 表示以 `nums[i]` 这个数结尾的最长递增子序列的长度。**
PS为什么这样定义呢这是解决子序列问题的一个套路后文[动态规划之子序列问题解题模板](https://labuladong.gitbook.io/algo) 总结了几种常见套路。你读完本章所有的动态规划问题,就会发现 `dp` 数组的定义方法也就那几种。
PS为什么这样定义呢这是解决子序列问题的一个套路后文[动态规划之子序列问题解题模板](https://labuladong.gitbook.io/algo/) 总结了几种常见套路。你读完本章所有的动态规划问题,就会发现 `dp` 数组的定义方法也就那几种。
根据这个定义,我们就可以推出 base case`dp[i]` 初始值为 1因为以 `nums[i]` 结尾的最长递增子序列起码要包含它自己。
@ -164,7 +164,7 @@ public int lengthOfLIS(int[] nums) {
我们只要把处理扑克牌的过程编程写出来即可。每次处理一张扑克牌不是要找一个合适的牌堆顶来放吗,牌堆顶的牌不是**有序**吗,这就能用到二分查找了:用二分查找来搜索当前牌应放置的位置。
PS旧文[二分查找算法详解](https://labuladong.gitbook.io/algo)详细介绍了二分查找的细节及变体,这里就完美应用上了,如果没读过强烈建议阅读。
PS旧文[二分查找算法详解](https://labuladong.gitbook.io/algo/)详细介绍了二分查找的细节及变体,这里就完美应用上了,如果没读过强烈建议阅读。
```java
public int lengthOfLIS(int[] nums) {
@ -207,16 +207,80 @@ public int lengthOfLIS(int[] nums) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### javascript
[scuhzs](https://github.com/brucecat)提供[300.最长上升子序列](https://leetcode-cn.com/problems/longest-increasing-subsequence)
动态规划做法如下:
```javascript
let lengthOfLIS = function (nums) {
// 用1填满dp数组
let dp = [];
dp.fill(1, 0, nums.length);
for (let i = 1; i < nums.length; i++)
for (let j = 0; j < i; j++)
nums[i] > nums[j] && (dp[i] = Math.max(dp[i], dp[j] + 1))
return nums.length < 2 ? nums.length : Math.max(...dp)
};
```
二分法做法如下:
```javascript
let lengthOfLIS01 = function (nums) {
let top = new Array(nums.length);
for (let i = 0; i < nums.length; i++) {
top[i] = 0;
}
// 牌堆数初始化为 0
let piles = 0;
for (let i = 0; i < nums.length; i++) {
// 要处理的扑克牌
let poker = nums[i];
/***** 搜索左侧边界的二分查找 *****/
let left = 0, right = piles;
while (left < right) {
// 记住这里要向下取整
let mid = Math.floor((left + right) / 2);
if (top[mid] > poker) {
right = mid;
} else if (top[mid] < poker) {
left = mid + 1;
} else {
right = mid;
}
}
/*********************************/
// 没找到合适的牌堆,新建一堆
left === piles && piles++;
// 把这张牌放到牌堆顶
top[left] = poker;
}
// 牌堆数就是 LIS 长度
return piles;
}
```
### python
```python 动态规划
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
@ -262,6 +326,9 @@ class Solution:
```
### c++
[Kian](https://github.com/KianKw/) 提供 C++ 代码
```c++

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [经典动态规划:完全背包问题](https://labuladong.gitbook.io/algo)
* [Git/SQL/正则表达式的在线练习平台](https://labuladong.gitbook.io/algo)
* [经典动态规划:完全背包问题](https://labuladong.gitbook.io/algo/)
* [Git/SQL/正则表达式的在线练习平台](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -216,7 +216,7 @@ int coinChange(int[] coins, int amount);
回到凑零钱问题,为什么说它符合最优子结构呢?比如你想求 `amount = 11` 时的最少硬币数(原问题),如果你知道凑出 `amount = 10` 的最少硬币数(子问题),你只需要把子问题的答案加一(再选一枚面值为 1 的硬币)就是原问题的答案。因为硬币的数量是没有限制的,所以子问题之间没有相互制,是互相独立的。
PS关于最优子结构的问题后文[动态规划答疑篇](https://labuladong.gitbook.io/algo) 还会再举例探讨。
PS关于最优子结构的问题后文[动态规划答疑篇](https://labuladong.gitbook.io/algo/) 还会再举例探讨。
那么,既然知道了这是个动态规划问题,就要思考**如何列出正确的状态转移方程**
@ -360,16 +360,17 @@ PS为啥 `dp` 数组初始化为 `amount + 1` 呢,因为凑成 `amount` 金
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### python
[DapangLiu](https://github.com/DapangLiu) 提供 509. 斐波那契数 Python3 解法代码:
递归写法
@ -409,4 +410,176 @@ class Solution:
for _ in range(n):
dp_0, dp_1 = dp_1, dp_0 + dp_1
return dp_0
```
```
### javascript
#### 一、斐波那契数
**1、暴力递归**
```js
let fib = function (n) {
if (n === 1 || n === 2) {
return 1;
}
return fib(n - 1) + fib(n - 2);
};
```
**2、带备忘录的递归**
```js
let fib = function (n) {
if (n < 1) return 0;
// 备忘录全初始化为 0
let memo = new Array(n + 1);
memo.fill(0, 0, n + 1);
// 进行带备忘录的递归
return helper(memo, n);
}
let helper = function (memo, n) {
// base case
if (n === 1 || n === 2) return 1;
// 已经计算过
if (memo[n] !== 0) return memo[n];
memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
return memo[n];
}
```
**3、dp 数组的迭代解法**
```js
let fib = function (n) {
if (n === 0) return 0;
if (n === 1) return 1;
let dp = new Array(n + 1);
dp.fill(0, 0, n + 1);
// base case
dp[1] = dp[2] = 1;
for (let i = 3; i <= n; i++)
dp[i] = dp[i - 1] + dp[i - 2];
return dp[n];
}
```
##### 4、dp数组 状态压缩
```js
let fib = function (n) {
if (n === 2 || n === 1)
return 1;
let prev = 1, curr = 1;
for (let i = 3; i <= n; i++) {
let sum = prev + curr;
prev = curr;
curr = sum;
}
return curr;
}
```
#### 二、凑零钱
1、**递归写法**
```js
var coinChange = function (coins, amount) {
let dp = function (n) {
// base case
if (n === 0) {
return 0
}
if (n < 0) {
return -1
}
// 求最小值,所以初始化为正无穷 或者是amount+1
let res = amount + 1
for (let coin of coins) {
let subproblem = dp(n - coin)
// 子问题无解,跳过
if (subproblem === -1)
continue;
res = Math.min(res, 1 + subproblem)
}
return res !== amount + 1 ? res : -1;
}
return dp(amount)
};
```
**2、带备忘录的递归写法**
```js
var coinChange = function (coins, amount) {
let memo = {};
this.dp = function (amount) {
if (memo[amount] !== undefined) {
return memo[amount];
}
if (amount === 0) {
return 0;
}
if (amount < 0) {
return -1;
}
let result = Infinity;
for (let coin of coins) {
// 计算子问题
const subResult = dp(amount - coin);
// 子问题无解
if (subResult === -1) {
continue;
}
// 个数为 1 + 子问题的解
result = Math.min(result, 1 + subResult);
}
// 备忘录
memo[amount] = result == Infinity ? -1 : result;
return memo[amount];
};
return dp(amount);
}
```
**3、dp 数组的迭代解法**
```js
var coinChange = function (coins, amount) {
// 数组大小为 amount + 1初始值也为 amount + 1
let dp = new Array(amount + 1);
dp.fill(amount + 1, 0, amount + 1);
// base case
dp[0] = 0;
// 外层 for 循环在遍历所有状态的所有取值
for (let i = 0; i < dp.length; i++) {
// 内层 for 循环在求所有选择的最小值
for (let coin of coins) {
// 子问题无解,跳过
if (i - coin < 0) continue;
dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
}
}
return (dp[amount] === amount + 1) ? -1 : dp[amount];
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [动态规划之KMP字符匹配算法](https://labuladong.gitbook.io/algo)
* [如何判断回文链表](https://labuladong.gitbook.io/algo)
* [动态规划之KMP字符匹配算法](https://labuladong.gitbook.io/algo/)
* [如何判断回文链表](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -420,12 +420,392 @@ int maxProfit_k_any(int max_k, int[] prices) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock)
[买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
[买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/)
[买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/)
[最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/)
[买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)
### javascript
**第一题**
[买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock),相当于`k=1`的情形。
```js
var maxProfit = function (prices) {
let n = prices.length;
if (n <= 1) {
return 0;
}
let dp = new Array(n);
dp.fill([0, 0], 0, n)
// base case
// 解释:
// dp[i][0]
// = max(dp[-1][0], dp[-1][1] + prices[i])
// = max(0, -infinity + prices[i]) = 0
// dp[0][0] = 0;
// 解释:
// dp[i][1]
// = max(dp[-1][1], dp[-1][0] - prices[i])
// = max(-infinity, 0 - prices[i])
// = -prices[i]
dp[0][1] = -prices[0];
// 状态转移
for (let i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], -prices[i])
}
return dp[n - 1][0]
};
```
状态压缩
```js
var maxProfit = function (prices) {
let n = prices.length;
// base case
let dp_i_0 = 0, dp_i_1 = -prices[0];
for (let i = 1; i < n; i++) {
// dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
// dp[i][1] = max(dp[i-1][1], -prices[i])
dp_i_1 = Math.max(dp_i_1, -prices[i]);
}
return dp_i_0;
}
```
**第二题**
[买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/),相当于`k = +infinity`的情形。
```js
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
let n = prices.length;
let dp = new Array(n);
dp.fill([0, 0], 0, n)
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (let i = 1; i < n; i++) {
dp[i][0] = Math.max(
dp[i - 1][0],
dp[i - 1][1] + prices[i]
)
dp[i][1] = Math.max(
dp[i - 1][1],
dp[i - 1][0] - prices[i]
)
}
return dp[n - 1][0]
};
```
状态压缩
```js
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
let n = prices.length;
// base case
let dp_i_0 = 0, dp_i_1 = -prices[0];
for (let i = 0; i < n; i++) {
// dp[i][0] = Math.max(
// dp[i - 1][0],
// dp[i - 1][1] + prices[i]
// )
dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
// dp[i][1] = Math.max(
// dp[i - 1][1],
// dp[i - 1][0] - prices[i]
// )
dp_i_1 = Math.max(dp_i_1, dp_i_0 - prices[i])
}
return dp_i_0;
};
```
**第三题**
[最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/),相当于`k = +infinity with cooldown`的情形。
- 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
```
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-2][0] - prices[i])
解释:第 i 天选择 buy 的时候,要从 i-2 的状态转移,而不是 i-1 。
```
```js
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
let n = prices.length;
if (n < 2) {
return 0;
}
if (n === 2) {
return Math.max(prices[1] - prices[0], 0)
}
let dp = new Array(n);
for (let i = 0; i < n; i++) {
dp[i] = [0, 0]
}
// base case
// dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[1][0] = Math.max(
dp[0][0],
dp[0][1] + prices[1]
)
dp[1][1] = Math.max(
dp[0][1],
dp[0][0] - prices[1]
);
// 状态转移
for (let i = 2; i < n; i++) {
dp[i][0] = Math.max(
dp[i - 1][0],
dp[i - 1][1] + prices[i]
)
dp[i][1] = Math.max(
dp[i - 1][1],
dp[i - 2][0] - prices[i] // 买被限制在卖一天后了
)
}
return dp[n - 1][0];
};
```
状态压缩
```js
/**
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(prices) {
let n = prices.length;
let dp_i_0 = 0;
let dp_i_1 = -Infinity; // 还未买入
let dp_pre_0 = 0; // 代表 dp[i-2][0]
for (let i = 0; i < n; i++) {
let temp = dp_i_0;
dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
dp_i_1 = Math.max(dp_i_1, dp_pre_0 - prices[i]);
dp_pre_0 = temp;
}
return dp_i_0;
};
```
**第四题**
[买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)。`k = +infinity with fee`的情形。
每次交易要支付手续费,只要把手续费从利润中减去即可。
```
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i] - fee)
解释:相当于买入股票的价格升高了。
在第一个式子里减也是一样的,相当于卖出股票的价格减小了。
```
```js
/**
* @param {number[]} prices
* @param {number} fee
* @return {number}
*/
var maxProfit = function(prices, fee) {
let n = prices.length;
let dp = new Array(n);
for (let i = 0; i < n; i++) {
dp[i] = [0, 0]
}
// base case
// dp[0][0] = 0;
dp[0][1] = -prices[0] - fee;
// 状态转移
for (let i = 1; i < n; i++) {
dp[i][0] = Math.max(
dp[i - 1][0],
dp[i - 1][1] + prices[i]
)
dp[i][1] = Math.max(
dp[i - 1][1],
dp[i - 1][0] - prices[i] - fee // 相当于买入股票的价格升高了
)
}
return dp[n - 1][0];
};
```
状态压缩
```js
var maxProfit = function (prices, fee) {
let n = prices.length;
// base case
let dp_i_0 = 0, dp_i_1 = -prices[0] - fee;
for (let i = 0; i < n; i++) {
dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i]);
dp_i_1 = Math.max(dp_i_1, dp_i_0 - prices[i] - fee)
}
return dp_i_0;
};
```
**第五题**
[买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/)。`k = 2 `的情形。
```
dp[-1][k][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0 。
dp[-1][k][1] = -infinity
解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0 。
dp[i][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
max( 选择 rest , 选择 sell )
解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest所以我今天还是没有持有
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
max( 选择 rest , 选择 buy )
解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest所以我今天还持有着股票
要么我昨天本没有持有,但今天我选择 buy所以今天我就持有股票了。
```
```js
var maxProfit = function(prices) {
//第一次 买入, 卖出的利润
let profit_1_in = -prices[0], profit_1_out = 0;
//继第一次之后,第二次买入卖出的利润
let profit_2_in = -prices[0], profit_2_out = 0;
let n = prices.length;
for (let i = 1; i < n; i++){
profit_2_out = Math.max(profit_2_out, profit_2_in + prices[i]);
//第二次买入后的利润, 第一次卖出的利润 - prices[i]
profit_2_in = Math.max(profit_2_in, profit_1_out - prices[i]);
profit_1_out = Math.max(profit_1_out, profit_1_in + prices[i]);
//第一次买入后,利润为 -prices[i]
profit_1_in = Math.max(profit_1_in, -prices[i]);
}
return profit_2_out;
};
```
**第六题**
[买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/)。k = any integer的情形。
```js
/**
* @param {number} k
* @param {number[]} prices
* @return {number}
*/
var maxProfit = function(k, prices) {
if (!prices.length) {
return 0;
}
const n = prices.length;
k = Math.min(k, Math.floor(n / 2));
const buy = new Array(k + 1).fill(0);
const sell = new Array(k + 1).fill(0);
buy[0]= -prices[0]
sell[0] = 0
for (let i = 1; i < k + 1; ++i) {
buy[i] = sell[i] = -Number.MAX_VALUE;
}
for (let i = 1; i < n; ++i) {
buy[0] = Math.max(buy[0], sell[0] - prices[i]);
for (let j = 1; j < k + 1; ++j) {
buy[j] = Math.max(buy[j], sell[j] - prices[i]);
sell[j] = Math.max(sell[j], buy[j - 1] + prices[i]);
}
}
return Math.max(...sell)
};
```
======其他语言代码======

View File

@ -12,8 +12,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [洗牌算法](https://labuladong.gitbook.io/algo)
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo)
* [洗牌算法](https://labuladong.gitbook.io/algo/)
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -166,7 +166,7 @@ int longestPalindromeSubseq(string s) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版微信扫码关注公众号后台回复「小抄」限时免费获取回复「进群」可进刷题群一起刷题labuladong 带你搞定 LeetCode**。
@ -174,4 +174,46 @@ int longestPalindromeSubseq(string s) {
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[516.最长回文子序列](https://leetcode-cn.com/problems/longest-palindromic-subsequence)
### javascript
```js
/**
* @param {string} s
* @return {number}
*/
var longestPalindromeSubseq = function (s) {
let l = s.length;
if (l <= 1) {
return l;
}
// 初始化一个 dp[l][l]
let dp = new Array(l);
for (let i = 0; i < l; i++) {
dp[i] = new Array(l);
dp[i].fill(0, 0, l)
// // base case
dp[i][i] = 1
}
// 从右下角开始,逐渐往上推
for (let i = l - 2; i >= 0; i--) {
for (let j = i + 1; j <= l - 1; j++) {
if (s[i] === s[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(
dp[i + 1][j],
dp[i][j - 1]
)
}
}
}
return dp[0][l - 1]
};
```

View File

@ -10,7 +10,7 @@
![](../pictures/souyisou.png)
相关推荐:
* [动态规划之四键键盘](https://labuladong.gitbook.io/algo)
* [动态规划之四键键盘](https://labuladong.gitbook.io/algo/)
* [经典动态规划:子集背包问题](/https://labuladong.gitbook.io/algo)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -250,15 +250,23 @@ int[] dp(TreeNode root) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[198.打家劫舍](https://leetcode-cn.com/problems/house-robber)
[213.打家劫舍II](https://leetcode-cn.com/problems/house-robber-ii)
[337.打家劫舍III](https://leetcode-cn.com/problems/house-robber-iii)
### python
[Shantom](https://github.com/Shantom) 提供 198. House Robber I Python3 解法代码:
```Python
@ -321,3 +329,157 @@ class Solution:
return max(dp(root))
```
### javascript
#### House Robber I
自顶向下
```js
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function (nums) {
let memo = new Array(nums.length);
memo.fill(-1, 0, nums.length)
// 返回nums[start..]能抢到的最大值
let dp = function (nums, start) {
if (start >= nums.length) {
return 0;
}
// 避免重复计算
if (memo[start] !== -1) return memo[start];
let res = Math.max(
// 不抢,去下一家
dp(nums, start + 1),
// 抢, 然后去下下家抢
nums[start] + dp(nums, start + 2)
)
// 记入备忘录
memo[start] = res;
return res;
}
// 强盗从第 0 间房子开始决定抢劫哪家
return dp(nums, 0)
};
```
自底向上
```js
var rob = function (nums) {
let n = nums.length;
// dp[i] = x 表示:
// 从第 i 间房子开始抢劫,最多能抢到的钱为 x
// base case: dp[n] = 0
let dp = new Array(n + 2);
dp.fill(0, 0, n + 2)
for (let i = n - 1; i >= 0; i--) {
dp[i] = Math.max(
dp[i + 1],
nums[i] + dp[i + 2]
)
}
// 强盗从第 0 间房子开始决定抢劫哪家
return dp[0]
};
```
自底向上 + 状态压缩
```js
var rob = function (nums) {
let n = nums.length;
// 记录 dp[i+1] 和 dp[i+2]
let dp_i_1 = 0, dp_i_2 = 0;
// 记录 dp[i]
let dp_i = 0;
for (let i = n - 1; i >= 0; i--) {
dp_i = Math.max(dp_i_1, nums[i] + dp_i_2);
dp_i_2 = dp_i_1;
dp_i_1 = dp_i;
}
return dp_i;
};
```
#### House Robber II
```js
var rob = function (nums) {
let n = nums.length;
if (n === 1) return nums[0];
// 仅计算闭区间 [start,end] 的最优结果
let robRange = function (nums, start, end) {
let dp_i_1 = 0, dp_i_2 = 0;
let dp_i = 0;
for (let i = end; i >= start; i--) {
dp_i = Math.max(dp_i_1, nums[i] + dp_i_2);
dp_i_2 = dp_i_1;
dp_i_1 = dp_i;
}
return dp_i;
}
return Math.max(
robRange(nums, 0, n - 2),
robRange(nums, 1, n - 1)
)
};
```
#### House Robber III
```js
var rob = function (root) {
let res = dp(root);
return Math.max(res[0], res[1]);
};
var dp = function (root){
if(root == null){
return [0,0];
}
let left = dp(root.left);
let right = dp(root.right);
// 抢,下家就不能抢了
let rob = root.val + left[0] + right[0];
// 不抢,下家可抢可不抢,取决于收益大小
let not_rob = Math.max(left[0], left[1]) + + Math.max(right[0], right[1]);
return [not_rob, rob]
}
```

View File

@ -12,8 +12,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [搜索引擎背后的经典数据结构和算法](https://labuladong.gitbook.io/algo)
* [动态规划之四键键盘](https://labuladong.gitbook.io/algo)
* [搜索引擎背后的经典数据结构和算法](https://labuladong.gitbook.io/algo/)
* [动态规划之四键键盘](https://labuladong.gitbook.io/algo/)
**-----------**
@ -54,7 +54,7 @@ return result;
当然,上面这个例子太简单了,不过请读者回顾一下,我们做动态规划问题,是不是一直在求各种最值,本质跟我们举的例子没啥区别,无非需要处理一下重叠子问题。
前文不[同定义不同解法](https://labuladong.gitbook.io/algo) 和 [高楼扔鸡蛋进阶](https://labuladong.gitbook.io/algo) 就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。
前文不[同定义不同解法](https://labuladong.gitbook.io/algo/) 和 [高楼扔鸡蛋进阶](https://labuladong.gitbook.io/algo/) 就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。
再举个常见但也十分简单的例子,求一棵二叉树的最大值,不难吧(简单起见,假设节点中的值都是非负数):
@ -149,12 +149,45 @@ for (int i = 1; i < m; i++)
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### javascript
正向遍历
```js
// 构建m*n的矩阵
let dp = new Array(m).fill(new Array(n))
for (let i = 0; i < m; i++)
for (let j = 0; j < n; j++)
// 计算 dp[i][j]
```
反向遍历
```js
for (let i = m - 1; i >= 0; i--)
for (let j = n - 1; j >= 0; j--)
// 计算 dp[i][j]
```
斜向遍历
```js
// 斜着遍历数组
for (let l = 2; l <= n; l++) {
for (let i = 0; i <= n - l; i++) {
let j = l + i - 1;
// 计算 dp[i][j]
}
}
```
======其他语言代码======

View File

@ -1,4 +1,4 @@
# 最长公共子序列
# 最长公共子序列
<p align='center'>
@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo/)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -139,16 +139,18 @@ else:
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[1143.最长公共子序列](https://leetcode-cn.com/problems/longest-common-subsequence)
### c++
[Edwenc](https://github.com/Edwenc) 提供 C++ 代码:
@ -183,6 +185,10 @@ public:
};
```
### java
[Shawn](https://github.com/Shawn-Hx) 提供 Java 代码:
```java
@ -207,4 +213,128 @@ public int longestCommonSubsequence(String text1, String text2) {
}
```
### python
[lo-tp](http://blog.lotp.xyz/) 提供 Python 代码:
```python
class Solution(object):
def longestCommonSubsequence(self, text1, text2):
# calculate the size of the first and second string
sz1, sz2 = len(text1), len(text2)
# since to calculate dp(i,j) we only need dp(i-1,j-1), dp(i-1,j), dp(i,j-1)
# we don't have to save data before i-1
# we use dp to save dp(i-1, 0), dp(i-1, 1)....dp(i-1, sz2)
# we use tmp to save dp(i, 0), dp(i,1)....(dpi-1, sz2)
tmp, dp = [0]*(sz2+1), [0]*(sz2+1)
for i in range(0, sz1):
for j in range(0, sz2):
tmp[j+1] = dp[j] + \
1 if text1[i] == text2[j] else max(tmp[j], dp[j+1])
# In the next iteration, we will calculate dp(i+1,0),dp(i+1, 1)....dp(i+1,sz2)
# So we exchange dp and tmp
tmp, dp = dp, tmp
return dp[-1]
```
### javascript
**暴力解法**
```js
var longestCommonSubsequence = function (text1, text2) {
let s1 = text1.length;
let s2 = text2.length;
let dp = function (i, j) {
// 空串的base case
if (i === -1 || j === -1) {
return 0;
}
if (text1[i] === text2[j]) {
// 这边找到一个 lcs 的元素,继续往前找
return dp(i - 1, j - 1) + 1
} else {
// 谁能让lcs最长就听谁的
return Math.max(dp(i - 1, j), dp(i, j - 1))
}
}
// i 和 j 初始化为最后一个索引
return dp(s1 - 1, s2 - 1)
};
```
**暴力解法+备忘录优化**
```js
var longestCommonSubsequence = function (text1, text2) {
let s1 = text1.length;
let s2 = text2.length;
let memo = new Map();
let dp = function (i, j) {
// 空串的base case
if (i === -1 || j === -1) {
return 0;
}
// 查询一下备忘录,防止重复计算
let key = i + "," + j
if (memo.has(key)) {
return memo.get(key)
}
let res;
if (text1[i] === text2[j]) {
// 这边找到一个 lcs 的元素,继续往前找
// 记入备忘录
res = dp(i - 1, j - 1) + 1
memo.set(key, res)
} else {
// 谁能让lcs最长就听谁的
res = Math.max(dp(i - 1, j), dp(i, j - 1))
memo.set(key, res)
}
return res;
}
// i 和 j 初始化为最后一个索引
return dp(s1 - 1, s2 - 1)
};
```
**DPtable优化**
```js
var longestCommonSubsequence = function (text1, text2) {
let s1 = text1.length;
let s2 = text2.length;
// 构建 DP table 和 base case
// 初始化一个 s1+1*s2+1的dp表
let dp = new Array(s1 + 1);
for (let i = 0; i < s1 + 1; i++) {
dp[i] = new Array(s2 + 1);
dp[i].fill(0, 0, s2 + 1)
}
// 进行状态转移
for (let i = 1; i < s1 + 1; i++) {
for (let j = 1; j < s2 + 1; j++) {
if (text1[i - 1] === text2[j - 1]) {
// 找到一个lcs中的字符
dp[i][j] = 1 + dp[i - 1][j - 1]
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
}
}
}
// i 和 j 初始化为最后一个索引
return dp[s1][s2]
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [labuladong优质作者扶持计划](https://labuladong.gitbook.io/algo)
* [动态规划设计:最大子数组](https://labuladong.gitbook.io/algo)
* [labuladong优质作者扶持计划](https://labuladong.gitbook.io/algo/)
* [动态规划设计:最大子数组](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -283,18 +283,20 @@ class Node {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章,公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章,公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[ChenjieXu](https://github.com/ChenjieXu) 提供Python版本代码
```python3
### python
[ChenjieXu](https://github.com/ChenjieXu) 提供Python版本[72.编辑距离](https://leetcode-cn.com/problems/edit-distance)代码:
```python
def minDistance(word1, word2):
m, n = len(word1), len(word2)
# 创建 DP 数组
@ -318,4 +320,92 @@ def minDistance(word1, word2):
dp[i - 1][j - 1] + 1)
# 储存着整个 word1 和 word2 的最小编辑距离
return dp[m][n]
````
````
### javascript
[SCUHZS](https://github.com/brucecat)提供[72.编辑距离](https://leetcode-cn.com/problems/edit-distance)
```javascript
let minDistance = function (s1, s2) {
let m = s1.length, n = s2.length;
// 初始化一个 (m+1) * (n+1)大小的数组
let dp = new Array(m + 1);
for (let i = 0; i < m + 1; i++) {
dp[i] = new Array(n + 1).fill(0)
}
for (let i = 1; i <= m; i++) {
dp[i][0] = i;
}
for (let j = 1; j <= n; j++)
dp[0][j] = j;
// 自底向上求解
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (s1[i - 1] === s2[j - 1])
dp[i][j] = dp[i - 1][j - 1]
else
dp[i][j] = Math.min(
dp[i - 1][j] + 1, // 删除
dp[i][j - 1] + 1, // 插入
dp[i - 1][j - 1] + 1 // 替换
)
}
}
// 储存着整个 s1 和 s2 的最小编辑距离
return dp[m][n];
};
```
上面的代码还可以进行状态压缩优化,我们还需要一个额外的变量 pre 来时刻保存 (i-1,j-1) 的值。推导公式就可以从二维的:
```
dp[i][j] = min(dp[i-1][j] , dp[i-1][j-1] , dp[i][j-1]) + 1
```
转化为一维的:
```
dp[i] = min(dp[i-1], pre, dp[i]) + 1
```
完整代码如下:
```js
let minDistance = function (word1, word2) {
let m = word1.length, n = word2.length;
// 初始化一个数组
let dp = new Array(Math.max(m,n) + 1)
// dp[0...n]的初始值
for (let j = 0; j <= n; j++)
dp[j] = j;
// dp[j] = min(dp[j-1], pre, dp[j]) + 1
for (let i = 1; i <= m; i++) {
let temp = dp[0];
// 相当于初始化
dp[0] = i;
for (let j = 1; j <= n; j++) {
// pre 相当于之前的 dp[i-1][j-1]
let pre = temp;
temp = dp[j];
// 如果 word1[i] 与 word2[j] 相等。第 i 个字符对应下标是 i-1
if (word1[i - 1] === word2[j - 1]) {
dp[j] = pre;
} else {
dp[j] = Math.min(Math.min(dp[j - 1], pre), dp[j]) + 1;
}
}
}
return dp[n];
};
```

View File

@ -1,4 +1,4 @@
# 贪心算法之区间调度问题
# 贪心算法之区间调度问题
<p align='center'>
@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何判定括号合法性](https://labuladong.gitbook.io/algo)
* [一文解决三道区间问题](https://labuladong.gitbook.io/algo)
* [如何判定括号合法性](https://labuladong.gitbook.io/algo/)
* [一文解决三道区间问题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -150,16 +150,19 @@ int findMinArrowShots(int[][] intvs) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[435. 无重叠区间](https://leetcode-cn.com/problems/non-overlapping-intervals/)
[452.用最少数量的箭引爆气球](https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons)
### python
Edwenc 提供 第435题的python3 代码:
@ -197,4 +200,116 @@ class Solution:
# 最后返回的是 需要移除的区间个数
return n-count
```
```
### javascript
**区间调度实现**
```js
var intervalSchedule = function (intvs) {
if (intvs.length === 0) return 0;
// 按end升序排序
intvs.sort((a, b) => {
if (a[1] < b[1])
return -1;
else if (a[1] > b[1])
return 1;
else return 0;
})
// 至少有一个区间不相交
let count = 1;
// 排序后,第一个区间就是 x
let x_end = intvs[0][1];
for (let interval of intvs) {
let start = interval[0];
if (start >= x_end) {
// 找到下一个选择的区间了
count++;
x_end = interval[1];
}
}
return count;
}
```
**第435题 无重叠区间**
```js
/**
* @param {number[][]} intervals
* @return {number}
*/
var eraseOverlapIntervals = function (intervals) {
let n = intervals.length;
// 我们已经会求最多有几个区间不会重叠了,那么剩下的不就是至少需要去除的区间吗?
return n - intervalSchedule(intervals);
};
var intervalSchedule = function (intvs) {
if (intvs.length === 0) return 0;
// 按end升序排序
intvs.sort((a, b) => {
if (a[1] < b[1])
return -1;
else if (a[1] > b[1])
return 1;
else return 0;
})
// 至少有一个区间不相交
let count = 1;
// 排序后,第一个区间就是 x
let x_end = intvs[0][1];
for (let interval of intvs) {
let start = interval[0];
if (start >= x_end) {
// 找到下一个选择的区间了
count++;
x_end = interval[1];
}
}
return count;
}
```
**第452题 用最少数量的箭引爆气球**
```js
/**
* @param {number[][]} points
* @return {number}
*/
var findMinArrowShots = function (intvs) {
if (intvs.length === 0) return 0;
// 按end升序排序
intvs.sort((a, b) => {
if (a[1] < b[1])
return -1;
else if (a[1] > b[1])
return 1;
else return 0;
})
// 至少有一个区间不相交
let count = 1;
// 排序后,第一个区间就是 x
let x_end = intvs[0][1];
for (let interval of intvs) {
let start = interval[0];
if (start > x_end) {
// 找到下一个选择的区间了
count++;
x_end = interval[1];
}
}
return count;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第二期)](https://labuladong.gitbook.io/algo)
* [状态压缩:对动态规划进行降维打击](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第二期)](https://labuladong.gitbook.io/algo/)
* [状态压缩:对动态规划进行降维打击](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -287,7 +287,7 @@ while (lo < hi) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -295,4 +295,40 @@ while (lo < hi) {
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[887.鸡蛋掉落](https://leetcode-cn.com/problems/super-egg-drop/)
### javascript
```js
/**
* @param {number} K
* @param {number} N
* @return {number}
*/
var superEggDrop = function (K, N) {
// m 最多不会超过 N 次(线性扫描)
// 初始化一个 (K+1)(N+1) 的矩阵
let dp = new Array(K + 1);
// base case:
// dp[0][..] = 0
// dp[..][0] = 0
// 初始化数组都为 0
for (let i = 0; i < K + 1; i++) {
dp[i] = new Array(N + 1);
dp[i].fill(0, 0, N + 1);
}
let m = 0;
while (dp[K][m] < N) {
m++;
for (let k = 1; k <= K; k++) {
dp[k][m] = dp[k][m - 1] + dp[k - 1][m - 1] + 1;
}
}
return m;
};
```

View File

@ -12,8 +12,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何判断回文链表](https://labuladong.gitbook.io/algo)
* [SQL进阶技巧](https://labuladong.gitbook.io/algo)
* [如何判断回文链表](https://labuladong.gitbook.io/algo/)
* [SQL进阶技巧](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -76,7 +76,7 @@ PS这有点像 Big O 表示法计算​算法的复杂度。
**「选择」其实就是去选择哪层楼扔鸡蛋**。回顾刚才的线性扫描和二分思路,二分查找每次选择到楼层区间的中间去扔鸡蛋,而线性扫描选择一层层向上测试。不同的选择会造成状态的转移。
现在明确了「状态」和「选择」,**动态规划的基本思路就形成了**:肯定是个二维的 `dp` 数组或者带有两个状态参数的 `dp` 函数来表示状态转移;外加一个 for 循环来遍历所有选择,择最优的选择更新状态:
```python
# 当前状态为 K 个鸡蛋,面对 N 层楼
# 返回这个状态下的最优结果
@ -243,7 +243,7 @@ def superEggDrop(self, K: int, N: int) -> int:
return dp(K, N)
```
这里就不展开其他解法了,留在下一篇文章 [高楼扔鸡蛋进阶](https://labuladong.gitbook.io/algo)
这里就不展开其他解法了,留在下一篇文章 [高楼扔鸡蛋进阶](https://labuladong.gitbook.io/algo/)
我觉得吧,我们这种解法就够了:找状态,做选择,足够清晰易懂,可流程化,可举一反三。掌握这套框架学有余力的话,再去考虑那些奇技淫巧也不迟。
@ -253,7 +253,7 @@ def superEggDrop(self, K: int, N: int) -> int:
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -261,4 +261,110 @@ def superEggDrop(self, K: int, N: int) -> int:
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[887.鸡蛋掉落](https://leetcode-cn.com/problems/super-egg-drop/)
### javascript
**dp状态转移 + 备忘录**
```js
var superEggDrop = function (K, N) {
let memo = {}
let dp = function (K, N) {
// base case
if (K === 1) return N;
if (N === 0) return 0;
// 避免重复计算
let key = K + ',' + N
if (memo[key] !== undefined) {
return memo[key];
}
// 正无穷
let res = Infinity;
// 穷举所有的可能的选择
for (let i = 1; i < N + 1; i++) {
res = Math.min(
res,
Math.max(
dp(K, N - i),
dp(K - 1, i - 1)
) + 1
)
}
// 记入备忘录
memo[key] = res;
return res;
}
return dp(K, N);
};
```
**dp状态转移 + 备忘录 + 二分法**
```js
var superEggDrop = function (K, N) {
let memo = {}
let dp = function (K, N) {
// base case
if (K === 1) return N;
if (N === 0) return 0;
// 避免重复计算
let key = K + ',' + N
if (memo[key] !== undefined) {
return memo[key];
}
// 正无穷
let res = Infinity;
// 穷举所有的可能的选择
// for (let i = 1; i < N + 1; i++) {
// res = Math.min(
// res,
// Math.max(
// dp(K, N - i),
// dp(K - 1, i - 1)
// ) + 1
// )
// }
// 用二分搜索代替线性搜索
let lo = 1, hi = N;
while (lo <= hi) {
let mid = Math.floor((lo + hi) / 2);
let broken = dp(K - 1, mid - 1) // 碎
let not_broken = dp(K, N - mid) // 没碎
// res = min(max(碎,没碎) + 1)
if (broken > not_broken) {
hi = mid - 1
res = Math.min(res, broken + 1)
} else {
lo = mid + 1
res = Math.min(res, not_broken + 1)
}
}
// 记入备忘录
memo[key] = res;
return res;
}
return dp(K, N);
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [状态压缩:对动态规划进行降维打击](https://labuladong.gitbook.io/algo)
* [我用四个命令概括了 Git 的所有套路](https://labuladong.gitbook.io/algo)
* [状态压缩:对动态规划进行降维打击](https://labuladong.gitbook.io/algo/)
* [我用四个命令概括了 Git 的所有套路](https://labuladong.gitbook.io/algo/)
**-----------**
@ -103,10 +103,10 @@ $ logout
类似的,还有一种后台运行常用的做法是这样:
```shell
$ nohub some_cmd &
$ nohup some_cmd &
```
`nohub`命令也是类似的原理,不过通过我的测试,还是`(cmd &)`这种形式更加稳定。
`nohup`命令也是类似的原理,不过通过我的测试,还是`(cmd &)`这种形式更加稳定。
### 三、单引号和双引号的区别
@ -149,7 +149,7 @@ $ sudo /home/fdl/bin/connect.sh
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -157,4 +157,4 @@ $ sudo /home/fdl/bin/connect.sh
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [一文解决三道区间问题](https://labuladong.gitbook.io/algo)
* [Union-Find算法详解](https://labuladong.gitbook.io/algo)
* [一文解决三道区间问题](https://labuladong.gitbook.io/algo/)
* [Union-Find算法详解](https://labuladong.gitbook.io/algo/)
**-----------**
@ -133,7 +133,7 @@ $ cmd1 | cmd2 | cmd3
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [烧饼排序](https://labuladong.gitbook.io/algo)
* [动态规划之正则表达](https://labuladong.gitbook.io/algo)
* [烧饼排序](https://labuladong.gitbook.io/algo/)
* [动态规划之正则表达](https://labuladong.gitbook.io/algo/)
**-----------**
@ -98,7 +98,7 @@ Redis 监听的默认端口是 6379我们设置它接收网卡 127.0.0.1 的
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何计算完全二叉树的节点数](https://labuladong.gitbook.io/algo)
* [Linux的进程、线程、文件描述符是什么](https://labuladong.gitbook.io/algo)
* [如何计算完全二叉树的节点数](https://labuladong.gitbook.io/algo/)
* [Linux的进程、线程、文件描述符是什么](https://labuladong.gitbook.io/algo/)
**-----------**
@ -147,7 +147,7 @@ https://github.com/astaxie/build-web-application-with-golang
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [区间调度之区间合并问题](https://labuladong.gitbook.io/algo)
* [别再说你不懂Linux内存管理了10张图给你安排的明明白白](https://labuladong.gitbook.io/algo)
* [区间调度之区间合并问题](https://labuladong.gitbook.io/algo/)
* [别再说你不懂Linux内存管理了10张图给你安排的明明白白](https://labuladong.gitbook.io/algo/)
**-----------**
@ -111,7 +111,7 @@ https://sqlzoo.net/
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [我用四个命令概括了 Git 的所有套路](https://labuladong.gitbook.io/algo)
* [一个方法团灭 nSum 问题](https://labuladong.gitbook.io/algo)
* [我用四个命令概括了 Git 的所有套路](https://labuladong.gitbook.io/algo/)
* [一个方法团灭 nSum 问题](https://labuladong.gitbook.io/algo/)
**-----------**
@ -194,7 +194,7 @@ HTTPS 协议中的 SSL/TLS 安全层会组合使用以上几种加密方式,**
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -203,4 +203,4 @@ HTTPS 协议中的 SSL/TLS 安全层会组合使用以上几种加密方式,**
</p>
======其他语言代码======
[test ad](https://labuladong.gitbook.io/algo)
[test ad](https://labuladong.gitbook.io/algo/)

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [算法就像搭乐高:带你手撸 LRU 算法](https://labuladong.gitbook.io/algo)
* [拆解复杂问题:实现计算器](https://labuladong.gitbook.io/algo)
* [算法就像搭乐高:带你手撸 LRU 算法](https://labuladong.gitbook.io/algo/)
* [拆解复杂问题:实现计算器](https://labuladong.gitbook.io/algo/)
**-----------**
@ -231,12 +231,129 @@ public Key delMax() {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### javascript
```js
/**
* 最大堆
*/
function left(i) {
return i * 2 + 1;
}
function right(i) {
return i * 2 + 2;
}
function swap(A, i, j) {
const t = A[i];
A[i] = A[j];
A[j] = t;
}
class Heap {
constructor(arr) {
this.data = [...arr];
this.size = this.data.length;
}
/**
* 重构堆
*/
rebuildHeap() {
const L = Math.floor(this.size / 2);
for (let i = L - 1; i >= 0; i--) {
this.maxHeapify(i);
}
}
isHeap() {
const L = Math.floor(this.size / 2);
for (let i = L - 1; i >= 0; i++) {
const l = this.data[left(i)] || Number.MIN_SAFE_INTEGER;
const r = this.data[right(i)] || Number.MIN_SAFE_INTEGER;
const max = Math.max(this.data[i], l, r);
if (max !== this.data[i]) {
return false;
}
return true;
}
}
sort() {
for (let i = this.size - 1; i > 0; i--) {
swap(this.data, 0, i);
this.size--;
this.maxHeapify(0);
}
}
insert(key) {
this.data[this.size++] = key;
if (this.isHeap()) {
return;
}
this.rebuildHeap();
}
delete(index) {
if (index >= this.size) {
return;
}
this.data.splice(index, 1);
this.size--;
if (this.isHeap()) {
return;
}
this.rebuildHeap();
}
/**
* 堆的其他地方都满足性质
* 唯独跟节点,重构堆性质
* @param {*} i
*/
maxHeapify(i) {
let max = i;
if (i >= this.size) {
return;
}
// 求左右节点中较大的序号
const l = left(i);
const r = right(i);
if (l < this.size && this.data[l] > this.data[max]) {
max = l;
}
if (r < this.size && this.data[r] > this.data[max]) {
max = r;
}
// 如果当前节点最大,已经是最大堆
if (max === i) {
return;
}
swap(this.data, i, max);
// 递归向下继续执行
return this.maxHeapify(max);
}
}
module.exports = Heap;
```
======其他语言代码======

View File

@ -1,4 +1,4 @@
# 二叉搜索树操作集锦
# 二叉搜索树操作集锦
<p align='center'>
@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo)
* [一行代码就能解决的算法题](https://labuladong.gitbook.io/algo)
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo/)
* [一行代码就能解决的算法题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -28,7 +28,7 @@
**-----------**
通过之前的文章[框架思维](https://labuladong.gitbook.io/algo),二叉树的遍历框架应该已经印到你的脑子里了,这篇文章就来实操一下,看看框架思维是怎么灵活运用,秒杀一切二叉树问题的。
通过之前的文章[框架思维](https://labuladong.gitbook.io/algo/),二叉树的遍历框架应该已经印到你的脑子里了,这篇文章就来实操一下,看看框架思维是怎么灵活运用,秒杀一切二叉树问题的。
二叉树算法的设计的总路线:明确一个节点要做的事情,然后剩下的事抛给框架。
@ -301,7 +301,7 @@ void BST(TreeNode root, int target) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -309,7 +309,18 @@ void BST(TreeNode root, int target) {
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[100.相同的树](https://leetcode-cn.com/problems/same-tree)
[450.删除二叉搜索树中的节点](https://leetcode-cn.com/problems/delete-node-in-a-bst)
[701.二叉搜索树中的插入操作](https://leetcode-cn.com/problems/insert-into-a-binary-search-tree)
[700.二叉搜索树中的搜索](https://leetcode-cn.com/problems/search-in-a-binary-search-tree)
[98.验证二叉搜索树](https://leetcode-cn.com/problems/validate-binary-search-tree)
### c++
@ -347,6 +358,43 @@ public:
};
```
[yanggg1997](https://github.com/yanggg1997)提供第100题C++代码:
``` c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
// 若当前节点均为空,则此处相同
if(!p && !q) return true;
// 若当前节点在一棵树上有而另一棵树上为空,则两棵树不同
if(!p && q) return false;
if(p && !q) return false;
// 若当前节点在两棵树上均存在。
if(p->val != q->val)
{
return false;
}
else
{
// 向左右子树分别递归判断
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
}
};
```
### python
[ChenjieXu](https://github.com/ChenjieXu)提供第98题Python3代码
@ -399,6 +447,7 @@ class Solution:
return p.val==q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
```
[Edwenc](https://github.com/Edwenc) 提供 leetcode第450题的python3 代码:
```python
@ -472,3 +521,200 @@ public boolean isValidBST(TreeNode root) {
return leftRes && rightRes;
}
```
### javascript
1. 如何把二叉树所有的节点中的值加一?
热热身,体会体会二叉树的递归思想。
```js
let plusOne = function(root) {
if (root == null) return;
root.val += 1;
plusOne(root.left);
plusOne(root.right);
}
```
2. 如何判断两棵二叉树是否完全相同?
[100.相同的树](https://leetcode-cn.com/problems/same-tree)
```js
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} p
* @param {TreeNode} q
* @return {boolean}
*/
var isSameTree = function(p, q) {
if(p == null && q == null)
return true;
if(p == null || q == null)
return false;
if(p.val != q.val)
return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
};
```
零、判断 BST 的合法性
[98. 验证二叉搜索树](https://leetcode-cn.com/problems/validate-binary-search-tree/)
```js
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isValidBST = function (root) {
return helper(root, null, null);
};
var helper = function (root, min, max) {
if (root == null) return true;
if (min != null && root.val <= min.val) return false;
if (max != null && root.val >= max.val) return false;
return helper(root.left, min, root)
&& helper(root.right, root, max);
}
```
一、在BST 中查找一个数是否存在
[700.二叉搜索树中的搜索](https://leetcode-cn.com/problems/search-in-a-binary-search-tree)
```js
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} val
* @return {TreeNode}
*/
var searchBST = function(root, target) {
if (root == null) return null;
if (root.val === target)
return root;
if (root.val < target)
return searchBST(root.right, target);
if (root.val > target)
return searchBST(root.left, target);
// root 该做的事做完了,顺带把框架也完成了,妙
};
```
二、在 BST 中插入一个数
[701.二叉搜索树中的插入操作](https://leetcode-cn.com/problems/insert-into-a-binary-search-tree)
```js
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} val
* @return {TreeNode}
*/
var insertIntoBST = function(root, val) {
// 找到空位置插入新节点
if (root == null) return new TreeNode(val);
// if (root.val == val)
// BST 中一般不会插入已存在元素
if (root.val < val)
root.right = insertIntoBST(root.right, val);
if (root.val > val)
root.left = insertIntoBST(root.left, val);
return root;
};
```
三、在 BST 中删除一个数
[450.删除二叉搜索树中的节点](https://leetcode-cn.com/problems/delete-node-in-a-bst)
```js
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} key
* @return {TreeNode}
*/
var deleteNode = function(root, key) {
if (!root) return null
// if key > root.val, delete node in root.right. Otherwise delete node in root.left.
if (key > root.val) {
const rightNode = deleteNode(root.right, key)
root.right = rightNode
return root
} else if (key < root.val) {
const leftNode = deleteNode(root.left, key)
root.left = leftNode
return root
} else {
// now root.val === key
if (!root.left) {
return root.right
}
if (!root.right) {
return root.left
}
// 将删除元素的左下方元素替代删除元素;
// 将左下方元素的右侧最下方子元素衔接删除元素的右下方子元素;
const rightChild = root.right
let newRightChild = root.left
while (newRightChild.right) {
newRightChild = newRightChild.right
}
newRightChild.right = rightChild
return root.left
}
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo)
* [动态规划解题套路框架](https://labuladong.gitbook.io/algo)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo/)
* [动态规划解题套路框架](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -173,16 +173,23 @@ vector<int> nextGreaterElements(vector<int>& nums) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[496.下一个更大元素I](https://leetcode-cn.com/problems/next-greater-element-i)
[503.下一个更大元素II](https://leetcode-cn.com/problems/next-greater-element-ii)
[739.每日温度](https://leetcode-cn.com/problems/daily-temperatures/)
### java
[ZakAnun](https://github.com/ZakAnun) 提供代码
@ -239,6 +246,7 @@ public int[] nextGreaterElement(int[] nums1, int[] nums2) {
}
```
[ZakAnun](https://github.com/ZakAnun) 提供代码
```java
// 739. Daily Temperatures
@ -258,4 +266,335 @@ class Solution {
return ans;
}
}
```
```
[JiangangZhao](https://github.com/JiangangZhao)提供【503.下一个更大元素II】【java】
```java
class Solution {
public int[] nextGreaterElements(int[] nums) {
//数组长度
int n = nums.length;
//逻辑拼接,数组长度翻倍
int len = n*2 - 1;
//存储结果数组
int[] res = new int[n];
//存放索引,不是元素
LinkedList<Integer> s = new LinkedList<>();
//从前往后遍历
for (int i = 0; i < len; ++i) {
//索引要取模
int val = nums[i % n];
//当前元素比栈顶元素大,即是栈顶元素的下一个更大的元素
while (!s.isEmpty() && val > nums[s.peek()]) {
res[s.pop()] = val;
}
//i<n时入栈
if (i < n) {
s.push(i);
}
}
//栈中剩余的索引不存在下一个更大的元素,赋值-1
while (!s.isEmpty()) {
res[s.pop()] = -1;
}
return res;
}
}
```
### javascript
单调栈模板
[496.下一个更大元素I](https://leetcode-cn.com/problems/next-greater-element-i)
这里需要用一个map记录nums2中各项的下一个更大值为何注意读题。
- nums1和nums2中所有整数 互不相同
- nums1 中的所有整数同样出现在 nums2 中
如果还是用数组的话num1中元素在nums2中的位置并不好找所以这里使用map来维护。
其它核心思想和上文中的大抵相同。值得注意的是,入栈顺序可以有正着入栈和倒着入栈,顺序不同,维护的动作也不同,详见下文。
正着入栈如下。
```js
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var nextGreaterElement = function (nums1, nums2) {
let len1 = nums1.length;
let len2 = nums2.length;
// base case
if (len1 < 1 || len2 < 1 || len1 > len2) {
return [];
}
let res = new Array(len1); // 存放答案的数组
let stack = [];
let map = {};
// 启动条件
stack.push(nums2[0]);
// 右边数字入栈
for (let j = 1; j < len2; j++) {
let currNum = nums2[j];
// 单调栈栈顶元素和当前数组元素作比较
// 找到下一个更大元素
while (stack.length !== 0 && currNum > stack[stack.length - 1]) {
map[stack.pop()] = currNum;
}
stack.push(currNum);
}
// 栈不为空 这些元素都是找不到下一个更大值的
while (stack.length !== 0) {
map[stack.pop()] = -1;
}
for (let i = 0; i < len1; i++) {
res[i] = map[nums1[i]];
}
return res;
};
```
接下来是倒着入栈,就是上文中提到的排队思路。
抽象思路nums2看做排队找后面第一个比自己高的高个子。
```js
var nextGreaterElement = function(nums1, nums2) {
// 把此类问题比作排队看后面第一个比自己高的
// 从后面开始遍历往前面看,就能很好的避免不知道后面什么情况了
let stack = []
let res = []
let map = new Map()
for(let i = nums2.length - 1; i >= 0; i--){
// 矮个子起开,要你也没用,反正看不见你
while(stack.length && nums2[i] >= stack[stack.length - 1]){
stack.pop()
}
//有比我个子高的吗?有就是你了,没有就是-1
map.set(nums2[i], stack.length ? stack[stack.length - 1] : -1)
stack.push(nums2[i])
}
nums1.forEach(item => {
res.push(map.get(item))
})
return res;
};
```
解决了这道题,后面的题就很容易理解了。在这道题的基础上,让单调栈中存放的元素是下标而不是值,因为有的题目需要根据下标计算,这样泛化性更好。
正着入栈,存储下标。
```js
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var nextGreaterElement = function (nums1, nums2) {
let len1 = nums1.length;
let len2 = nums2.length;
// base case
if (len1 < 1 || len2 < 1 || len1 > len2) {
return [];
}
let map = new Map()
let res = []; // 存放结果
let stack = []
for (let i = 0; i < len2; i++) {
//栈顶元素存在,并且当前的元素大于栈顶
while (stack.length && nums2[i] > nums2[stack[stack.length - 1]]) {
// 关键步骤1
let index = stack.pop();
map.set(nums2[index], nums2[i])
}
// 关键步骤2 下标入栈
stack.push(i)
}
//栈内还有元素,说明后面没有比自己小的了
while (stack.length) {
let index = stack.pop();
map.set(nums2[index], -1)
}
// 最后导出结果
nums1.forEach(item => {
res.push(map.get(item))
})
return res
};
```
倒着入栈,存储下标。
```js
// 存储的是下标
var nextGreaterElement = function (nums1, nums2) {
// 把此类问题比作排队看后面第一个比自己高的
// 从后面开始遍历往前面看,就能很好的避免不知道后面什么情况了
let stack = []
let res = []
let map = new Map()
for (let i = nums2.length - 1; i >= 0; i--) {
// 矮个子起开,要你也没用,反正看不见你
while (stack.length && nums2[i] >= nums2[stack[stack.length - 1]]) {
stack.pop()
}
//有比我个子高的吗?有就是你了,没有就是-1
map.set(nums2[i], stack.length ? nums2[stack[stack.length - 1]] : -1)
// 关键步骤:存储的是下标
stack.push(i)
}
nums1.forEach(item => {
res.push(map.get(item))
})
return res;
};
```
进一步而谈其实map也可以转化成使用index对应index不过这种情况的题比较少见了解即可不必抛开框架深入追究细节。
```js
nums1:[4,1,2]
nums2:[1,3,4,2]
直接num1的value对nums2的value 前提value唯一
{
4: -1
1: 3
2: -1
}
num1的index对nums2的index
这里也可以用数组来做,自由发挥吧,这里只提供一些思路。
{
0-1
11
2-1
}
```
**[503.下一个更大元素II](https://leetcode-cn.com/problems/next-greater-element-ii)**
因为是环形数组,所以最后一个数的下一个最大的数不是-1而是要把再数组从头开始遍历到末尾得出这个数可以把数组扩大两倍解决。
- 把数组扩大两倍逆序遍历依次放入栈中,栈中的栈顶元素代表下一个迭代的数的后面第一个最大的数;
- 当前数比栈顶元素大时,出栈;
- 此时栈有值时,栈顶元素即为当前数的下一个最大的数,把它存入结果数组对应的下标中;
- 把当前数入栈
这里用的和上文一样,还是反着入栈,相信读者可以自己悟出正着入栈怎么写了吧。
```js
/**
* @param {number[]} nums
* @return {number[]}
*/
var nextGreaterElements = function (nums) {
let n = nums.length;
let res = [];
let stack = [];
// 假装这个数组长度翻倍了
for (let i = 2 * n - 1; i >= 0; i--) {
// 索引要求模,其他的和模板一样
while (stack.length && stack[stack.length - 1] <= nums[i % n])
stack.pop();
res[i % n] = stack.length ? stack[stack.length - 1] : -1;
stack.push(nums[i % n]);
}
return res;
};
```
**[739.每日温度](https://leetcode-cn.com/problems/daily-temperatures/)**
很简单就是第一个next greater的变形而已存储的是索引。
倒着入栈。
```js
/**
* @param {number[]} T
* @return {number[]}
*/
var dailyTemperatures = function (T) {
let res = new Array(T.length).fill(0);
// 这里放元素索引,而不是元素
let stack = [];
/* 单调栈模板 */
for (let i = T.length - 1; i >= 0; i--) {
while (stack.length !== 0 && T[stack[stack.length - 1]] <= T[i]) {
stack.pop();
}
// 得到索引间距
res[i] = stack.length === 0 ? 0 : (stack[stack.length - 1] - i);
// 将索引入栈,而不是元素
stack.push(i);
}
return res;
};
```
正着入栈es6写法。
```js
const dailyTemperatures = (T) => {
const res = new Array(T.length).fill(0);
for (let i = 0; i < T.length; i++) {
for (let j = i + 1; j < T.length; j++) {
if (T[j] > T[i]) {
res[i] = j - i;
break;
}
}
}
return res;
}
```
部分做题规律如下,仅供做题套路参考,实际可以自由发挥。
当前项向左找第一个比自己大的位置:从左向右维护一个单调递减栈
当前项向左找第一个比自己小的位置:从左向右维护一个单调递增栈
当前项向右找第一个比自己大的位置:从右向左维护一个单调递减栈
当前项向右找第一个比自己小的位置:从右向左维护一个单调递增栈

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [几个反直觉的概率问题](https://labuladong.gitbook.io/algo)
* [Git/SQL/正则表达式的在线练习平台](https://labuladong.gitbook.io/algo)
* [几个反直觉的概率问题](https://labuladong.gitbook.io/algo/)
* [Git/SQL/正则表达式的在线练习平台](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -202,17 +202,18 @@ vector<int> maxSlidingWindow(vector<int>& nums, int k) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### python3
[239.滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum)
### python
由[SCUHZS](ttps://github.com/brucecat)提供
@ -312,3 +313,62 @@ class Solution {
}
}
```
### javascript
这里用js实现的思路和上文中一样都是自己实现一个单调队列注意这里的单调队列和优先级队列大小堆不是同一个概念。
```js
let MonotonicQueue = function () {
// 模拟一个deque双端队列
this.data = [];
// 在队尾添加元素 n
this.push = function (n) {
while (this.data.length !== 0 && this.data[this.data.length - 1] < n)
this.data.pop();
this.data.push(n);
}
// 返回当前队列中的最大值
this.max = function () {
return this.data[0];
};
// 队头元素如果是 n删除它
this.pop = function (n) {
if (this.data.length !== 0 && this.data[0] === n)
this.data.shift();
};
}
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function (nums, k) {
let window = new MonotonicQueue();
let res = []
for (let i = 0; i < nums.length; i++) {
if (i < k - 1) { //先把窗口的前 k - 1 填满
window.push(nums[i]);
} else {
// 窗口开始向前滑动
window.push(nums[i]);
res.push(window.max());
window.pop(nums[i - k + 1]);
// nums[i - k + 1] 就是窗口最后的元素
}
}
return res;
};
```

View File

@ -12,8 +12,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo)
* [一行代码就能解决的算法题](https://labuladong.gitbook.io/algo)
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo/)
* [一行代码就能解决的算法题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -26,7 +26,7 @@
**-----------**
我们最终要实现的计算器功能如下:
1、输入一个字符串可以包含`+ - * /`、数字、括号以及空格,你的算法返回运算结果。
2、要符合运算法则括号的优先级最高先乘除后加减。
@ -298,7 +298,7 @@ def calculate(s: str) -> int:
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -306,4 +306,108 @@ def calculate(s: str) -> int:
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[224.基本计算器](https://leetcode-cn.com/problems/basic-calculator)
[227.基本计算器II](https://leetcode-cn.com/problems/basic-calculator-ii)
[772.基本计算器III](https://leetcode-cn.com/problems/basic-calculator-iii)
### javascript
说实话用js的话你完全可以妙用eval来秒杀这类题。
```js
var calculate = function(s) {
var Fn = Function;
return new Fn('return ' + s)()
};
```
不过相信看该作者文章的读者都是奔着学习框架思想来的下面用js来还原上文代码。
[224.基本计算器](https://leetcode-cn.com/problems/basic-calculator)
```js
/**
* @param {string} s
* @return {number}
*/
var calculate = function(s) {
var q = [], n = '', f = '+', a = typeof s === 'string' ? Array.from(s).reverse() : s
while(a.length || n) {
var p = a.pop()
if (p === ' ') continue
if (p === '(') {
n = calculate(a)
} else if (/\D/.test(p)) {
switch (f) {
case '+':
q.push(n)
break;
case '-':
q.push(-n)
break;
case '*':
q.push(q.pop() * n)
break;
case '/':
q.push(q.pop() / n | 0)
}
if (p === ')') break
f = p, n = ''
} else n += p
}
return q.reduce((p, v) => p + (v | 0), 0)
};
```
[227.基本计算器II](https://leetcode-cn.com/problems/basic-calculator-ii)
- 从左向右遍历字符串,符号标识`f`,初始`+`
- `空格`,忽视。`数字`,当字符串拼接。`非数字`,根据`f`运算
- `+`和`-`入栈,`*`和`/`和栈`第一位`运算,结果入栈
- 返回栈的累加和
```js
/**
* @param {string} s
* @return {number}
*/
var calculate = function(s) {
var q = [], n = '', f = '+'
for (var i = 0; i < s.length || n; i++) {
if (s[i] === ' ') continue
if (/\D/.test(s[i])) {
switch (f) {
case '+':
q.push(n)
break;
case '-':
q.push(-n)
break;
case '*':
q.push(q.pop() * n)
break;
case '/':
q.push(q.pop() / n | 0)
}
f = s[i], n = ''
} else n += s[i]
}
return q.reduce((p, v) => p + (v | 0), 0)
};
```
[772.基本计算器III](https://leetcode-cn.com/problems/basic-calculator-iii)
要会员才能做这道题,打扰了。
```js
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [面试官你说对MySQL事务很熟那我问你10个问题](https://labuladong.gitbook.io/algo)
* [一行代码就能解决的算法题](https://labuladong.gitbook.io/algo)
* [面试官你说对MySQL事务很熟那我问你10个问题](https://labuladong.gitbook.io/algo/)
* [一行代码就能解决的算法题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -294,16 +294,19 @@ PS本文前两张图片和 GIF 是我第一次尝试用平板的绘图软件
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[355.设计推特](https://leetcode-cn.com/problems/design-twitter)
### c++
[happy-yuxuan](https://github.com/happy-yuxuan) 提供 C++ 代码:
```c++
@ -419,4 +422,178 @@ public:
userMap[followerId]->unfollow(followeeId);
}
};
```
```
### javascript
由于js没有大小堆相关的内置库所以可以考虑使用其它的方式类似实现链表+优先级队列的功能。
followMap用户关注列表 用 Set 数据类型不需要去处理重复数据,取消关注(从列表删除)也会更方便;
postMap用户推文列表
latestPostId推文的自增id用于后续获取推文列表时排序
- 在 postTweet 函数中,将新增的 推文 { tweetId, postTime } 放到列表的最前面,并确保 latestPostId 自增;
- 在 follow 函数中,先检查 followMap 是否已存在 followerId 数据,若已存在,直接 add(followeeId), 若不存在,新增 new Set([followeeId])
- 在 unfollow 函数中,直接检查是否存在 followMap[followerId] 列表若存在直接delete(followeeId)
- 在 getNewsFeed 函数中,因为要取用户和用户关注的用户的最新 10 条推文所以只需要把这些用户的前10条推文取出来再根据postTime去排序然后取最新10条推文。
```js
/**
* Initialize your data structure here.
*/
var Twitter = function() {
this.followMap = {}
this.postMap = new Map()
this.latestPostId = 0
}
/**
* Compose a new tweet.
* @param {number} userId
* @param {number} tweetId
* @return {void}
*/
Twitter.prototype.postTweet = function(userId, tweetId) {
const postTime = this.latestPostId++
let tweeList = [{ tweetId, postTime }]
if (this.postMap.has(userId)) {
tweeList = tweeList.concat(this.postMap.get(userId))
}
this.postMap.set(userId, tweeList)
}
/**
* Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
* @param {number} userId
* @return {number[]}
*/
Twitter.prototype.getNewsFeed = function(userId) {
const followeeIdList = this.followMap[userId] ? [...this.followMap[userId]] : []
const tweeList = []
const userIds = [...new Set(followeeIdList.concat([userId]))]
userIds.forEach(uid => {
if (this.postMap.has(uid)) {
tweeList.push(...this.postMap.get(uid).slice(0, 10))
}
})
tweeList.sort((a, b) => b.postTime - a.postTime)
return tweeList.slice(0, 10).map(item => item.tweetId)
}
/**
* Follower follows a followee. If the operation is invalid, it should be a no-op.
* @param {number} followerId
* @param {number} followeeId
* @return {void}
*/
Twitter.prototype.follow = function(followerId, followeeId) {
if (this.followMap[followerId]) {
this.followMap[followerId].add(followeeId)
} else {
this.followMap[followerId] = new Set([followeeId])
}
}
/**
* Follower unfollows a followee. If the operation is invalid, it should be a no-op.
* @param {number} followerId
* @param {number} followeeId
* @return {void}
*/
Twitter.prototype.unfollow = function(followerId, followeeId) {
if (this.followMap[followerId]) {
this.followMap[followerId].delete(followeeId)
}
}
```
### python
```python
import heapq
class Tweet:
def __init__(self, tid: int, time: int) -> None:
self.tid = tid
self.time = time
self.next = None
class User:
def __init__(self, uid: int):
self.uid = uid
self.following = set()
self.tweetlst = None
self.follow(uid)
def post(self, tid: int, time: int) -> None:
tweet = Tweet(tid, time)
tweet.next = self.tweetlst
self.tweetlst = tweet
def follow(self, uid: int) -> None:
if uid not in self.following:
self.following.add(uid)
def unfollow(self, uid: int) -> None:
# one cannot unfollow itself
if uid != self.uid and uid in self.following:
self.following.remove(uid)
class Twitter:
def __init__(self):
"""
Initialize your data structure here.
"""
self.id2user = {}
self.timestamp = 0
def postTweet(self, userId: int, tweetId: int) -> None:
"""
Compose a new tweet.
"""
if userId not in self.id2user: self.id2user[userId] = User(userId)
user = self.id2user[userId]
user.post(tweetId, self.timestamp)
self.timestamp += 1
def getNewsFeed(self, userId: int) -> List[int]:
"""
Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
"""
heap, user = [], self.id2user.get(userId)
if user:
for uid in user.following:
tweets = self.id2user[uid].tweetlst
while tweets:
heap.append(tweets)
tweets = tweets.next
return [twt.tid for twt in heapq.nlargest(10, heap, key= lambda twt: twt.time)]
else: return []
def follow(self, followerId: int, followeeId: int) -> None:
"""
Follower follows a followee. If the operation is invalid, it should be a no-op.
"""
if followerId not in self.id2user:
self.id2user[followerId] = User(followerId)
if followeeId not in self.id2user:
self.id2user[followeeId] = User(followeeId)
self.id2user[followerId].follow(followeeId)
def unfollow(self, followerId: int, followeeId: int) -> None:
"""
Follower unfollows a followee. If the operation is invalid, it should be a no-op.
"""
if followerId in self.id2user:
self.id2user[followerId].unfollow(followeeId)
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo)
* [回溯算法最佳实践:括号生成](https://labuladong.gitbook.io/algo)
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo/)
* [回溯算法最佳实践:括号生成](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -210,48 +210,194 @@ ListNode reverseBetween(ListNode head, int m, int n) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[92.反转链表II](https://leetcode-cn.com/problems/reverse-linked-list-ii/)
### c++
[shilei](https://github.com/ShileiGuo) 提供C++解法代码:
思想:
1.head表示需要反转的头节点pre表示需要反转头节点的前驱节点
2.对于从m到n的节点反转需要反转n-m次将head的next节点移动到需要反转链表部分的首部需要反转链表部分剩余节点依旧保持相对顺序即可
3.示例 当m=2, n=5时
第一次反转1(pre) 2(head) 3(next) 4 5 反转为 1 3 2 4 5
第二次反转1(pre) 3 2(head) 4(next) 5 反转为 1 4 3 2 5
第三次发转1(pre) 4 3 2(head) 5(next) 反转为 1 5 4 3 2
```CPP
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
//初始化哨兵节点
ListNode* dummy=new ListNode(-1);
//初始化待反转区间的前一个节点
ListNode* pre=dummy;
//哨兵节点下一个节点指向head头节点
dummy->next=head;
//获取待反转节点的前一个节点
for(int i=0;i<m-1;i++)
pre=pre->next;
//获取待反转节点的第一个节点
head=pre->next;
//迭代反转n-m次将head的next节点移动到需要反转链表部分的首部
for(int i=m;i<n;i++){
ListNode* t=head->next;
head->next=t->next;
t->next=pre->next;
pre->next=t;
}
//返回哨兵节点
return dummy->next;
}
};
```
### python
[DiamondI](https://github.com/DiamondI) 提供python3版本代码
思路递归。时间复杂度为O(n)由于递归调用需要借助栈的空间因此空间复杂度亦为O(n)。
```python3
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def __init__(self):
self.__successor = None
def __init__(self):
self.__successor = None
def __reverseN(self, head: ListNode, n: int) -> ListNode:
if n == 1:
# 记录第 n + 1 个节点
self.__successor = head.next;
return head;
# 以 head.next 为起点,需要反转前 n - 1 个节点
last = self.__reverseN(head.next, n - 1);
head.next.next = head;
# 让反转之后的 head 节点和后面的节点连起来
head.next = self.__successor;
return last;
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
# base case
if m == 1:
return self.__reverseN(head, n);
# 前进到反转的起点触发 base case
head.next = self.reverseBetween(head.next, m - 1, n - 1);
if n == 1:
# 记录第 n + 1 个节点
self.__successor = head.next;
return head;
# 以 head.next 为起点,需要反转前 n - 1 个节点
last = self.__reverseN(head.next, n - 1);
head.next.next = head;
# 让反转之后的 head 节点和后面的节点连起来
head.next = self.__successor;
return last;
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
# base case
if m == 1:
return self.__reverseN(head, n);
# 前进到反转的起点触发 base case
head.next = self.reverseBetween(head.next, m - 1, n - 1);
return head;
```
### javascript
**递归反转整个链表**
[206. 反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)
```js
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
if (head == null || head.next == null) {
return head;
}
const last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
};
```
**反转链表前 N 个节点**
这题貌似在leetcode上没找到就不贴链接了。
```js
let successor = null; // 后驱节点
let reverseListN = function(head, n) {
if (n === 1) {
// 记录第 n + 1 个节点
successor = head.next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
let last = reverseListN(head.next, n - 1);
head.next.next = head;
// 让反转之后的 head 节点和后面的节点连起来
head.next = successor;
return last;
};
```
**反转链表的一部分**
现在解决我们最开始提出的问题,给一个索引区间 `[m,n]`(索引从 1 开始),仅仅反转区间中的链表元素。
```js
let successor = null; // 后驱节点
let reverseListN = function(head, n) {
if (n === 1) {
// 记录第 n + 1 个节点
successor = head.next;
return head;
}
// 以 head.next 为起点,需要反转前 n - 1 个节点
let last = reverseListN(head.next, n - 1);
head.next.next = head;
// 让反转之后的 head 节点和后面的节点连起来
head.next = successor;
return last;
};
/**
* @param {ListNode} head
* @param {number} m
* @param {number} n
* @return {ListNode}
*/
let reverseBetween = function(head, m, n) {
// base case
if (m === 1) {
return reverseListN(head, n);
}
// 前进到反转的起点触发 base case
head.next = reverseBetween(head.next, m - 1, n - 1);
return head;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo)
* [高性能短链设计](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo/)
* [高性能短链设计](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -222,7 +222,7 @@ public boolean empty() {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -230,4 +230,147 @@ public boolean empty() {
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[232.用栈实现队列](https://leetcode-cn.com/problems/implement-queue-using-stacks)
[225.用队列实现栈](https://leetcode-cn.com/problems/implement-stack-using-queues)
### javascript
[232.用栈实现队列](https://leetcode-cn.com/problems/implement-queue-using-stacks)
```js
/**
* Initialize your data structure here.
*/
var MyQueue = function () {
this.inStack = [];
this.outStack = [];
};
/**
* Push element x to the back of queue.
* @param {number} x
* @return {void}
*/
MyQueue.prototype.push = function (x) {
this.inStack.push(x);
};
/**
* 检查outStack
*/
MyQueue.prototype.checkOutStack = function () {
// // 把 inStack 元素压入 outStack
if (!this.outStack.length) {
while (this.inStack.length) {
this.outStack.push(this.inStack.pop());
}
}
};
/**
* Removes the element from in front of queue and returns that element.
* @return {number}
*/
MyQueue.prototype.pop = function () {
this.checkOutStack();
return this.outStack.pop();
};
/**
* Get the front element.
* @return {number}
*/
MyQueue.prototype.peek = function () {
this.checkOutStack();
return this.outStack[this.outStack.length - 1];
};
/**
* Returns whether the queue is empty.
* @return {boolean}
*/
MyQueue.prototype.empty = function () {
return (!this.inStack.length && !this.outStack.length);
};
```
[225.用队列实现栈](https://leetcode-cn.com/problems/implement-stack-using-queues)
```js
// --------------------------------
/**
* Initialize your data structure here.
*/
var MyStack = function () {
this.q = []
this.top_elem = 0;
};
/**
* Push element x onto stack.
* @param {number} x
* @return {void}
*/
MyStack.prototype.push = function (x) {
// x 是队列的队尾,是栈的栈顶
this.q.push(x);
this.top_elem = x;
};
/**
* Removes the element on top of the stack and returns that element.
* @return {number}
*/
MyStack.prototype.pop = function () {
let size = this.q.length;
// 留下队尾 2 个元素
while (size > 2) {
// peek()都是用来返回队列的头元素, 相当于[0]
// poll()都是用来从队列头部删除一个元素 相当于js的shift()
this.q.push(this.q.shift());
size--;
}
// 记录新的队尾元素
this.top_elem = this.q[0];
this.q.push(this.q.shift());
// 删除之前的队尾元素
return this.q.shift();
};
/**
* Get the top element.
* @return {number}
*/
MyStack.prototype.top = function () {
return this.top_elem;
};
/**
* Returns whether the stack is empty.
* @return {boolean}
*/
MyStack.prototype.empty = function () {
return !this.q.length;
};
/**
* Your MyStack object will be instantiated and called as such:
* var obj = new MyStack()
* obj.push(x)
* var param_2 = obj.pop()
* var param_3 = obj.top()
* var param_4 = obj.empty()
*/
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何高效进行模幂运算](https://labuladong.gitbook.io/algo)
* [经典动态规划0-1 背包问题](https://labuladong.gitbook.io/algo)
* [如何高效进行模幂运算](https://labuladong.gitbook.io/algo/)
* [经典动态规划0-1 背包问题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -126,7 +126,7 @@ image[x][y] = newColor;
完全 OK这也是处理「图」的一种常用手段。不过对于此题不用开数组我们有一种更好的方法那就是回溯算法。
前文 [回溯算法框架套路](https://labuladong.gitbook.io/algo)讲过,这里不再赘述,直接套回溯算法框架:
前文 [回溯算法框架套路](https://labuladong.gitbook.io/algo/)讲过,这里不再赘述,直接套回溯算法框架:
```java
void fill(int[][] image, int x, int y,
@ -231,7 +231,7 @@ int fill(int[][] image, int x, int y,
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -239,4 +239,89 @@ int fill(int[][] image, int x, int y,
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[733.图像渲染](https://leetcode-cn.com/problems/flood-fill)
### javascript
**BFS**
从起始像素向上下左右扩散,只要相邻的点存在并和起始点颜色相同,就染成新的颜色,并继续扩散。
借助一个队列去遍历节点,考察出列的节点,带出满足条件的节点入列。已经染成新色的节点不会入列,避免重复访问节点。
时间复杂度O(n)。空间复杂度O(n)
```js
const floodFill = (image, sr, sc, newColor) => {
const m = image.length;
const n = image[0].length;
const oldColor = image[sr][sc];
if (oldColor == newColor) return image;
const fill = (i, j) => {
if (i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oldColor) {
return;
}
image[i][j] = newColor;
fill(i - 1, j);
fill(i + 1, j);
fill(i, j - 1);
fill(i, j + 1);
};
fill(sr, sc);
return image;
};
```
**DFS**
思路与上文相同。
```js
/**
* @param {number[][]} image
* @param {number} sr
* @param {number} sc
* @param {number} newColor
* @return {number[][]}
*/
let floodFill = function (image, sr, sc, newColor) {
let origColor = image[sr][sc];
fill(image, sr, sc, origColor, newColor);
return image;
}
let fill = function (image, x, y, origColor, newColor) {
// 出界:超出边界索引
if (!inArea(image, x, y)) return;
// 碰壁:遇到其他颜色,超出 origColor 区域
if (image[x][y] !== origColor) return;
// 已探索过的 origColor 区域
if (image[x][y] === -1) return;
// 打标记 避免重复
image[x][y] = -1;
fill(image, x, y + 1, origColor, newColor);
fill(image, x, y - 1, origColor, newColor);
fill(image, x - 1, y, origColor, newColor);
fill(image, x + 1, y, origColor, newColor);
// un choose将标记替换为 newColor
image[x][y] = newColor;
}
let inArea = function (image, x, y) {
return x >= 0 && x < image.length
&& y >= 0 && y < image[0].length;
}
```

View File

@ -11,14 +11,14 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第一期)](https://labuladong.gitbook.io/algo)
* [二分查找详解](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第一期)](https://labuladong.gitbook.io/algo/)
* [二分查找详解](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
[130.被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions)
[990.等式方程的可满足性](https://leetcode-cn.com/problems/surrounded-regions)
[990.等式方程的可满足性](https://leetcode-cn.com/problems/satisfiability-of-equality-equations)
[261.以图判树](https://leetcode-cn.com/problems/graph-valid-tree/)
@ -244,16 +244,25 @@ boolean equationsPossible(String[] equations) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[130.被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions)
[990.等式方程的可满足性](https://leetcode-cn.com/problems/satisfiability-of-equality-equations)
[261.以图判树](https://leetcode-cn.com/problems/graph-valid-tree/)
### java
第261题的Java代码提供[LEODPEN](https://github.com/LEODPEN)
```java
@ -327,3 +336,237 @@ class Solution {
}
}
```
### javascript
[130.被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions)
```js
class UF {
// 记录连通分量
count;
// 节点 x 的根节点是 parent[x]
parent;
// 记录树的“重量”
size;
constructor(n) {
// 一开始互不连通
this.count = n;
// 父节点指针初始指向自己
this.parent = new Array(n);
this.size = new Array(n);
for (let i = 0; i < n; i++) {
this.parent[i] = i;
this.size[i] = 1;
}
}
/* 返回某个节点 x 的根节点 */
find(x) {
// 根节点的 parent[x] == x
while (this.parent[x] !== x) {
// 进行路径压缩
this.parent[x] = this.parent[this.parent[x]];
x = this.parent[x];
}
return x;
}
/* 将 p 和 q 连接 */
union(p, q) {
// 如果某两个节点被连通,则让其中的(任意)
// 一个节点的根节点接到另一个节点的根节点上
let rootP = this.find(p);
let rootQ = this.find(q);
if (rootP === rootQ) return;
// 小树接到大树下面,较平衡
if (this.size[rootP] > this.size[rootQ]) {
this.parent[rootQ] = rootP;
this.size[rootP] += this.size[rootQ];
} else {
this.parent[rootP] = rootQ;
this.size[rootQ] += this.size[rootP];
}
this.count--; // 两个分量合二为一
}
/* 判断 p 和 q 是否连通 */
connected(p, q) {
let rootP = this.find(p);
let rootQ = this.find(q);
return rootP === rootQ;
};
/* 返回图中有多少个连通分量 */
getCount() {
return this.count;
};
}
/**
* @param {[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
let solve = function (board) {
if (board.length === 0) return;
let m = board.length;
let n = board[0].length;
// 给 dummy 留一个额外位置
let uf = new UF(m * n + 1);
let dummy = m * n;
// 将首列和末列的 O 与 dummy 连通
for (let i = 0; i < m; i++) {
if (board[i][0] === 'O')
uf.union(i * n, dummy);
if (board[i][n - 1] === 'O')
uf.union(i * n + n - 1, dummy);
}
// 将首行和末行的 O 与 dummy 连通
for (let j = 0; j < n; j++) {
if (board[0][j] === 'O')
uf.union(j, dummy);
if (board[m - 1][j] === 'O')
uf.union(n * (m - 1) + j, dummy);
}
// 方向数组 d 是上下左右搜索的常用手法
let d = [[1, 0], [0, 1], [0, -1], [-1, 0]];
for (let i = 1; i < m - 1; i++)
for (let j = 1; j < n - 1; j++)
if (board[i][j] === 'O')
// 将此 O 与上下左右的 O 连通
for (let k = 0; k < 4; k++) {
let x = i + d[k][0];
let y = j + d[k][1];
if (board[x][y] === 'O')
uf.union(x * n + y, i * n + j);
}
// 所有不和 dummy 连通的 O都要被替换
for (let i = 1; i < m - 1; i++)
for (let j = 1; j < n - 1; j++)
if (!uf.connected(dummy, i * n + j))
board[i][j] = 'X';
}
```
[990.等式方程的可满足性](https://leetcode-cn.com/problems/surrounded-regions)
需要注意的点主要为js字符与ASCII码互转。
在java、c这些语言中字符串直接相减得到的是ASCII码的差值结果为整数而js中`"a" - "b"`的结果为NaN所以需要使用`charCodeAt(index)`方法来获取字符的ASCII码index不填时默认结果为第一个字符的ASCII码。
```js
class UF {
// 记录连通分量
count;
// 节点 x 的根节点是 parent[x]
parent;
// 记录树的“重量”
size;
constructor(n) {
// 一开始互不连通
this.count = n;
// 父节点指针初始指向自己
this.parent = new Array(n);
this.size = new Array(n);
for (let i = 0; i < n; i++) {
this.parent[i] = i;
this.size[i] = 1;
}
}
/* 返回某个节点 x 的根节点 */
find(x) {
// 根节点的 parent[x] == x
while (this.parent[x] !== x) {
// 进行路径压缩
this.parent[x] = this.parent[this.parent[x]];
x = this.parent[x];
}
return x;
}
/* 将 p 和 q 连接 */
union(p, q) {
// 如果某两个节点被连通,则让其中的(任意)
// 一个节点的根节点接到另一个节点的根节点上
let rootP = this.find(p);
let rootQ = this.find(q);
if (rootP === rootQ) return;
// 小树接到大树下面,较平衡
if (this.size[rootP] > this.size[rootQ]) {
this.parent[rootQ] = rootP;
this.size[rootP] += this.size[rootQ];
} else {
this.parent[rootP] = rootQ;
this.size[rootQ] += this.size[rootP];
}
this.count--; // 两个分量合二为一
}
/* 判断 p 和 q 是否连通 */
connected(p, q) {
let rootP = this.find(p);
let rootQ = this.find(q);
return rootP === rootQ;
};
/* 返回图中有多少个连通分量 */
getCount() {
return this.count;
};
}
/**
* @param {string[]} equations
* @return {boolean}
*/
let equationsPossible = function (equations) {
// 26 个英文字母
let uf = new UF(26);
// 先让相等的字母形成连通分量
for (let eq of equations) {
if (eq[1] === '=') {
let x = eq[0];
let y = eq[3];
// 'a'.charCodeAt() 为 97
uf.union(x.charCodeAt(0) - 97, y.charCodeAt(0) - 97);
}
}
// 检查不等关系是否打破相等关系的连通性
for (let eq of equations) {
if (eq[1] === '!') {
let x = eq[0];
let y = eq[3];
// 如果相等关系成立,就是逻辑冲突
if (uf.connected(x.charCodeAt(0) - 97, y.charCodeAt(0) - 97))
return false;
}
}
return true;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [一文秒杀四道原地修改数组的算法题](https://labuladong.gitbook.io/algo)
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo)
* [一文秒杀四道原地修改数组的算法题](https://labuladong.gitbook.io/algo/)
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo/)
**-----------**
@ -309,7 +309,7 @@ Union-Find 算法的复杂度可以这样分析:构造函数初始化数据结
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -317,4 +317,138 @@ Union-Find 算法的复杂度可以这样分析:构造函数初始化数据结
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
### javascript
```js
class UF {
// 记录连通分量
count;
// 节点 x 的根节点是 parent[x]
parent;
constructor(n) {
// 一开始互不连通
this.count = n;
// 父节点指针初始指向自己
this.parent = new Array(n);
for (let i = 0; i < n; i++)
this.parent[i] = i;
}
/* 返回某个节点 x 的根节点 */
find(x) {
// 根节点的 parent[x] == x
while (this.parent[x] !== x)
x = this.parent[x];
return x;
}
/* 将 p 和 q 连接 */
union(p, q) {
// 如果某两个节点被连通,则让其中的(任意)
// 一个节点的根节点接到另一个节点的根节点上
let rootP = this.find(p);
let rootQ = this.find(q);
if (rootP === rootQ) return;
// 将两棵树合并为一棵
parent[rootP] = rootQ;
// parent[rootQ] = rootP 也一样
count--; // 两个分量合二为一
}
/* 判断 p 和 q 是否连通 */
connected(p, q) {
let rootP = this.find(p);
let rootQ = this.find(q);
return rootP === rootQ;
};
/* 返回图中有多少个连通分量 */
getCount() {
return this.count;
};
}
```
引入size属性更好地平衡森林。
```js
class UF {
// 记录连通分量
count;
// 节点 x 的根节点是 parent[x]
parent;
// 记录树的“重量”
size;
constructor(n) {
// 一开始互不连通
this.count = n;
// 父节点指针初始指向自己
this.parent = new Array(n);
this.size = new Array(n);
for (let i = 0; i < n; i++) {
this.parent[i] = i;
this.size[i] = 1;
}
}
/* 返回某个节点 x 的根节点 */
find(x) {
// 根节点的 parent[x] == x
while (this.parent[x] !== x) {
// 进行路径压缩
this.parent[x] = this.parent[this.parent[x]];
x = this.parent[x];
}
return x;
}
/* 将 p 和 q 连接 */
union(p, q) {
// 如果某两个节点被连通,则让其中的(任意)
// 一个节点的根节点接到另一个节点的根节点上
let rootP = this.find(p);
let rootQ = this.find(q);
if (rootP === rootQ) return;
// 小树接到大树下面,较平衡
if (this.size[rootP] > this.size[rootQ]) {
this.parent[rootQ] = rootP;
this.size[rootP] += this.size[rootQ];
} else {
this.parent[rootP] = rootQ;
this.size[rootQ] += this.size[rootP];
}
this.count--; // 两个分量合二为一
}
/* 判断 p 和 q 是否连通 */
connected(p, q) {
let rootP = this.find(p);
let rootQ = this.find(q);
return rootP === rootQ;
};
/* 返回图中有多少个连通分量 */
getCount() {
return this.count;
};
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [我写了首诗,让你闭着眼睛也能写对二分搜索](https://labuladong.gitbook.io/algo)
* [经典动态规划:完全背包问题](https://labuladong.gitbook.io/algo)
* [我写了首诗,让你闭着眼睛也能写对二分搜索](https://labuladong.gitbook.io/algo/)
* [经典动态规划:完全背包问题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -177,12 +177,126 @@ int[] twoSum(int[] nums, int target) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[1.两数之和](https://leetcode-cn.com/problems/two-sum)
[170.两数之和 III - 数据结构设计](https://leetcode-cn.com/problems/two-sum-iii-data-structure-design)
### python
由[JodyZ0203](https://github.com/JodyZ0203)提供 1. Two Sums Python3 解法代码:
只用一个哈希表
```python
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
# 提前构造一个哈希表
hashTable = {}
# 寻找两个目标数值
for i, n in enumerate(nums):
other_num = target - n
# 如果存在这个余数 other_num
if other_num in hashTable.keys():
# 查看是否存在哈希表里,如果存在的话就返回数组
return [i, hashTable[other_num]]
# 如果不存在的话继续处理剩余的数
hashTable[n] = i
```
### javascript
[1.两数之和](https://leetcode-cn.com/problems/two-sum)
穷举
```js
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
for (let i = 0; i < nums.length; i++)
for (let j = i + 1; j < nums.length; j++)
if (nums[j] === target - nums[i])
return [i, j];
// 不存在这么两个数
return [-1, -1];
};
```
备忘录
```js
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
let n = nums.length;
let index = new Map();
// 构造一个哈希表:元素映射到相应的索引
for (let i = 0; i < n; i++)
index.set(nums[i], i);
for (let i = 0; i < n; i++) {
let other = target - nums[i];
// 如果 other 存在且不是 nums[i] 本身
if (index.has(other) && index.get(other) !== i)
return [i, index.get(other)];
}
// 不存在这么两个数
return [-1, -1];
};
```
[170.两数之和 III - 数据结构设计](https://leetcode-cn.com/problems/two-sum-iii-data-structure-design)
哈希集合优化。
```js
class TwoSum {
constructor() {
this.sum = new Set();
this.nums = [];
}
// 向数据结构中添加一个数 number
add(number) {
// 记录所有可能组成的和
for (let n of this.nums) {
this.sum.push(n + number)
}
this.nums.add(number);
}
// 寻找当前数据结构中是否存在两个数的和为 value
find(value) {
return this.sum.has(value);
}
}
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [递归反转链表的一部分](https://labuladong.gitbook.io/algo)
* [25 张图解:键入网址后,到网页显示,其间发生了什么](https://labuladong.gitbook.io/algo)
* [递归反转链表的一部分](https://labuladong.gitbook.io/algo/)
* [25 张图解:键入网址后,到网页显示,其间发生了什么](https://labuladong.gitbook.io/algo/)
**-----------**
@ -93,7 +93,7 @@
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo/)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -500,12 +500,318 @@ int right_bound(int[] nums, int target) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[704.二分查找](https://leetcode-cn.com/problems/binary-search)
[34.在排序数组中查找元素的第一个和最后一个位置](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)
### python
[MarineJoker](https://github.com/MarineJoker) 提供 Python3 代码
```python
# 基本二分搜索
def binarySearch(nums, target):
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
# 直接返回
return mid
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
# 直接返回
return -1
# 寻找左侧边界的二分搜索,开区间写法
def left_bound(nums, target):
left, right = 0, len(nums)
if right == 0:
return -1
while left < right:
mid = (left + right) // 2
if nums[mid] == target:
# 锁定左侧边界
right = mid
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid
# 检查left越界情况
if left >= len(nums) or nums[left] != target:
return -1
return left
# 寻找右侧边界的二分搜索,开区间写法
def right_bound(nums, target):
left, right = 0, len(nums)
if right == 0:
return -1
while left < right:
mid = (left + right) // 2
if nums[mid] == target:
# 锁定右侧边界
left = mid + 1
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid
# 检查越界情况
if left == 0 or nums[left - 1] != target:
return -1
return left - 1
# 寻找左侧边界的二分搜索,闭区间写法
def left_bound(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
# 锁定左侧边界
right = mid - 1
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
# 检查left越界情况
if left >= len(nums) or nums[left] != target:
return -1
return left
# 寻找右侧边界的二分搜索,闭区间写法
def right_bound(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
# 锁定右侧边界
left = mid + 1
elif nums[mid] < target:
left = mid + 1
elif nums[mid] > target:
right = mid - 1
# 检查right越界情况
if right < 0 or nums[right] != target:
return -1
return right
```
### javascript
寻找左、右侧边界的二分搜索,两端闭合
```js
let binary_search = function (nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] === target) {
// 直接返回
return mid;
}
}
// 直接返回
return -1;
}
let left_bound = function (nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
// 搜索区间为 [left, right]
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] === target) {
// 收缩右侧边界
right = mid - 1;
}
}
// 检查出界情况
if (left >= nums.length || nums[left] !== target)
return -1;
return left;
}
// 两端都闭
let right_bound = function (nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 区间变到[mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 区间变到[left, mid-1]
right = mid - 1;
} else if (nums[mid] === target) {
// 别返回,锁定右侧边界
// 区间变到[mid+1, right]
left = mid + 1;
}
}
// 最后要检查 right 越界的情况
if (right < 0 || nums[right] !== target)
return -1;
return right;
}
```
[704.二分查找](https://leetcode-cn.com/problems/binary-search)
```js
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] === target) {
// 直接返回
return mid;
}
}
// 直接返回
return -1;
};
```
[34.在排序数组中查找元素的第一个和最后一个位置](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)
按照本文的思路,可以很容易就写出答案,但会超时。
```js
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
let left = left_bound(nums, target);
let right = right_bound(nums, target)
return [left]
};
let left_bound = function (nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
// 搜索区间为 [left, right]
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] === target) {
// 收缩右侧边界
right = mid - 1;
}
}
// 检查出界情况
if (left >= nums.length || nums[left] !== target)
return -1;
return left;
}
// 两端都闭
let right_bound = function (nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 区间变到[mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 区间变到[left, mid-1]
right = mid - 1;
} else if (nums[mid] === target) {
// 别返回,锁定右侧边界
// 区间变到[mid+1, right]
left = mid + 1;
}
}
// 最后要检查 right 越界的情况
if (right < 0 || nums[right] !== target)
return -1;
return right;
}
```
现通过lower变量来确定当前的二分法是向左找还是向右找。
```js
const binarySearch = (nums, target, lower) => {
let left = 0, right = nums.length - 1, ans = nums.length;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] > target || (lower && nums[mid] >= target)) {
right = mid - 1;
ans = mid;
} else {
left = mid + 1;
}
}
return ans;
}
var searchRange = function(nums, target) {
let ans = [-1, -1];
const leftIdx = binarySearch(nums, target, true);
const rightIdx = binarySearch(nums, target, false) - 1;
if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] === target && nums[rightIdx] === target) {
ans = [leftIdx, rightIdx];
}
return ans;
};
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [讲两道常考的阶乘算法题](https://labuladong.gitbook.io/algo)
* [状态压缩:对动态规划进行降维打击](https://labuladong.gitbook.io/algo)
* [讲两道常考的阶乘算法题](https://labuladong.gitbook.io/algo/)
* [状态压缩:对动态规划进行降维打击](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -22,7 +22,7 @@
很多算法问题都需要排序技巧,其难点不在于排序本身,而是需要巧妙地排序进行预处理,将算法问题进行转换,为之后的操作打下基础。
信封嵌套问题就需要先按特定的规则排序,之后就转换为一个 [最长递增子序列问题](https://labuladong.gitbook.io/algo),可以用前文 [二分查找详解](https://labuladong.gitbook.io/algo) 的技巧来解决了。
信封嵌套问题就需要先按特定的规则排序,之后就转换为一个 [最长递增子序列问题](https://labuladong.gitbook.io/algo/),可以用前文 [二分查找详解](https://labuladong.gitbook.io/algo/) 的技巧来解决了。
### 一、题目概述
@ -127,7 +127,7 @@ public int lengthOfLIS(int[] nums) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -135,4 +135,123 @@ public int lengthOfLIS(int[] nums) {
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[354.俄罗斯套娃信封问题](https://leetcode-cn.com/problems/russian-doll-envelopes)
### javascript
[300. 最长递增子序列](https://leetcode-cn.com/problems/longest-increasing-subsequence/)
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
```js
/**
* @param {number[]} nums
* @return {number}
*/
let lengthOfLIS = function(nums) {
let top = new Array(nums.length);
// 牌堆数初始化为 0
let piles = 0;
for (let i = 0; i < nums.length; i++) {
// 要处理的扑克牌
let poker = nums[i];
/***** 搜索左侧边界的二分查找 *****/
let left = 0, right = piles;
while (left < right) {
let mid = (left + right) / 2;
if (top[mid] > poker) {
right = mid;
} else if (top[mid] < poker) {
left = mid + 1;
} else {
right = mid;
}
}
/*********************************/
// 没找到合适的牌堆,新建一堆
if (left === piles) piles++;
// 把这张牌放到牌堆顶
top[left] = poker;
}
// 牌堆数就是 LIS 长度
return piles;
};
```
[354.俄罗斯套娃信封问题](https://leetcode-cn.com/problems/russian-doll-envelopes)
```js
/**
* @param {number[]} nums
* @return {number}
*/
let lengthOfLIS = function(nums) {
let top = new Array(nums.length);
// 牌堆数初始化为 0
let piles = 0;
for (let i = 0; i < nums.length; i++) {
// 要处理的扑克牌
let poker = nums[i];
/***** 搜索左侧边界的二分查找 *****/
let left = 0, right = piles;
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (top[mid] > poker) {
right = mid;
} else if (top[mid] < poker) {
left = mid + 1;
} else {
right = mid;
}
}
/*********************************/
// 没找到合适的牌堆,新建一堆
if (left === piles) piles++;
// 把这张牌放到牌堆顶
top[left] = poker;
}
// 牌堆数就是 LIS 长度
return piles;
};
/**
* @param {number[][]} envelopes
* @return {number}
*/
var maxEnvelopes = function (envelopes) {
let n = envelopes.length;
// 按宽度升序排列,如果宽度一样,则按高度降序排列
envelopes.sort((a, b) => {
return a[0] === b[0] ? b[1] - a[1] : a[0] - b[0];
})
// 对高度数组寻找 LIS
let height = new Array(n);
for (let i = 0; i < n; i++)
height[i] = envelopes[i][1];
return lengthOfLIS(height);
};
```

View File

@ -11,12 +11,12 @@
![](../pictures/souyisou.png)
相关推荐:
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo)
* [我写了首诗,让你闭着眼睛也能写对二分搜索](https://labuladong.gitbook.io/algo)
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo/)
* [我写了首诗,让你闭着眼睛也能写对二分搜索](https://labuladong.gitbook.io/algo/)
**-----------**
上篇文章 [洗牌算法详解](https://labuladong.gitbook.io/algo) 讲到了验证概率算法的蒙特卡罗方法,今天聊点轻松的内容:几个和概率相关的有趣问题。
上篇文章 [洗牌算法详解](https://labuladong.gitbook.io/algo/) 讲到了验证概率算法的蒙特卡罗方法,今天聊点轻松的内容:几个和概率相关的有趣问题。
计算概率有下面两个最简单的原则:
@ -133,7 +133,7 @@
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何去除有序数组的重复元素](https://labuladong.gitbook.io/algo)
* [区间调度之区间合并问题](https://labuladong.gitbook.io/algo)
* [如何去除有序数组的重复元素](https://labuladong.gitbook.io/algo/)
* [区间调度之区间合并问题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -151,7 +151,7 @@ for (int i = 1; i < count.length; i++)
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -159,4 +159,80 @@ for (int i = 1; i < count.length; i++)
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[560.和为K的子数组](https://leetcode-cn.com/problems/subarray-sum-equals-k)
### javascript
[560.和为K的子数组](https://leetcode-cn.com/problems/subarray-sum-equals-k)
```js
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
let subarraySum = function (nums, k) {
let n = nums.length;
// 构造前缀和
let sum = new Array(n + 1);
sum[0] = 0;
for (let i = 0; i < n; i++) {
sum[i + 1] = sum[i] + nums[i];
}
let ans = 0;
// 穷举所有子数组
for (let i = 1; i <= n; i++)
for (let j = 0; j < i; j++)
// sum of nums[j..i-1]
if (sum[i] - sum[j] === k)
ans++;
return ans;
}
```
优化一下。
```js
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
let subarraySum = function (nums, k) {
let n = nums.length;
// map前缀和 -> 该前缀和出现的次数
let preSum = new Map();
// base case
preSum.set(0, 1);
let ans = 0, sum0_i = 0;
for (let i = 0; i < n; i++) {
sum0_i += nums[i];
// 这是我们想找的前缀和 nums[0..j]
let sum0_j = sum0_i - k;
// 如果前面有这个前缀和,则直接更新答案
if (preSum.has(sum0_j))
ans += preSum.get(sum0_j);
// 把前缀和 nums[0..i] 加入并记录出现次数
if (preSum.has(sum0_i)) {
preSum.set(sum0_i,
preSum.get(sum0_i) + 1);
} else {
preSum.set(sum0_i, 1);
}
}
return ans;
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [经典动态规划:编辑距离](https://labuladong.gitbook.io/algo)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo)
* [经典动态规划:编辑距离](https://labuladong.gitbook.io/algo/)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -125,12 +125,84 @@ def intervalIntersection(A, B):
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[986.区间列表的交集](https://leetcode-cn.com/problems/interval-list-intersections)
### java
[KiraZh](https://github.com/KiraZh)提供第986题Java代码
```java
class Solution {
public int[][] intervalIntersection(int[][] A, int[][] B) {
List<int[]> res = new ArrayList<>();
int a = 0, b = 0;
while(a < A.length && b < B.length) {
// 确定左边界,两个区间左边界的最大值
int left = Math.max(A[a][0], B[b][0]);
// 确定右边界,两个区间右边界的最小值
int right = Math.min(A[a][1], B[b][1]);
// 左边界小于右边界则加入结果集
if (left <= right)
res.add(new int[] {left, right});
// 右边界更大的保持不动,另一个指针移动,继续比较
if(A[a][1] < B[b][1]) a++;
else b++;
}
// 将结果转为数组
return res.toArray(new int[0][]);
}
}
```
### javascript
[986.区间列表的交集](https://leetcode-cn.com/problems/interval-list-intersections)
```js
/**
* @param {number[][]} firstList
* @param {number[][]} secondList
* @return {number[][]}
*/
var intervalIntersection = function (firstList, secondList) {
let i, j;
i = j = 0;
let res = [];
while (i < firstList.length && j < secondList.length) {
let a1 = firstList[i][0];
let a2 = firstList[i][1];
let b1 = secondList[j][0];
let b2 = secondList[j][1];
// 两个区间存在交集
if (b2 >= a1 && a2 >= b1) {
// 计算出交集,加入 res
res.push([Math.max(a1, b1), Math.min(a2, b2)])
}
// 指针前进
if (b2 < a2) {
j += 1;
} else {
i += 1
}
}
return res;
};
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [Git原理之最近公共祖先](https://labuladong.gitbook.io/algo)
* [洗牌算法](https://labuladong.gitbook.io/algo)
* [Git原理之最近公共祖先](https://labuladong.gitbook.io/algo/)
* [洗牌算法](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -83,7 +83,7 @@ def merge(intervals):
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -92,7 +92,13 @@ def merge(intervals):
</p>
======其他语言代码======
~~~java
[56.合并区间](https://leetcode-cn.com/problems/merge-intervals)
### java
```java
class Solution {
/**
* 1. 先对区间集合进行排序(根据开始位置)
@ -151,5 +157,83 @@ class Solution {
}
}
~~~
```
### c++
[Kian](https://github.com/KianKw/) 提供第 56 题 C++ 代码
```c++
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// len 为 intervals 的长度
int len = intervals.size();
if (len < 1)
return {};
// 按区间的 start 升序排列
sort(intervals.begin(), intervals.end());
// 初始化 res 数组
vector<vector<int>> res;
res.push_back(intervals[0]);
for (int i = 1; i < len; i++) {
vector<int> curr = intervals[i];
// res.back() 为 res 中最后一个元素的索引
if (curr[0] <= res.back()[1]) {
// 找到最大的 end
res.back()[1] = max(res.back()[1], curr[1]);
} else {
// 处理下一个待合并区间
res.push_back(curr);
}
}
return res;
}
};
```
### javascript
[56. 合并区间](https://leetcode-cn.com/problems/merge-intervals/)
```js
/**
* @param {number[][]} intervals
* @return {number[][]}
*/
var merge = function (intervals) {
if (intervals.length < 1) {
return []
}
// 按区间的 start 升序排列
intervals.sort((a, b) => {
return a[0] - b[0]
})
const res = []
res.push(intervals[0].concat())
for (let i = 1; i < intervals.length; i++) {
let curr = intervals[i]
// res 中最后一个元素的引用
let last = res[res.length - 1]
if (curr[0] <= last[1]) {
// 找到最大的 end
last[1] = Math.max(last[1], curr[1])
} else {
// 处理下一个待合并区间
res.push(curr.concat())
}
}
return res
}
```

View File

@ -11,14 +11,14 @@
![](../pictures/souyisou.png)
相关推荐:
* [一文秒杀四道原地修改数组的算法题](https://labuladong.gitbook.io/algo)
* [Linux的进程、线程、文件描述符是什么](https://labuladong.gitbook.io/algo)
* [一文秒杀四道原地修改数组的算法题](https://labuladong.gitbook.io/algo/)
* [Linux的进程、线程、文件描述符是什么](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
[141.环形链表](https://leetcode-cn.com/problems/linked-list-cycle)
[141.环形链表II](https://leetcode-cn.com/problems/linked-list-cycle-ii)
[142.环形链表II](https://leetcode-cn.com/problems/linked-list-cycle-ii)
[167.两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted)
@ -221,22 +221,134 @@ void reverse(int[] nums) {
这也许是双指针技巧的最高境界了,如果掌握了此算法,可以解决一大类子字符串匹配的问题,不过「滑动窗口」稍微比上述的这些算法复杂些。
幸运的是,这类算法是有框架模板的,而且[这篇文章](https://labuladong.gitbook.io/algo)就讲解了「滑动窗口」算法模板,帮大家秒杀几道 LeetCode 子串匹配的问题。
幸运的是,这类算法是有框架模板的,而且[这篇文章](https://labuladong.gitbook.io/algo/)就讲解了「滑动窗口」算法模板,帮大家秒杀几道 LeetCode 子串匹配的问题。
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[141.环形链表](https://leetcode-cn.com/problems/linked-list-cycle)
[142.环形链表II](https://leetcode-cn.com/problems/linked-list-cycle-ii)
[167.两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted)
### java
[zhengpj95](https://github.com/zhengpj95) 提供 Java 代码
```java
public class Solution {
public boolean hasCycle(ListNode head) {
//链表为空或只有一个结点,无环
if (head == null || head.next == null) return false;
ListNode fast = head, slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
// 快慢指针相遇则表示有环
if (fast == slow) return true;
}
//fast指针到末尾无环
return false;
}
}
```
### c++
[deardeer7](https://github.com/DearDeer7/) 提供 C++ 代码
```cpp
class Solution {
public:
bool hasCycle(ListNode *head) {
// 链表为空或有一个元素,则无环
if(!head || !head->next) return false;
ListNode* slow = head;
ListNode* fast = head->next;
while(fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
// 快慢指针相遇,则有环
if(fast == slow) return true;
}
return false; // 链表走完,快慢指针未相遇,则无环
}
};
```
[zhengpj95](https://github.com/zhengpj95) 提供 【2、已知链表中含有环返回这个环的起始位置】 C++ 代码
> 其实快慢指针问题,也就是著名的 *[Floyd's cycle detection algorithm](https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_Tortoise_and_Hare)* 问题。
```c++
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
// 如果链表为空或者第一个结点的指针为空,则无环
if (!head || !head->next) {
return NULL;
}
// 快慢指针找相遇点
ListNode *fast = head, *slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) {
break;
}
}
// 如果没有相遇点,表示没有环,直接返回即可
// 此时,快慢指针要么指向同一个结点,要么快指针指向空(偶数个结点)或者倒数第一个结点(奇数个结点)
if (fast != slow) {
return NULL;
}
//让慢指针回到第一个结点,然后快慢指针重新同步前进,两指针相遇时就是环的起点位置
slow = head;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
```
### python
[MarineJoker](https://github.com/MarineJoker) 提供 167.两数之和 II - 输入有序数组 Python 代码
```python
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left, right = 0, len(numbers) - 1
while left < right:
two_sum = numbers[left] + numbers[right]
if two_sum > target:
right -= 1 # 使得two_sum变小
elif two_sum < target:
left += 1 # 使得two_sum变大
elif two_sum == target:
return [left+1, right+1] # 由于索引由1开始
return [-1, -1]
```
[ryandeng32](https://github.com/ryandeng32/) 提供 Python 代码
```python
class Solution:
@ -257,3 +369,230 @@ class Solution:
# 退出循环,则链表有结束,不可能为环形
return False
```
### javascript
#### 一、快慢指针的常见算法
**1、判定链表中是否含有环**
[141.环形链表](https://leetcode-cn.com/problems/linked-list-cycle)
```js
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) return true;
}
return false;
};
```
**2、已知链表中含有环返回这个环的起始位置**
[142.环形链表II](https://leetcode-cn.com/problems/linked-list-cycle-ii)
```js
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
let fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break;
}
// 上面的代码类似 hasCycle 函数
if (fast == null || fast.next == null) {
// fast 遇到空指针说明没有环
return null;
}
slow = head;
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
};
```
**3、寻找链表的中点**
[876. 链表的中间结点](https://leetcode-cn.com/problems/middle-of-the-linked-list/)
```js
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var middleNode = function(head) {
let fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
// slow 就在中间位置
return slow;
};
```
**4、寻找链表的倒数第 k 个元素**
[剑指 Offer 22. 链表中倒数第k个节点](https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/)
```js
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var getKthFromEnd = function(head, k) {
let slow, fast;
slow = fast = head;
while (k-- > 0)
fast = fast.next;
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;
};
```
#### 二、左右指针的常用算法
**1、二分查找**
[704. 二分查找](https://leetcode-cn.com/problems/binary-search/)
```js
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
if (nums.length === 0) return -1;
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] === target) {
// 直接返回
return mid;
}
}
// 直接返回
return -1;
};
```
**2、两数之和**
[167.两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted)
```js
/**
* @param {number[]} numbers
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let left = 0, right = nums.length - 1;
while (left < right) {
let sum = nums[left] + nums[right];
if (sum === target) {
// 题目要求的索引是从 1 开始的
return [left + 1, right + 1];
} else if (sum < target) {
left++; // 让 sum 大一点
} else if (sum > target) {
right--; // 让 sum 小一点
}
}
return [-1, -1];
};
```
**3、反转数组**
[344. 反转字符串](https://leetcode-cn.com/problems/reverse-string/)
```js
/**
* @param {character[]} s
* @return {void} Do not return anything, modify s in-place instead.
*/
var reverseString = function(s) {
let left = 0;
let right = s.length - 1;
while (left < right) {
// swap(s[left], s[right])
let temp = s[left];
s[left] = s[right];
s[right] = temp;
left++; right--;
}
};
```
**4、滑动窗口算法**
详见[这篇文章](https://labuladong.gitbook.io/algo)。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [我写了首诗,把滑动窗口算法算法变成了默写题](https://labuladong.gitbook.io/algo)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo)
* [我写了首诗,把滑动窗口算法算法变成了默写题](https://labuladong.gitbook.io/algo/)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -301,12 +301,242 @@ def backtrack(...):
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[46.全排列](https://leetcode-cn.com/problems/permutations)
[51.N皇后](https://leetcode-cn.com/problems/n-queens)
### java
[46.全排列](https://leetcode-cn.com/problems/permutations)
```java
List<List<Integer>> res = new LinkedList<>();
/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
// 记录「路径」
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, track);
return res;
}
// 路径:记录在 track 中
// 选择列表nums 中不存在于 track 的那些元素
// 结束条件nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track) {
// 触发结束条件
if (track.size() == nums.length) {
res.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length; i++) {
// 排除不合法的选择
if (track.contains(nums[i]))
continue;
// 做选择
track.add(nums[i]);
// 进入下一层决策树
backtrack(nums, track);
// 取消选择
track.removeLast();
}
}
```
由[kepler-zc](https://github.com/kepler-zc) 提供 51.N皇后 Java 解法代码:
```java
class solution {
private List<List<String>> res = new ArrayList<>();
// 输入棋盘边长 n返回所有合法的放置
public List<List<String>> solveNQueens(int n){
// '.'表示空,'Q'表示皇后,初始化空棋盘
char[][] chess = new char[n][n];
for (int i = 0; i < n; i++) {
Arrays.fill(chess[i], '.');
}
// 已经不能放置皇后的列(被占用)
boolean[] usedCol = new boolean[n];
// 已经不能放置皇后的正斜线 , 按右上角到左下角排列 , 共2n-1条
boolean[] usedSlash = new boolean[2*n-1];
// 已经不能放置皇后的反斜线 , 按左上角到右下角排列 , 共2n-1条
boolean[] usedBackSlash = new boolean[2*n-1];
backtrack(chess, 0, usedCol, usedSlash, usedBackSlash);
return res;
}
// 路径chess 中小于 row 的那些行都已经成功放置了皇后
// 选择列表:第 row 行的所有列都是放置皇后的选择
// 结束条件row 超过 棋盘最后一行
private void backtrack(char[][] chess, int row, boolean[] usedCol, boolean[] usedSlash, boolean[] usedBackSlash) {
// 触发结束条件
if (row == chess.length){
res.add(construct(chess));
return;
}
for (int col = 0; col < chess.length; col++) {
// 对合法选择进行回溯操作
// 分别检查列,左上方, 右上方是否存在皇后冲突,都不冲突集为合法选择。
if (!usedCol[col] && !usedSlash[row-col+usedCol.length-1] && !usedBackSlash[col+row]){
// 做选择
chess[row][col] = 'Q';
usedCol[col] = true;
// 对坐标为[i,j]的点对应的正斜线和反斜线的索引分别为row-col+n-1; col+row
usedSlash[row-col+chess.length-1] = true;
usedBackSlash[col+row] = true;
// 进入下一行
backtrack(chess, row+1, usedCol,usedSlash, usedBackSlash);
// 撤销选择
chess[row][col] = '.';
usedCol[col] = false;
usedSlash[row-col+chess.length-1] = false;
usedBackSlash[col+row] = false;
}
}
}
private List<String> construct(char[][] chess) {
// 数组转List
List<String> path = new ArrayList<>();
for (char[] chars : chess) {
path.add(new String(chars));
}
return path;
}
}
```
### javascript
[46.全排列](https://leetcode-cn.com/problems/permutations)
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums){
// 1. 设置结果集
const result = [];
// 2. 回溯
const recursion = (path, set) => {
// 2.1 设置回溯终止条件
if (path.length === nums.length) {
// 2.1.1 推入结果集
result.push(path.concat());
// 2.1.2 终止递归
return;
}
// 2.2 遍历数组
for (let i = 0; i < nums.length; i++) {
// 2.2.1 必须是不存在 set 中的坐标
if (!set.has(i)) {
// 2.2.2 本地递归条件(用完记得删除)
path.push(nums[i]);
set.add(i);
// 2.2.3 进一步递归
recursion(path, set);
// 2.2.4 回溯:撤回 2.2.2 的操作
path.pop();
set.delete(i);
}
}
};
recursion([], new Set());
// 3. 返回结果
return result;
};
```
[ 51.N皇后](https://leetcode-cn.com/problems/n-queens)
检查斜线的时候,可以用两个坐标点的|(y1-y2)| == |(x1 - x2)|,如果true就是共斜线
```js
/**
* @param {number} n
* @return {string[][]}
*/
var solveNQueens = function (n) {
const board = new Array(n);
// '.' 表示空,'Q' 表示皇后,初始化空棋盘。
for (let i = 0; i < n; i++) { // 棋盘的初始化
board[i] = new Array(n).fill('.');
}
const res = []
// 验证是否是有效的位置
const isValid = (row, col) => {
for (let i = 0; i < row; i++) { // 之前的行
for (let j = 0; j < n; j++) { // 所有的列
if (board[i][j] === 'Q' && // 发现了皇后,并且和自己同列/对角线
(j === col || i + j === row + col || i - j === row - col)) {
return false; // 不是合法的选择
}
}
}
return true;
};
// 路径board 中小于 row 的那些行都已经成功放置了皇后
// 选择列表:第 row 行的所有列都是放置皇后的选择
// 结束条件row 超过 board 的最后一行
const backtrack = (row) => {
// 触发结束条件
if (row === n) {
const stringsBoard = board.slice(); // 拷贝一份board
for (let i = 0; i < n; i++) {
stringsBoard[i] = stringsBoard[i].join(''); // 将每一行拼成字符串
}
res.push(stringsBoard); // 推入res数组
return;
}
for (let col = 0; col < n; col++) {
// 排除不合法选择
if (!isValid(row, col))
continue;
// 做选择
board[row][col] = 'Q';
// 进入下一行决策
backtrack(row + 1);
// 撤销选择
board[row][col] = '.';
}
}
backtrack(0);
return res;
};
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [关于 Linux shell 你必须知道的](https://labuladong.gitbook.io/algo)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo)
* [关于 Linux shell 你必须知道的](https://labuladong.gitbook.io/algo/)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -21,7 +21,7 @@
**-----------**
对于比较小的数字,做运算可以直接使用编程语言提供的运算符,但是如果相乘的两个因数非常大,语言提供的数据类型可能就会溢出。一种替代方案就是,运算数以字符串的形式输入,然后模仿我们小学学习的乘法算术过程计算出结果,并且也用字符串表示。
![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/title.png)
需要注意的是,`num1` 和 `num2` 可以非常长,所以不可以把他们直接转成整型然后运算,唯一的思路就是模仿我们手算乘法。
@ -93,16 +93,19 @@ string multiply(string num1, string num2) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[43.字符串相乘](https://leetcode-cn.com/problems/multiply-strings)
### python
[fengshuu](https://github.com/fengshuu) 提供 Python 解法代码:
@ -177,4 +180,38 @@ public String multiply(String num1, String num2) {
}
return ans.toString();
}
```
```
### javascript
```js
/**
* @param {string} num1
* @param {string} num2
* @return {string}
*/
const multiply = (num1, num2) => {
const m = num1.length;
const n = num2.length;
const pos = new Array(m + n).fill(0);
for (let i = m - 1; i >= 0; i--) {
const n1 = +num1[i];
for (let j = n - 1; j >= 0; j--) {
const n2 = +num2[j];
const multi = n1 * n2;
const sum = pos[i + j + 1] + multi;
pos[i + j + 1] = sum % 10;
pos[i + j] += sum / 10 | 0;
}
}
while (pos[0] === 0) {
pos.shift();
}
return pos.length ? pos.join('') : '0';
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo)
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo/)
* [经典动态规划:高楼扔鸡蛋(进阶)](https://labuladong.gitbook.io/algo/)
**-----------**
@ -130,7 +130,7 @@ N 叉树的遍历又可以扩展为图的遍历,因为图就是好几 N 叉棵
首先要明确的是,**数据结构是工具,算法是通过合适的工具解决特定问题的方法**。也就是说,学习算法之前,最起码得了解那些常用的数据结构,了解它们的特性和缺陷。
那么该如何在 LeetCode 刷题呢?之前的文章[算法学习之路](https://labuladong.gitbook.io/algo)写过一些,什么按标签刷,坚持下去云云。现在距那篇文章已经过去将近一年了,我不说那些不痛不痒的话,直接说具体的建议:
那么该如何在 LeetCode 刷题呢?之前的文章[算法学习之路](https://labuladong.gitbook.io/algo/)写过一些,什么按标签刷,坚持下去云云。现在距那篇文章已经过去将近一年了,我不说那些不痛不痒的话,直接说具体的建议:
**先刷二叉树,先刷二叉树,先刷二叉树**
@ -214,7 +214,7 @@ void traverse(TreeNode* node) {
再举例吧,说几道我们之前文章写过的问题。
[动态规划详解](https://labuladong.gitbook.io/algo)说过凑零钱问题,暴力解法就是遍历一棵 N 叉树:
[动态规划详解](https://labuladong.gitbook.io/algo/)说过凑零钱问题,暴力解法就是遍历一棵 N 叉树:
![](../pictures/动态规划详解进阶/5.jpg)
@ -247,7 +247,7 @@ def dp(n):
其实很多动态规划问题就是在遍历一棵树,你如果对树的遍历操作烂熟于心,起码知道怎么把思路转化成代码,也知道如何提取别人解法的核心思路。
再看看回溯算法,前文[回溯算法详解](https://labuladong.gitbook.io/algo)干脆直接说了,回溯算法就是个 N 叉树的前后序遍历问题,没有例外。
再看看回溯算法,前文[回溯算法详解](https://labuladong.gitbook.io/algo/)干脆直接说了,回溯算法就是个 N 叉树的前后序遍历问题,没有例外。
比如 N 皇后问题吧,主要代码如下:
@ -286,7 +286,7 @@ N 叉树的遍历框架,找出来了把~你说,树这种结构重不重要
但是,你要是心中没有框架,那么你根本无法解题,给了你答案,你也不会发现这就是个树的遍历问题。
这种思维是很重要的,[动态规划详解](https://labuladong.gitbook.io/algo)中总结的找状态转移方程的几步流程,有时候按照流程写出解法,说实话我自己都不知道为啥是对的,反正它就是对了。。。
这种思维是很重要的,[动态规划详解](https://labuladong.gitbook.io/algo/)中总结的找状态转移方程的几步流程,有时候按照流程写出解法,说实话我自己都不知道为啥是对的,反正它就是对了。。。
**这就是框架的力量,能够保证你在快睡着的时候,依然能写出正确的程序;就算你啥都不会,都能比别人高一个级别。**
@ -301,7 +301,7 @@ N 叉树的遍历框架,找出来了把~你说,树这种结构重不重要
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [40张图解TCP三次握手和四次挥手面试题](https://labuladong.gitbook.io/algo)
* [动态规划答疑篇](https://labuladong.gitbook.io/algo)
* [40张图解TCP三次握手和四次挥手面试题](https://labuladong.gitbook.io/algo/)
* [动态规划答疑篇](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -164,12 +164,89 @@ http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[191.位1的个数](https://leetcode-cn.com/problems/number-of-1-bits)
[231.2的幂](https://leetcode-cn.com/problems/power-of-two/)
### python
由[JodyZ0203](https://github.com/JodyZ0203)提供 191. 位1的个数 Python3 解法代码:
```Python
class Solution:
def hammingWeight(self, n: int) -> int:
# 先定义一个count用来存1的出现数量
count = 0
# 只要二进制串不等于0之前我们用一个循环边消除1和计1的出现数量
while n!=0:
# 用labuladong在文章中所提到的 n&(n-1) 技巧来消除最后一个1
n = n & (n-1)
count+=1
# 当二进制串全消除完之后返回1出现的总数量
return count
```
### javascript
[191.位1的个数](https://leetcode-cn.com/problems/number-of-1-bits)
```js
let hammingWeight = function(n) {
let res = 0;
while (n !== 0) {
n = n & (n - 1);
res++;
}
return res;
}
```
[231.2的幂](https://leetcode-cn.com/problems/power-of-two/)
```js
/**
* @param {number} n
* @return {boolean}
*/
let isPowerOfTwo = function(n) {
if (n <= 0) return false;
return (n & (n - 1)) === 0;
}
```
[136. 只出现一次的数字](https://leetcode-cn.com/problems/single-number/)
查找只出现一次的元素
```js
/**
* @param {number[]} nums
* @return {number}
*/
let singleNumber = function(nums) {
let res = 0;
for (let n of nums) {
res ^= n;
}
return res;
}
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [二叉搜索树操作集锦](https://labuladong.gitbook.io/algo)
* [动态规划解题套路框架](https://labuladong.gitbook.io/algo)
* [二叉搜索树操作集锦](https://labuladong.gitbook.io/algo/)
* [动态规划解题套路框架](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -203,12 +203,105 @@ for (int feq : count)
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[384.打乱数组](https://leetcode-cn.com/problems/shuffle-an-array)
### javascript
```js
// 得到一个在闭区间 [min, max] 内的随机整数
const randInt = function (minNum, maxNum) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
};
// 第一种写法
let shuffle = function (arr) {
const swap = (i, j) => {
let t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
let n = arr.length;
/******** 区别只有这两行 ********/
for (let i = 0; i < n; i++) {
// 从 i 到最后随机选一个元素
let rand = randInt(i, n - 1);
/*************************/
// 交换 i rand 上的元素
swap(i, rand);
}
}
// 第二种写法
let shuffle = function (arr) {
const swap = (i, j) => {
let t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
let n = arr.length;
/******** 区别只有这两行 ********/
for (let i = 0; i < n - 1; i++) {
let rand = randInt(i, n - 1);
/*************************/
// 交换 i rand 上的元素
swap(i, rand);
}
}
// 第三种写法
let shuffle = function (arr) {
const swap = (i, j) => {
let t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
let n = arr.length;
/******** 区别只有这两行 ********/
for (let i = n - 1; i >= 0; i--) {
let rand = randInt(0, i);
/*************************/
// 交换 i rand 上的元素
swap(i, rand);
}
}
// 第四种写法
let shuffle = function (arr) {
const swap = (i, j) => {
let t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
let n = arr.length;
/******** 区别只有这两行 ********/
for (let i = n - 1; i > 0; i--) {
let rand = randInt(0, i);
/*************************/
// 交换 i rand 上的元素
swap(i, rand);
}
}
```
======其他语言代码======

View File

@ -12,8 +12,8 @@
**最新消息:关注公众号参与活动,有机会成为 [70k star 算法仓库](https://github.com/labuladong/fucking-algorithm) 的贡献者,机不可失时不再来**
相关推荐:
* [东哥吃葡萄时竟然吃出一道算法题!](https://labuladong.gitbook.io/algo)
* [如何寻找缺失的元素](https://labuladong.gitbook.io/algo)
* [东哥吃葡萄时竟然吃出一道算法题!](https://labuladong.gitbook.io/algo/)
* [如何寻找缺失的元素](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -363,7 +363,7 @@ class Solution:
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -373,10 +373,20 @@ class Solution:
======其他语言代码======
[76.最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring)
[567.字符串的排列](https://leetcode-cn.com/problems/permutation-in-string)
[438.找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string)
[3.无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters)
### python
第3题 Python3 代码(提供: [FaDrYL](https://github.com/FaDrYL)
```Python3
```python
def lengthOfLongestSubstring(self, s: str) -> int:
# 子字符串
sub = ""
@ -399,3 +409,172 @@ def lengthOfLongestSubstring(self, s: str) -> int:
return len(sub)
return largest
```
### javascript
[76.最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring)
```js
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
const minWindow = (s, t) => {
let minLen = s.length + 1;
let start = s.length; // 结果子串的起始位置
let map = {}; // 存储目标字符和对应的缺失个数
let missingType = 0; // 当前缺失的字符种类数
for (const c of t) { // t为baac的话map为{a:2,b:1,c:1}
if (!map[c]) {
missingType++; // 需要找齐的种类数 +1
map[c] = 1;
} else {
map[c]++;
}
}
let l = 0, r = 0; // 左右指针
for (; r < s.length; r++) { // 主旋律扩张窗口超出s串就结束
let rightChar = s[r]; // 获取right指向的新字符
if (map[rightChar] !== undefined) map[rightChar]--; // 是目标字符,它的缺失个数-1
if (map[rightChar] == 0) missingType--; // 它的缺失个数新变为0缺失的种类数就-1
while (missingType == 0) { // 当前窗口包含所有字符的前提下,尽量收缩窗口
if (r - l + 1 < minLen) { // 窗口宽度如果比minLen小就更新minLen
minLen = r - l + 1;
start = l; // 更新最小窗口的起点
}
let leftChar = s[l]; // 左指针要右移,左指针指向的字符要被丢弃
if (map[leftChar] !== undefined) map[leftChar]++; // 被舍弃的是目标字符,缺失个数+1
if (map[leftChar] > 0) missingType++; // 如果缺失个数新变为>0缺失的种类+1
l++; // 左指针要右移 收缩窗口
}
}
if (start == s.length) return "";
return s.substring(start, start + minLen); // 根据起点和minLen截取子串
};
```
[567.字符串的排列](https://leetcode-cn.com/problems/permutation-in-string)
```js
var checkInclusion = function(s1, s2) {
const n = s1.length, m = s2.length;
if (n > m) {
return false;
}
const cnt1 = new Array(26).fill(0);
const cnt2 = new Array(26).fill(0);
for (let i = 0; i < n; ++i) {
++cnt1[s1[i].charCodeAt() - 'a'.charCodeAt()];
++cnt2[s2[i].charCodeAt() - 'a'.charCodeAt()];
}
if (cnt1.toString() === cnt2.toString()) {
return true;
}
for (let i = n; i < m; ++i) {
++cnt2[s2[i].charCodeAt() - 'a'.charCodeAt()];
--cnt2[s2[i - n].charCodeAt() - 'a'.charCodeAt()];
if (cnt1.toString() === cnt2.toString()) {
return true;
}
}
return false;
};
```
[438.找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string)
```js
/**
* @param {string} s
* @param {string} p
* @return {number[]}
*/
var findAnagrams = function(s, p) {
// 用于保存结果
const res = []
// 用于统计p串所需字符
const need = new Map()
for(let i = 0; i < p.length; i++) {
need.set(p[i], need.has(p[i])?need.get(p[i])+1: 1)
}
// 定义滑动窗口
let left = 0, right = 0, valid = 0
// 用于统计窗口中的字符
const window = new Map()
// 遍历s串
while(right < s.length) {
// 进入窗口的字符
const c = s[right]
// 扩大窗口
right++
// 进入窗口的字符是所需字符
if (need.has(c)) {
// 更新滑动窗口中的字符记录
window.set(c, window.has(c)?window.get(c)+1:1)
// 当窗口中的字符数和滑动窗口中的字符数一致
if (window.get(c) === need.get(c)) {
// 有效字符自增
valid++
}
}
// 当滑动窗口的大小超出p串长度时 收缩窗口
while (right - left >= p.length) {
// 有效字符和所需字符数一致 找到一条符合条件的子串
if (valid === need.size) {
// 保存子串的起始索引位置
res.push(left)
}
// 离开窗口的字符
const d = s[left]
// 收缩窗口
left++
// 如果离开窗口字符是所需字符
if (need.has(d)) {
// 如果离开字符数和所需字符数一致
if (window.get(d) === need.get(d)) {
// 有效字符减少一个
valid--
}
// 更新滑动窗口中的字符数
window.set(d, window.get(d)-1)
}
}
}
// 返回结果
return res
};
```
[3.无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters)
```js
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
// 哈希集合,记录每个字符是否出现过
const occ = new Set();
const n = s.length;
// 右指针,初始值为 -1相当于我们在字符串的左边界的左侧还没有开始移动
let rk = -1, ans = 0;
for (let i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.delete(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.has(s.charAt(rk + 1))) {
// 不断地移动右指针
occ.add(s.charAt(rk + 1));
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = Math.max(ans, rk - i + 1);
}
return ans;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo)
* [Union-Find算法应用](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo/)
* [Union-Find算法应用](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -34,7 +34,7 @@
![](../pictures/pancakeSort/title.png)
如何解决这个问题呢?其实类似上篇文章 [递归反转链表的一部分](https://labuladong.gitbook.io/algo),这也是需要**递归思想**的。
如何解决这个问题呢?其实类似上篇文章 [递归反转链表的一部分](https://labuladong.gitbook.io/algo/),这也是需要**递归思想**的。
### 一、思路分析
@ -141,16 +141,19 @@ void reverse(int[] arr, int i, int j) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[969.煎饼排序](https://leetcode-cn.com/problems/pancake-sorting)
### python3
[fengshuu](https://github.com/fengshuu) 提供 Python3 解法代码:
@ -309,3 +312,87 @@ class Solution {
}
}
```
### javascript
```js
/**
* @param {number[]} arr
* @return {number[]}
*/
var pancakeSort = function (arr) {
let res = []
const sort = (cakes, n) => {
// base case
if (n === 1) return;
// 寻找最大饼的索引
let maxCake = 0;
let maxCakeIndex = 0;
for (let i = 0; i < n; i++)
if (cakes[i] > maxCake) {
maxCakeIndex = i;
maxCake = cakes[i];
}
// 第一次翻转,将最大饼翻到最上面
reverse(cakes, 0, maxCakeIndex);
res.push(maxCakeIndex + 1);
// 第二次翻转,将最大饼翻到最下面
reverse(cakes, 0, n - 1);
res.push(n);
// 递归调用
sort(cakes, n - 1);
}
let reverse = (arr, i, j) => {
while (i < j) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
sort(arr, arr.length)
return res;
};
```
也可以写成下面这样。
```js
var reserve = function (nums, k) {
let l = 0, r = k - 1
while (l < r) {
let temp = nums[l]
nums[l] = nums[r]
nums[r] = temp
l++
r--
}
}
/**
* @param {number[]} arr
* @return {number[]}
*/
var pancakeSort = function (arr) {
let n = arr.length
if (n === 0) return []
let nums = [...arr], ans = []
nums.sort((a, b) => a - b)
for (let i = n - 1, j = 0; i > -1; i--, j++) {
let index = arr.indexOf(nums[i])
ans.push(index + 1, n - j)
reserve(arr, index + 1)
reserve(arr, n - j)
}
return ans
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [Git/SQL/正则表达式的在线练习平台](https://labuladong.gitbook.io/algo)
* [回溯算法团灭子集、排列、组合问题](https://labuladong.gitbook.io/algo)
* [Git/SQL/正则表达式的在线练习平台](https://labuladong.gitbook.io/algo/)
* [回溯算法团灭子集、排列、组合问题](https://labuladong.gitbook.io/algo/)
**-----------**
@ -96,7 +96,7 @@ PS**如果有的英文题目实在看不懂,有个小技巧**,你在题
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo)
* [设计Twitter](https://labuladong.gitbook.io/algo)
* [特殊数据结构:单调队列](https://labuladong.gitbook.io/algo/)
* [设计Twitter](https://labuladong.gitbook.io/algo/)
**-----------**
@ -267,7 +267,7 @@ https://leetcode.com/tag/divide-and-conquer/
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -275,4 +275,79 @@ https://leetcode.com/tag/divide-and-conquer/
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
### javascript
[437. 路径总和 III](https://leetcode-cn.com/problems/path-sum-iii/)
```js
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number} sum
* @return {number}
*/
var pathSum = function(root, sum) {
// 二叉树-题目要求只能从父节点到子节点 所以用先序遍历
// 路径总数
let ans = 0
// 存储前缀和
let map = new Map()
// 先序遍历二叉树
function dfs(presum,node) {
if(!node)return 0 // 遍历出口
// 将当前前缀和添加到map
map.set(presum,(map.get(presum) || 0) +1 )
// 从根节点到当前节点的值
let target = presum + node.val
// target-sum = 需要的前缀和长度
// 然而前缀和之前我们都存过了 检索map里key为该前缀和的value
// map的值相当于有多少个节点到当前节点=sum 也就是有几条路径
ans+=(map.get(target - sum) || 0)
// 按顺序遍历左右节点
dfs(target,node.left)
dfs(target,node.right)
// 这层遍历完就把该层的前缀和去掉
map.set(presum,map.get(presum) -1 )
}
dfs(0,root)
return ans
};
```
归并排序
```js
var sort = function (arr) {
if (arr.length <= 1) return arr;
let mid = parseInt(arr.length / 2);
// 递归调用自身拆分的数组都是排好序的最后传入merge合并处理
return merge(sort(arr.slice(0, mid)), sort(arr.slice(mid)));
}
// 将两个排好序的数组合并成一个顺序数组
var merge = function (left, right) {
let res = [];
while (left.length > 0 && right.length > 0) {
// 不断比较left和right数组的第一项小的取出存入res
left[0] < right[0] ? res.push(left.shift()) : res.push(right.shift());
}
return res.concat(left, right);
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [25 张图解:键入网址后,到网页显示,其间发生了什么](https://labuladong.gitbook.io/algo)
* [如何在无限序列中随机抽取元素](https://labuladong.gitbook.io/algo)
* [25 张图解:键入网址后,到网页显示,其间发生了什么](https://labuladong.gitbook.io/algo/)
* [如何在无限序列中随机抽取元素](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -338,16 +338,19 @@ class LRUCache {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[146.LRU缓存机制](https://leetcode-cn.com/problems/lru-cache/)
### c++
[gowufang](https://github.com/gowufang)提供第146题C++代码:
```cpp
class LRUCache {
@ -434,7 +437,11 @@ class LRUCache {
};
```
```python3
### python
```python
"""
所谓LRU缓存根本的难点在于记录最久被使用的键值对这就设计到排序的问题
在python中天生具备排序功能的字典就是OrderDict。
@ -447,20 +454,185 @@ class LRUCache {
"""
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity # cache的容量
self.visited = OrderedDict() # python内置的OrderDict具备排序的功能
def get(self, key: int) -> int:
if key not in self.visited:
return -1
self.visited.move_to_end(key) # 最近访问的放到链表最后,维护好顺序
return self.visited[key]
def put(self, key: int, value: int) -> None:
if key not in self.visited and len(self.visited) == self.capacity:
# last=False时按照FIFO顺序弹出键值对
# 因为我们将最近访问的放到最后所以最远访问的就是最前的也就是最first的故要用FIFO顺序
self.visited.popitem(last=False)
self.visited[key]=value
self.visited.move_to_end(key) # 最近访问的放到链表最后,维护好顺序
def __init__(self, capacity: int):
self.capacity = capacity # cache的容量
self.visited = OrderedDict() # python内置的OrderDict具备排序的功能
def get(self, key: int) -> int:
if key not in self.visited:
return -1
self.visited.move_to_end(key) # 最近访问的放到链表最后,维护好顺序
return self.visited[key]
def put(self, key: int, value: int) -> None:
if key not in self.visited and len(self.visited) == self.capacity:
# last=False时按照FIFO顺序弹出键值对
# 因为我们将最近访问的放到最后所以最远访问的就是最前的也就是最first的故要用FIFO顺序
self.visited.popitem(last=False)
self.visited[key]=value
self.visited.move_to_end(key) # 最近访问的放到链表最后,维护好顺序
```
### javascript
没啥好说的es6的哈希表Map + 双向链表。
这里先使用es5的语法实现一遍看完后相信你一定能用es6的class语法实现这里的map用的是es6中的map()这题是研究LRU的就不用在{}和map()上过于深究了,直接用`new Map()`比较方便。
```js
// 双向链表节点
var LinkNode = function (key, val) {
if (!(this instanceof LinkNode)) {
return new LinkNode(key, val)
}
this.key = key;
this.val = val;
}
// 双向链表
var DoubleLink = function () {
// 初始化双向链表的数据
this.head = new LinkNode(0, 0);
this.tail = new LinkNode(0, 0);
this.head.next = this.tail;
this.tail.prev = this.head;
// 链表元素数
this.size = 0;
}
// // 在链表尾部添加节点 x时间 O(1)
DoubleLink.prototype.addLast = function (node) {
node.prev = this.tail.prev;
node.next = this.tail;
this.tail.prev.next = node;
this.tail.prev = node;
++this.size;
}
// 删除链表中的 x 节点x 一定存在)
// 由于是双链表且给的是目标 Node 节点,时间 O(1)
DoubleLink.prototype.remove = function (node) {
node.prev.next = node.next;
node.next.prev = node.prev;
--this.size;
}
// 删除链表中第一个节点,并返回该节点,时间 O(1)
DoubleLink.prototype.removeFirst = function () {
if (this.head.next === this.tail)
return null;
let first = this.head.next;
this.remove(first);
return first;
}
// 返回链表长度,时间 O(1)
DoubleLink.prototype.getSize = function () {
return this.size;
}
/**
* @param {number} capacity
*/
var LRUCache = function (capacity) {
this.map = new Map();
this.cache = new DoubleLink();
this.cap = capacity;
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
if (!this.map.has(key)) {
return -1;
}
// 将该数据提升为最近使用的
this.makeRecently(key);
return this.map.get(key).val;
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
if (this.map.has(key)) {
// 删除旧的数据
this.deleteKey(key);
// 新插入的数据为最近使用的数据
this.addRecently(key, value);
return;
}
if (this.cap === this.cache.getSize()) {
// 删除最久未使用的元素
this.removeLeastRecently();
}
// 添加为最近使用的元素
this.addRecently(key, value);
};
/**
* Your LRUCache object will be instantiated and called as such:
* var obj = new LRUCache(capacity)
* var param_1 = obj.get(key)
* obj.put(key,value)
*/
/* 将某个 key 提升为最近使用的 */
LRUCache.prototype.makeRecently = function (key) {
let x = this.map.get(key);
// 先从链表中删除这个节点
this.cache.remove(x);
// 重新插入到队尾
this.cache.addLast(x);
}
/* 添加最近使用的元素 */
LRUCache.prototype.addRecently = function (key, val) {
let x = new LinkNode(key, val);
// 链表尾部就是最近使用的元素
this.cache.addLast(x);
// 别忘了在 map 中添加 key 的映射
this.map.set(key, x);
}
/* 删除某一个 key */
LRUCache.prototype.deleteKey = function (key) {
let x = this.map.get(key);
// 从链表中删除
this.cache.remove(x);
// 从 map 中删除
this.map.delete(key);
}
/* 删除最久未使用的元素 */
LRUCache.prototype.removeLeastRecently = function () {
// 链表头部的第一个元素就是最久未使用的
let deletedNode = this.cache.removeFirst();
// 同时别忘了从 map 中删除它的 key
let deletedKey = deletedNode.key;
this.map.delete(deletedKey);
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何运用贪心思想玩跳跃游戏](https://labuladong.gitbook.io/algo)
* [如何寻找最长回文子串](https://labuladong.gitbook.io/algo)
* [如何运用贪心思想玩跳跃游戏](https://labuladong.gitbook.io/algo/)
* [如何寻找最长回文子串](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -161,17 +161,22 @@ for (int i = 0; i < n; i++)
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
#### c++
[875.爱吃香蕉的珂珂](https://leetcode-cn.com/problems/koko-eating-bananas)
[1011.在D天内送达包裹的能力](https://leetcode-cn.com/problems/capacity-to-ship-packages-within-d-days)
### c++
[cchroot](https://github.com/cchroot) 提供 C++ 代码:
```c++
@ -247,3 +252,124 @@ class Solution:
return H
```
### javascript
用js写二分的时候一定要注意使用`Math.floor((right - left) / 2)`或者`paserInt()`将结果整数化由于js不声明变量类型很多时候就很难发现自己浮点数、整数使用的问题。
[875.爱吃香蕉的珂珂](https://leetcode-cn.com/problems/koko-eating-bananas)
```js
/**
* @param {number[]} piles
* @param {number} H
* @return {number}
*/
var minEatingSpeed = function (piles, H) {
// 套用搜索左侧边界的算法框架
let left = 1, right = getMax(piles) + 1;
while (left < right) {
// 防止溢出
let mid = left + Math.floor((right - left) / 2);
if (canFinish(piles, mid, H)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
};
// 时间复杂度 O(N)
let canFinish = (piles, speed, H) => {
let time = 0;
for (let n of piles) {
time += timeOf(n, speed);
}
return time <= H;
}
// 计算所需时间
let timeOf = (n, speed) => {
return Math.floor(
(n / speed) + ((n % speed > 0) ? 1 : 0)
);
}
let getMax = (piles) => {
let max = 0;
for (let n of piles) {
max = Math.max(n, max);
}
return max;
}
```
[传送门1011.在D天内送达包裹的能力](https://leetcode-cn.com/problems/capacity-to-ship-packages-within-d-days)
```js
// 第1011题
/**
* @param {number[]} weights
* @param {number} D
* @return {number}
*/
// 寻找左侧边界的二分查找
var shipWithinDays = function (weights, D) {
// 载重可能的最小值
let left = getMax(weights);
// 载重可能的最大值 + 1
let right = getSum(weights) + 1;
while (left < right) {
let mid = left + Math.floor((right - left) / 2);
if (canFinish(weights, D, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
// 如果载重为 cap是否能在 D 天内运完货物?
let canFinish = (w, D, cap) => {
let i = 0;
for (let day = 0; day < D; day++) {
let maxCap = cap;
while ((maxCap -= w[i]) >= 0) {
i++;
if (i === w.length)
return true;
}
}
return false;
}
let getMax = (piles) => {
let max = 0;
for (let n of piles) {
max = Math.max(n, max);
}
return max;
}
/**
* @param {number[]} weights
// 获取货物总重量
*/
let getSum = (weights) => {
return weights.reduce((total, cur) => {
total += cur;
return total
}, 0)
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [区间调度之区间交集问题](https://labuladong.gitbook.io/algo)
* [动态规划和回溯算法到底谁是谁爹?](https://labuladong.gitbook.io/algo)
* [区间调度之区间交集问题](https://labuladong.gitbook.io/algo/)
* [动态规划和回溯算法到底谁是谁爹?](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -32,7 +32,7 @@
### 一、分析问题
首先,前文[学习数据结构的框架思维](https://labuladong.gitbook.io/algo)提到过,链表是一种兼具递归和迭代性质的数据结构,认真思考一下可以发现**这个问题具有递归性质**。
首先,前文[学习数据结构的框架思维](https://labuladong.gitbook.io/algo/)提到过,链表是一种兼具递归和迭代性质的数据结构,认真思考一下可以发现**这个问题具有递归性质**。
什么叫递归性质?直接上图理解,比如说我们对这个链表调用 `reverseKGroup(head, 2)`,即以 2 个节点为一组反转链表:
@ -148,7 +148,7 @@ ListNode reverseKGroup(ListNode head, int k) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -156,4 +156,80 @@ ListNode reverseKGroup(ListNode head, int k) {
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[25.K个一组翻转链表](https://leetcode-cn.com/problems/reverse-nodes-in-k-group)
### javascript
```js
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
// 示例一反转以a为头结点的链表
let reverse = function (a) {
let pre, cur, nxt;
pre = null;
cur = a;
nxt = a;
while (cur != null) {
nxt = cur.next;
// 逐个结点反转
cur.next = pre;
// 更新指针位置
pre = cur;
cur = nxt;
}
// 返回反转后的头结点
return pre;
}
/** 反转区间 [a, b) 的元素,注意是左闭右开 */
let reverse = (a, b) => {
let pre, cur, nxt;
pre = null;
cur = a;
nxt = a;
// while 终止的条件改一下就行了
while (cur !== b) {
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
// 返回反转后的头结点
return pre;
}
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
let reverseKGroup = (head, k) => {
if (head == null) return null;
// 区间 [a, b) 包含 k 个待反转元素
let a, b;
a = b = head;
for (let i = 0; i < k; i++) {
// 不足k个不需反转base case
if(b==null) return head;
b = b.next;
}
// 反转前k个元素
let newHead = reverse(a,b);
// 递归反转后续链表并连接起来
a.next = reverseKGroup(b,k);
return newHead;
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo)
* [我用四个命令概括了 Git 的所有套路](https://labuladong.gitbook.io/algo)
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo/)
* [我用四个命令概括了 Git 的所有套路](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -141,12 +141,121 @@ int bulbSwitch(int n) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[292.Nim游戏](https://leetcode-cn.com/problems/nim-game)
[877.石子游戏](https://leetcode-cn.com/problems/stone-game)
[319.灯泡开关](https://leetcode-cn.com/problems/bulb-switcher)
### python
由[JodyZ0203](https://github.com/JodyZ0203)提供 292. Nim 游戏 Python3 解法代码:
```Python
class Solution:
def canWinNim(self, n: int) -> bool:
# 如果除于是0说明是4的倍数所以必输
# 否则不是除于不等于0说明不是4的倍数说明必胜
return n % 4 != 0
```
由[JodyZ0203](https://github.com/JodyZ0203)提供 877. 石子游戏 Python3 解法代码:
```Python
class Solution:
def stoneGame(self, piles: List[int]) -> bool:
# 双方都很聪明的前提下, 先手必胜无疑
# 先手可以提前观察偶数堆还是基数的石头总数更多
return True
```
由[JodyZ0203](https://github.com/JodyZ0203)提供 319. 灯泡开关 Python3 解法代码:
```Python
class Solution:
def bulbSwitch(self, n: int) -> int:
# 平方根电灯个数之后向下取整即可
return floor(sqrt (n))
```
### c++
由[JodyZ0203](https://github.com/JodyZ0203)提供 877. 石子游戏 C++ 解法代码:
```cpp
class Solution {
public:
bool stoneGame(vector<int>& piles) {
// 双方都很聪明的前提下, 先手必胜无疑
return true;
}
};
```
由[JodyZ0203](https://github.com/JodyZ0203)提供 319. 灯泡开关 C++ 解法代码:
```cpp
class Solution {
public:
int bulbSwitch(int n) {
// 平方根电灯个数之后向下取整即可
return floor(sqrt (n));
}
};
```
### javascript
[292.Nim游戏](https://leetcode-cn.com/problems/nim-game)
```js
/**
* @param {number} n
* @return {boolean}
*/
var canWinNim = function(n) {
// 如果上来就踩到 4 的倍数,那就认输吧
// 否则,可以把对方控制在 4 的倍数,必胜
return n % 4 !== 0;
};
```
[877.石子游戏](https://leetcode-cn.com/problems/stone-game)
```js
var stoneGame = function(piles) {
return true;
};
```
[319.灯泡开关](https://leetcode-cn.com/problems/bulb-switcher)
```js
/**
* @param {number} n
* @return {number}
*/
var bulbSwitch = function(n) {
return Math.floor(Math.sqrt(n));
};
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo)
* [经典动态规划:子集背包问题](https://labuladong.gitbook.io/algo)
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo/)
* [经典动态规划:子集背包问题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -20,7 +20,7 @@
**-----------**
二分查找本身不难理解,难在巧妙地运用二分查找技巧。对于一个问题,你可能都很难想到它跟二分查找有关,比如前文 [最长递增子序列](https://labuladong.gitbook.io/algo) 就借助一个纸牌游戏衍生出二分查找解法。
二分查找本身不难理解,难在巧妙地运用二分查找技巧。对于一个问题,你可能都很难想到它跟二分查找有关,比如前文 [最长递增子序列](https://labuladong.gitbook.io/algo/) 就借助一个纸牌游戏衍生出二分查找解法。
今天再讲一道巧用二分查找的算法问题:如何判定字符串 `s` 是否是字符串 `t` 的子序列(可以假定 `s` 长度比较小,且 `t` 的长度非常大)。举两个例子:
@ -95,7 +95,7 @@ for (int i = 0; i < n; i++) {
### 三、再谈二分查找
在前文 [二分查找详解](https://labuladong.gitbook.io/algo) 中,详解了如何正确写出三种二分查找算法的细节。二分查找返回目标值 `val` 的索引,对于搜索**左侧边界**的二分查找,有一个特殊性质:
在前文 [二分查找详解](https://labuladong.gitbook.io/algo/) 中,详解了如何正确写出三种二分查找算法的细节。二分查找返回目标值 `val` 的索引,对于搜索**左侧边界**的二分查找,有一个特殊性质:
**当 `val` 不存在时,得到的索引恰好是比 `val` 大的最小元素索引**。
@ -160,17 +160,22 @@ boolean isSubsequence(String s, String t) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
======其他语言代码======
[dekunma](https://www.linkedin.com/in/dekun-ma-036a9b198/) 提供C++代码
[392.判断子序列](https://leetcode-cn.com/problems/is-subsequence)
### c++
[dekunma](https://www.linkedin.com/in/dekun-ma-036a9b198/) 提供C++代码
**解法一:遍历(也可以用双指针):**
```C++
class Solution {
public:
@ -188,7 +193,7 @@ public:
return true;
}
};
```
```
**解法二:二分查找:**
```C++
@ -232,3 +237,75 @@ public:
}
};
```
### javascript
双指针一遍扫描做法
```js
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isSubsequence = function (s, t) {
let i = 0, j = 0;
while (i < s.length && j < t.length) {
if (s[i] === t[j]) i++;
j++;
}
return i === s.length;
};
```
**升级二分法做法可应对与多个s的情况**
```js
var isSubsequence = function (s, t) {
let m = s.length, n = t.length;
let index = new Array(256);
// 先记下 t 中每个字符出现的位置
for (let i = 0; i < n; i++) {
let c = t[i];
if (index[c] == null) {
index[c] = [];
}
index[c].push(i)
}
// 串t上的指针
let j = 0;
// 借助index查找s[i]
for (let i = 0; i < m; i++) {
let c = s[i];
// 整个t压根没有字符c
if (index[c] == null) return false
let pos = left_bound(index[c], j);
// 二分搜索区间中没有找到字符c
if (pos == index[c].length) return false;
// 向前移动指针j
j = index[c][pos] + 1;
}
return true;
};
var left_bound = function (arr, tar) {
let lo = 0, hi = arr.length;
while (lo < hi) {
let mid = lo + Math.floor((hi - lo) / 2);
if (tar > arr[mid]) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何高效进行模幂运算](https://labuladong.gitbook.io/algo)
* [一文学会递归解题](https://labuladong.gitbook.io/algo)
* [如何高效进行模幂运算](https://labuladong.gitbook.io/algo/)
* [一文学会递归解题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -229,43 +229,138 @@ p.next = reverse(q);
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
C++版本:
[234.回文链表](https://leetcode-cn.com/problems/palindrome-linked-list)
### C++
```cpp
bool isPalindrome(ListNode* head) {
if (head == nullptr || head->next == nullptr) //为空或者只有一个节点时直接判断为true
return true;
ListNode* slow = head, * fast = head;
while (fast != nullptr) {//首先找到中间节点
slow = slow->next;
fast = fast->next == nullptr? fast->next:fast->next->next; //因为链表长度可能是奇数或偶数,所以需要进行判断
}
ListNode* temp = nullptr,* pre = nullptr;//pre始终保持后续链表的头部temp节点则作为中间零时替换的节点
while (slow != nullptr) {//利用头插法,将当前节点与后续链表断链处理,反转后半部分的链表
temp = slow->next;
slow->next = pre;//建立连接
pre = slow;//pre始终作为后续链表的头部
slow = temp;
}
while (head !=nullptr && pre != nullptr) {//同步进行比较
if (head->val != pre->val) {//值有不一样的说明不是回文联表直接返回false了
return false;
}
head = head->next;//head向下走直到走到空
pre = pre->next;//pre节点也向下走直到走到空
}
return true;//到此说明当前链表是回文链表返回true即可
bool isPalindrome(ListNode* head) {
if (head == nullptr || head->next == nullptr) //为空或者只有一个节点时直接判断为true
return true;
ListNode* slow = head, * fast = head;
while (fast != nullptr) {//首先找到中间节点
slow = slow->next;
fast = fast->next == nullptr? fast->next:fast->next->next; //因为链表长度可能是奇数或偶数,所以需要进行判断
}
ListNode* temp = nullptr,* pre = nullptr;//pre始终保持后续链表的头部temp节点则作为中间零时替换的节点
while (slow != nullptr) {//利用头插法,将当前节点与后续链表断链处理,反转后半部分的链表
temp = slow->next;
slow->next = pre;//建立连接
pre = slow;//pre始终作为后续链表的头部
slow = temp;
}
while (head !=nullptr && pre != nullptr) {//同步进行比较
if (head->val != pre->val) {//值有不一样的说明不是回文联表直接返回false了
return false;
}
head = head->next;//head向下走直到走到空
pre = pre->next;//pre节点也向下走直到走到空
}
return true;//到此说明当前链表是回文链表返回true即可
}
```
### javascript
**后序遍历法**
```js
let left;
var isPalindrome = function(head) {
left = head;
return traverse(head);
};
var traverse= function(right){
if(right == null) return true;
let res = traverse(right.next);
// 后序遍历
res = res && (right.val === left.val);
left = left.next;
return res;
}
```
**双指针找到链表中点+链表翻转+链表对比**
```js
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function (head) {
if(head == null || head.next == null){
return true;
}
let left = head;
let midNode = findMid(head);
let right = reverse(midNode);
// 开始比较
while(right != null){
if(left.val !== right.val){
return false;
}
left = left.next;
right = right.next;
}
return true;
};
// 双指针找到链表中间结点
var findMid = function (head){
let fast, slow;
fast = slow = head;
while (fast != null && fast.next != null && slow != null) {
slow = slow.next;
fast = fast.next.next;
}
// slow 现在指向链表中点
return slow;
}
// 翻转以head为头的链表
var reverse = function (head) {
let pre = null, cur = head;
while(cur!=null){
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo)
* [Linux shell 的实用小技巧](https://labuladong.gitbook.io/algo)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo/)
* [Linux shell 的实用小技巧](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -104,17 +104,18 @@ char leftOf(char c) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
### Python3
[20.有效的括号](https://leetcode-cn.com/problems/valid-parentheses)
### python
```python
def isValid(self, s: str) -> bool:
left = []
@ -136,6 +137,9 @@ def isValid(self, s: str) -> bool:
```
### java
```java
//基本思想:每次遇到左括号时都将相对应的右括号''']'或'}'推入堆栈
//如果在字符串中出现右括号,则需要检查堆栈是否为空,以及顶部元素是否与该右括号相同。如果不是,则该字符串无效。
@ -157,6 +161,44 @@ public boolean isValid(String s) {
return stack.isEmpty();
}
```
### javascript
```js
/**
* @param {string} s是
* @return {boolean}
*/
var isValid = function (s) {
let left = []
for (let c of s) {
if (c === '(' || c === '{' || c === '[') {
left.push(c);
} else {
// 字符c是右括号
//出现右括号,检查堆栈是否为空,以及顶部元素是否与该右括号相同
if (left.length !== 0 && leftOf(c) === left[left.length - 1]) {
left.pop();
} else {
// 和最近的左括号不匹配
return false;
}
}
}
return left.length === 0;
};
let leftOf = function (c) {
if (c === '}') return '{';
if (c === ')') return '(';
return '[';
}
```

View File

@ -66,6 +66,23 @@ ListNode deleteDuplicates(ListNode head) {
![labuladong](../pictures/labuladong.png)
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[26. 删除排序数组中的重复项](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)
[83. 删除排序链表中的重复元素](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/)
### python
[eric wang](https://www.github.com/eric496) 提供有序数组 Python3 代码
```python
@ -108,14 +125,68 @@ def deleteDuplicates(self, head: ListNode) -> ListNode:
return head
```
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
### javascript
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
[26. 删除排序数组中的重复项](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)
```js
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function(nums) {
let n = nums.length;
if (n === 0) return 0;
if (n === 1) return 1;
let slow = 0, fast = 1;
while (fast < n) {
if (nums[fast] !== nums[slow]) {
slow++;
// 维护nums[0...slow]无重复
nums[slow] = nums[fast];
}
fast++;
}
// 长度为索引+1
return slow + 1;
};
```
[83. 删除排序链表中的重复元素](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/)
```js
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var deleteDuplicates = function (head) {
if (head == null) return null;
let slow = head, fast = head.next;
while (fast != null) {
if(fast.val !== slow.val){
// nums[slow] = nums[fast];
slow.next = fast;
// slow++;
slow = slow.next;
}
// fast++
fast = fast.next;
}
// 断开与后面重复元素的连接
slow.next = null;
return head;
}
```
======其他语言代码======

View File

@ -12,8 +12,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo)
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo)
* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo/)
* [twoSum问题的核心思想](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -276,7 +276,7 @@ void backtrack(int[] nums, LinkedList<Integer> track) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
@ -285,6 +285,16 @@ void backtrack(int[] nums, LinkedList<Integer> track) {
</p>
======其他语言代码======
[78.子集](https://leetcode-cn.com/problems/subsets)
[46.全排列](https://leetcode-cn.com/problems/permutations)
[77.组合](https://leetcode-cn.com/problems/combinations)
### java
[userLF](https://github.com/userLF)提供全排列的java代码
```java
@ -292,33 +302,255 @@ import java.util.ArrayList;
import java.util.List;
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
res.clear();
dfs(nums, 0);//
return res;
}
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
res.clear();
dfs(nums, 0);//
return res;
}
public void dfs(int[] n, int start) {//start表示要被替换元素的位置
if( start >= n.length) {
List<Integer> list = new ArrayList<Integer>();
for(int i : n) {
list.add(i);
}
res.add(list);
return;
}
public void dfs(int[] n, int start) {//start表示要被替换元素的位置
if( start >= n.length) {
List<Integer> list = new ArrayList<Integer>();
for(int i : n) {
list.add(i);
}
res.add(list);
return;
}
for(int i = start; i< n.length; i++) {//i从start开始如果从start+1开始的话会把当前序列遗漏掉直接保存了下一个序列
int temp= n[i];
n[i] = n[start];
n[start] = temp;
dfs(n, start + 1);//递归下一个位置
//回到上一个状态
n[start] = n[i];
n[i] = temp;
}
}
for(int i = start; i< n.length; i++) {//i从start开始如果从start+1开始的话会把当前序列遗漏掉直接保存了下一个序列
int temp= n[i];
n[i] = n[start];
n[start] = temp;
dfs(n, start + 1);//递归下一个位置
//回到上一个状态
n[start] = n[i];
n[i] = temp;
}
}
}
```
```
### javascript
[传送门78. 子集](https://leetcode-cn.com/problems/subsets/)
数学归纳思想
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var subsets = function (nums) {
// base case, 返回一个空集
if (nums.length === 0) {
return [[]]
}
// 把最后一个元素拿出来
let n = nums.pop();
// 递归算出前面元素的所有子集
let res = subsets(nums);
let size = res.length;
for (let i = 0; i < size; i++) {
// 然后在之前的结果之上追加
res.push(res[i]);
res[res.length - 1].push(n);
}
return res;
}
```
回溯思想
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
const subsets = (nums) => {
// 1. 设置结果集
const result = [[]];
// 2. 数组排序
nums.sort((a, b) => a - b);
// 3. 递归
const recursion = (index, path) => {
// 3.1 设置终止条件
if (path.length === nums.length) {
return;
}
// 3.2 遍历数组
for (let i = index; i < nums.length; i++) {
// 3.2.1 添加内容
path.push(nums[i]);
// 3.2.2 添加结果集
result.push(path.concat());
// 3.2.3 进一步递归
recursion(i + 1, path);
// 3.2.4 回溯,还原之前状态,以备下一次使用
path.pop();
}
};
recursion(0, []);
// 4. 返回结果
return result;
};
console.log(subsets([1, 2, 3]));
```
[传送门46.全排列](https://leetcode-cn.com/problems/permutations)
不得不说用js实现递归总是能遇上很多坑其中多半都是js内置函数和引用问题的坑。看完本文你很容易就能写入如下的js解法但结果放到leetcode一跑输出却十分迷惑。
```js
let res = []
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
let track = [];
backtrack(nums, track);
return res;
};
var backtrack = function (nums, track) {
// 触发结束条件
if (track.length === nums.length) {
res.push(track)
return;
}
for (let i = 0; i < nums.length; i++) {
// 排除不合法的选择
if (track.indexOf(nums[i]) > -1) {
continue;
}
// 做选择
track.push(nums[i]);
// 进入下一层决策树
backtrack(nums, track);
// 取消选择
track.pop()
}
}
```
```
输入:[1,2,3]
输出结果:[[],[],[],[],[],[]]
预期结果:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
```
经过借鉴和改进后无bug写法如下。
```js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums){
// 1. 设置结果集
const result = [];
// 2. 回溯
const recursion = (path, set) => {
// 2.1 设置回溯终止条件
if (path.length === nums.length) {
// 2.1.1 推入结果集
result.push(path.concat());
// 2.1.2 终止递归
return;
}
// 2.2 遍历数组
for (let i = 0; i < nums.length; i++) {
// 2.2.1 必须是不存在 set 中的坐标
if (!set.has(i)) {
// 2.2.2 本地递归条件(用完记得删除)
path.push(nums[i]);
set.add(i);
// 2.2.3 进一步递归
recursion(path, set);
// 2.2.4 回溯:撤回 2.2.2 的操作
path.pop();
set.delete(i);
}
}
};
recursion([], new Set());
// 3. 返回结果
return result;
};
```
看到这,才恍然大悟,原来是在 2.1.1 推入结果集的过程中需要使用concat来实现数组的浅复制并加入到result结果集中不然会因为引用问题而导致结果集中都是空list。除此之外你还要注意格外let块级作用域在作用域外是找不到的
[传送门77.组合](https://leetcode-cn.com/problems/combinations)
```js
var combine = function (n, k) {
const res = []
if (k <= 0 || n <= 0) return res;
const track = [];
const backtrack = (n, k, start, track) => {
// 到达树的底部
if (k === track.length) {
res.push(track.concat());
return;
}
// 注意i从start开始递增
for (let i = start; i <= n; i++) {
// 做选择
track.push(i);
backtrack(n, k, i + 1, track);
// 撤销选择
track.pop();
}
}
backtrack(n, k, 1, track);
return res;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [一个方法团灭 LeetCode 股票买卖问题](https://labuladong.gitbook.io/algo)
* [Linux shell 的实用小技巧](https://labuladong.gitbook.io/algo)
* [一个方法团灭 LeetCode 股票买卖问题](https://labuladong.gitbook.io/algo/)
* [Linux shell 的实用小技巧](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -225,12 +225,84 @@ private int distance(int[] intv) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[855.考场就座](https://leetcode-cn.com/problems/exam-room)
### javascript
js内置没有treeset相关的实现而且实现起来也比较麻烦。
array记录有人的位置
seat的情况如下
- 如果没有array时seatNo默认0
- 有array有一个时则看离两边的距离选距离远的
- 两两遍历取中间值和初始值之和
```js
class ExamRoom {
/**
* @param {number} N
*/
constructor(N) {
this.array = [];
this.seatNo = 0;
this.number = N - 1;
}
/**
* @return {number}
*/
seat() {
this.seatNo = 0;
if (this.array.length == 1) {
if (this.array[0] == 0) {
this.seatNo = this.number;
} else if (this.array[0] == this.number) {
this.seatNo = 0;
} else {
let distance1 = this.array[0];
let distance2 = this.number - this.array[0];
if (distance1 >= distance2) {
this.seatNo = 0 + distance1;
} else {
this.seatNo = distance1 + distance2;
}
}
} else if ((this.array.length > 1)) {
let maxDistance = this.array[0], start;
for (let i = 0; i < this.array.length - 1; i++) {
let distance = Math.floor((this.array[i + 1] - this.array[i] >>> 1));
if (maxDistance < distance) {
maxDistance = distance;
start = this.array[i]
this.seatNo = start + maxDistance;
}
}
if (this.number - this.array[this.array.length - 1] > maxDistance) {
this.seatNo = this.number;
}
}
this.array.push(this.seatNo);
this.array.sort((a, b) => { return a - b })
return this.seatNo;
}
/**
* @param {number} p
* @return {void}
*/
leave(p) {
let index = this.array.indexOf(p)
this.array.splice(index, 1)
};
}
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [算法就像搭乐高:带你手撸 LRU 算法](https://labuladong.gitbook.io/algo)
* [区间调度之区间合并问题](https://labuladong.gitbook.io/algo)
* [算法就像搭乐高:带你手撸 LRU 算法](https://labuladong.gitbook.io/algo/)
* [区间调度之区间合并问题](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -170,17 +170,18 @@ int countPrimes(int n) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
C++解法:
[204.计数质数](https://leetcode-cn.com/problems/count-primes)
### C++
采用的算法是埃拉托斯特尼筛法
埃拉托斯特尼筛法的具体内容就是:**要得到自然数n以内的全部素数必须把不大于根号n的所有素数的倍数剔除剩下的就是素数。**
同时考虑到大于2的偶数都不是素数所以可以进一步优化成**要得到自然数n以内的全部素数必须把不大于根号n的所有素数的奇数倍剔除剩下的奇数就是素数。**
@ -189,28 +190,57 @@ C++解法:
这里提供C++的代码:
```C++
class Solution {
public:
int countPrimes(int n) {
public:
int countPrimes(int n) {
int res = 0;
bool prime[n+1];
for(int i = 0; i < n; ++i)
prime[i] = true;
prime[i] = true;
for(int i = 2; i <= sqrt(n); ++i) //计数过程
{ //外循环优化因为判断一个数是否为质数只需要整除到sqrt(n),反推亦然
if(prime[i])
if(prime[i])
{
for(int j = i * i; j < n; j += i) //内循环优化i*i之前的比如i*2i*3等在之前的循环中已经验证了
{
for(int j = i * i; j < n; j += i) //内循环优化i*i之前的比如i*2i*3等在之前的循环中已经验证了
{
prime[j] = false;
}
}
prime[j] = false;
}
}
}
for (int i = 2; i < n; ++i)
if (prime[i]) res++; //最后遍历统计一遍存入res
if (prime[i]) res++; //最后遍历统计一遍存入res
return res;
}
}
};
```
### javascript
```js
var countPrimes = function (n) {
let isPrime = new Array(n);
isPrime.fill(true, 0, n)
// 计数过程
// 外循环优化因为判断一个数是否为质数只需要整除到sqrt(n),反推亦然
for (let i = 2; i * i < n; i++) {
if (isPrime[i]) {
// 内循环优化i*i之前的比如i*2i*3等在之前的循环中已经验证了
for (let j = i * i; j < n; j += i) {
isPrime[j] = false;
}
}
}
let res = 0;
for (let i = 2; i < n; i++) {
//最后遍历统计一遍存入res
if (isPrime[i]) res++;
}
return res;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo)
* [45张图解IP基础知识全家桶](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo/)
* [45张图解IP基础知识全家桶](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -203,16 +203,21 @@ if (l_max < r_max) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[42.接雨水](https://leetcode-cn.com/problems/trapping-rain-water)
### java
[Yifan Zhang](https://github.com/FanFan0919) 提供 java 代码
**双指针解法**:时间复杂度 O(N),空间复杂度 O(1)
@ -306,4 +311,124 @@ class Solution {
return res;
}
}
```
```
### javascript
**暴力解法**
```js
/**
* @param {number[]} height
* @return {number}
*/
var trap = function (height) {
let n = height.length;
let res = 0;
for (let i = 1; i <= n - 2; i++) {
let l_max = 0, r_max = 0;
// 找右边高的柱子
for (let j = i; j < n; j++) {
r_max = Math.max(r_max, height[j])
}
// 找左边高的柱子
for (let j = i; j >= 0; j--) {
l_max = Math.max(l_max, height[j])
}
// 如果自己就是最高的话
// l_max == r_max == height[i]
res += Math.min(l_max, r_max) - height[i]
}
return res;
};
```
**备忘录优化**
```js
/**
* @param {number[]} height
* @return {number}
*/
var trap = function (height) {
let n = height.length;
if (n <= 2) {
return 0;
}
let res = 0;
// 数组充当备忘录
let l_max = new Array(n);
let r_max = new Array(n);
// 初始化base case
l_max[0] = height[0];
r_max[n - 1] = height[n - 1];
// 从左往右算l_max
for (let i = 1; i < n; i++) {
l_max[i] = Math.max(height[i], l_max[i - 1])
}
// 从右往左计算r_max
for (let i = n - 2; i >= 0; i--) {
r_max[i] = Math.max(height[i], r_max[i + 1])
}
// 计算答案
for (let i = 1; i <= n - 2; i++) {
res += Math.min(l_max[i], r_max[i]) - height[i];
}
return res;
};
```
**双指针解法**
```js
/**
* @param {number[]} height
* @return {number}
*/
var trap = function (height) {
let n = height.length;
if (n <= 2) {
return 0;
}
let res = 0;
let left = 0;
let right = n - 1;
let l_max = height[0];
let r_max = height[n - 1];
while (left <= right) {
l_max = Math.max(l_max, height[left]);
r_max = Math.max(r_max, height[right]);
// res += min(l_max, r_max) - height[i]
if (l_max < r_max) {
res += l_max - height[left];
left++;
} else {
res += r_max - height[right];
right--;
}
}
return res;
};
```

View File

@ -12,8 +12,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [如何运用二分查找算法](https://labuladong.gitbook.io/algo)
* [动态规划答疑篇](https://labuladong.gitbook.io/algo)
* [如何运用二分查找算法](https://labuladong.gitbook.io/algo/)
* [动态规划答疑篇](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -123,16 +123,19 @@ string longestPalindrome(string s) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[5.最长回文子串](https://leetcode-cn.com/problems/longest-palindromic-substring)
### java
[cchromt](https://github.com/cchroot) 提供 Java 代码:
```java
@ -168,3 +171,44 @@ class Solution {
```
做完这题,大家可以去看看 [647. 回文子串](https://leetcode-cn.com/problems/palindromic-substrings/) ,也是类似的题目
### javascript
```js
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function (s) {
let res = "";
for (let i = 0; i < s.length; i++) {
// 以s[i]为中心的最长回文子串
let s1 = palindrome(s,i,i);
// 以 s[i] 和 s[i+1] 为中心的最长回文子串
let s2 = palindrome(s, i, i + 1);
// res = longest(res, s1, s2)
res = res.length > s1.length ? res : s1;
res = res.length > s2.length ? res : s2;
}
};
// 寻找最长回文串
let palindrome = (s, l, r) => {
// 防止索引越界
while (l >= 0 && r < s.length && s[l] === s[r]) {
// 向两边展开
l--;
r++;
}
// 返回以s[l]和s[r]为中心的最长回文串
return s.substr(l + 1, r - l - 1)
}
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [一文看懂 session 和 cookie](https://labuladong.gitbook.io/algo)
* [算法就像搭乐高:带你手撸 LFU 算法](https://labuladong.gitbook.io/algo)
* [一文看懂 session 和 cookie](https://labuladong.gitbook.io/algo/)
* [算法就像搭乐高:带你手撸 LFU 算法](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -140,12 +140,106 @@ $$ -->
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[382.链表随机节点](https://leetcode-cn.com/problems/linked-list-random-node)
[398.随机数索引](https://leetcode-cn.com/problems/random-pick-index)
### javascript
题目传送门:[382. 链表随机节点](https://leetcode-cn.com/problems/linked-list-random-node/)
返回链表中一个随机节点的值
```js
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
* @param {ListNode} head
*/
var Solution = function (head) {
this.head = head;
};
/**
* Returns a random node's value.
* @return {number}
*/
Solution.prototype.getRandom = function () {
let i = 0, res = 0;
let p = this.head;
// while循环遍历链表
while (p != null) {
// 生成一个 [0, i) 之间的整数
// 这个整数等于 0 的概率就是 1/i
if (Math.floor(Math.random()*(++i)) === 0) {
res = p.val;
}
p = p.next;
}
return res;
};
/**
* Your Solution object will be instantiated and called as such:
* var obj = new Solution(head)
* var param_1 = obj.getRandom()
*/
```
**[题目传送门398. 随机数索引](https://leetcode-cn.com/problems/random-pick-index/)**
假设当前正要读取第n个数据则我们以1/n的概率留下该数据否则以(n-1)/n 的概率留下前n-1个数据中的一个。
而前n-1个数组留下的那个概率为1/(n-1),因此最终留下上次n-1个数中留下的那个数的概率为[1/(n-1)]*[(n-1)/n] = 1/n,符合均匀分布的要求。
```js
/**
* @param {number[]} nums
*/
var Solution = function (nums) {
this.nums = nums;
// 需要k个数据在本题中k等于1
this.need = 1;
};
// 假设当前正要读取第n个数据则我们以1/n的概率留下该数据否则以(n-1)/n 的概率留下前n-1个数据中的一个。
// 而前n-1个数组留下的那个概率为1/(n-1),
// 因此最终留下上次n-1个数中留下的那个数的概率为[1/(n-1)]*[(n-1)/n] = 1/n,符合均匀分布的要求
//
Solution.prototype.pick = function (target) {
let count = 0
let res = 0
this.nums.forEach((value, key) => {
if (value === target) {
//我们的目标对象中选取。
count++;
//我们以1/n的概率留下该数据
if (Math.floor(Math.random() * count) === 0) {
res = key;
}
}
})
return res
};
```
======其他语言代码======

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo)
* [回溯算法最佳实践:括号生成](https://labuladong.gitbook.io/algo)
* [学习算法和数据结构的思路指南](https://labuladong.gitbook.io/algo/)
* [回溯算法最佳实践:括号生成](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -125,16 +125,52 @@ public int missingNumber(int[] nums) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[剑指 Offer 53 - II. 0n-1中缺失的数字](https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/)
[448.找到所有数组中消失的数字](https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array)
### python
```python
def missingNumber(self, nums: List[int]) -> int:
#思路1,位运算
res = len(nums)
for i,num in enumerate(nums):
res ^= i^num
return res
```
```python
def missingNumber(self, nums: List[int]) -> int:
#思路2,求和
n = len(nums)
return n*(n+1)//2-sum(nums)
```
```python
def missingNumber(self, nums: List[int]) -> int:
#思路3,防止整形溢出的优化
res = len(nums)
for i,num in enumerate(nums):
res+=i-num
return res
```
事实上在python3中不存在整数溢出的问题只要内存放得下思路3的优化提升并不大不过看上去有内味了哈...
### c++
[happy-yuxuan](https://github.com/happy-yuxuan) 提供 三种方法的 C++ 代码:
```c++
@ -179,3 +215,81 @@ int missingNumber(vector<int>& nums) {
```
### javascript
[传送门:剑指 Offer 53 - II. 0n-1中缺失的数字](https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/)
**位运算**
```js
/**
* @param {number[]} nums
* @return {number}
*/
var missingNumber = function(nums) {
let n = nums.length;
let res = 0;
// 先和新补的索引异或一下
res ^= n;
// 和其它的元素、索引做异或
for (let i = 0; i < n; i++) {
res ^= i ^ nums[i];
}
return res;
};
```
**直接相减**
```js
/**
* @param {number[]} nums
* @return {number}
*/
var missingNumber = function(nums) {
let n = nums.length;
let res = 0;
// 新补的索引
res += n - 0;
// 剩下索引和元素的差加起来
for (let i = 0; i < n; i++) {
res += i - nums[i];
}
return res;
};
```
[传送门448. 找到所有数组中消失的数字](https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/)
这道题的核心思路是将访问过的元素变成负数,第二次遍历直接收集正数并加入结果集中。
```js
/**
* @param {number[]} nums
* @return {number[]}
*/
var findDisappearedNumbers = function (nums) {
for (let i = 0; i < nums.length; i++) {
let newIndex = Math.abs(nums[i]) - 1;
if (nums[newIndex] > 0) {
nums[newIndex] *= -1;
}
}
let result = [];
for (let i = 1; i <= nums.length; i++) {
if (nums[i - 1] > 0) {
result.push(i);
}
}
return result;
};
```

View File

@ -11,8 +11,8 @@
![](../pictures/souyisou.png)
相关推荐:
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo)
* [25 张图解:键入网址后,到网页显示,其间发生了什么](https://labuladong.gitbook.io/algo)
* [手把手带你刷二叉树(第三期)](https://labuladong.gitbook.io/algo/)
* [25 张图解:键入网址后,到网页显示,其间发生了什么](https://labuladong.gitbook.io/algo/)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
@ -131,16 +131,21 @@ vector<int> findErrorNums(vector<int>& nums) {
**_____________**
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo) 持续更新最新文章**。
**刷算法,学套路,认准 labuladong公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。
**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。
<p align='center'>
<img src="../pictures/qrcode.jpg" width=200 >
</p>
======其他语言代码======
[645.错误的集合](https://leetcode-cn.com/problems/set-mismatch)
### java
[zhuli](https://github.com/1097452462 "zhuli")提供的Java代码
```java
class Solution {
@ -166,3 +171,38 @@ class Solution {
}
}
```
### javascript
```js
/**
* @param {number[]} nums
* @return {number[]}
*/
var findErrorNums = function (nums) {
let n = nums.length;
let dup = -1;
for (let i = 0; i < n; i++) {
// 现在的元素是从1开始的
let index = Math.abs(nums[i]) - 1;
if (nums[index] < 0) {
dup = Math.abs(nums[i]);
} else {
nums[index] *= -1;
}
}
let missing = -1;
for (let i = 0; i < n; i++) {
if (nums[i] > 0) {
// 将索引转换成元素
missing = i + 1;
}
}
return [dup, missing]
};
```