diff --git a/README.md b/README.md index 0a74a829e4a4336ddcab054c0bc78c5e16604d9c..d79b9cecd8f5eee48cb1c94c2d9433ebffcee608 100644 --- a/README.md +++ b/README.md @@ -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), diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213KMP\345\255\227\347\254\246\345\214\271\351\205\215\347\256\227\346\263\225.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213KMP\345\255\227\347\254\246\345\214\271\351\205\215\347\256\227\346\263\225.md" index 37a597be00a9b151d43da1c417ce25ef22ee9c3a..5e2bd1bb80d01aede1260d24b871c3d831cf4014 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213KMP\345\255\227\347\254\246\345\214\271\351\205\215\347\256\227\346\263\225.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213KMP\345\255\227\347\254\246\345\214\271\351\205\215\347\256\227\346\263\225.md" @@ -430,41 +430,128 @@ KMP 算法也就是动态规划那点事,我们的公众号文章目录有一

- ======其他语言代码====== + +[28.实现 strStr()](https://leetcode-cn.com/problems/implement-strstr) + +### python + [MoguCloud](https://github.com/MoguCloud) 提供 实现 strStr() 的 Python 完整代码: -```py + +```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])] - + 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) +}; +``` + + + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\215\232\345\274\210\351\227\256\351\242\230.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\215\232\345\274\210\351\227\256\351\242\230.md" index c75749ad25a126f4507dc78285dc3fc48a13e084..66dc8dad77d004fac2b63f4222e66327fa526863 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\215\232\345\274\210\351\227\256\351\242\230.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\215\232\345\274\210\351\227\256\351\242\230.md" @@ -214,14 +214,9 @@ int stoneGame(int[] piles) {

- ======其他语言代码====== - - - - -* 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] +}; +``` + + + +### \ No newline at end of file diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\233\233\351\224\256\351\224\256\347\233\230.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\233\233\351\224\256\351\224\256\347\233\230.md" index 312cad1f0e711e18e58eff566b37574693dc8d76..12fca8f5350020c76de9197e7baa57e7716ff933 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\233\233\351\224\256\351\224\256\347\233\230.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\345\233\233\351\224\256\351\224\256\347\233\230.md" @@ -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]; @@ -200,4 +200,60 @@ def dp(n, a_num, copy):

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### 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]; +} +``` + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\346\255\243\345\210\231\350\241\250\350\276\276.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\346\255\243\345\210\231\350\241\250\350\276\276.md" index a3d13bd0e7244f6cb463083d777bddf746bfcaa1..7e96fa9327db828c93bf918f171643035be0c08c 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\346\255\243\345\210\231\350\241\250\350\276\276.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\344\271\213\346\255\243\345\210\231\350\241\250\350\276\276.md" @@ -294,5 +294,132 @@ bool dp(string& s, int i, string& p, int j) {

- ======其他语言代码====== + +### 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; + } + + // 记录状态(i,j),消除重叠子问题 + 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; + } + + // 指针 i,j 从索引 0 开始移动 + return dp(s, 0, p, 0); +}; +``` + + + +### C++ + +```c++ +class Solution { +public: + map memo; + bool isMatch(string s, string p) { + // 指针 i,j 从索引 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; + } +}; +``` + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\256\276\350\256\241\357\274\232\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\256\276\350\256\241\357\274\232\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" index 55aeb8dc11bb4d9a839914009e206f4282723303..f0215de46a28fe7687c2750b1083b8ebf1b75dbf 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\256\276\350\256\241\357\274\232\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\256\276\350\256\241\357\274\232\346\234\200\351\225\277\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" @@ -214,9 +214,73 @@ public int lengthOfLIS(int[] nums) {

- ======其他语言代码====== +### 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++ diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\257\246\350\247\243\350\277\233\351\230\266.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\257\246\350\247\243\350\277\233\351\230\266.md" index c21bad80440ea2c23c82da37ef695db1eed0aff9..b155019c2500304553d8c0ba3c7ced7225090a7c 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\257\246\350\247\243\350\277\233\351\230\266.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\212\250\346\200\201\350\247\204\345\210\222\350\257\246\350\247\243\350\277\233\351\230\266.md" @@ -369,9 +369,10 @@ PS:为啥 `dp` 数组初始化为 `amount + 1` 呢,因为凑成 `amount` 金

- ======其他语言代码====== +### python + [DapangLiu](https://github.com/DapangLiu) 提供 509. 斐波那契数 Python3 解法代码: 递归写法 @@ -411,4 +412,176 @@ class Solution: for _ in range(n): dp_0, dp_1 = dp_1, dp_0 + dp_1 return dp_0 -``` \ No newline at end of file +``` + + + +### 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]; +} +``` + + + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\233\242\347\201\255\350\202\241\347\245\250\351\227\256\351\242\230.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\233\242\347\201\255\350\202\241\347\245\250\351\227\256\351\242\230.md" index f34d173e1861521d09db4b55db2851c0ac7762f3..73b40ed9baf82d97ca75178610cb45a818b830e0 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\233\242\347\201\255\350\202\241\347\245\250\351\227\256\351\242\230.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\233\242\347\201\255\350\202\241\347\245\250\351\227\256\351\242\230.md" @@ -427,5 +427,385 @@ int maxProfit_k_any(int max_k, int[] prices) {

+======其他语言代码====== + +[买卖股票的最佳时机](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) +}; +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\255\220\345\272\217\345\210\227\351\227\256\351\242\230\346\250\241\346\235\277.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\255\220\345\272\217\345\210\227\351\227\256\351\242\230\346\250\241\346\235\277.md" index 1f2570a095a7a9d0e19cff8bb275ddd008f42f60..cc21f27eb083f28a0b6eaf113cd37f58cbe7a462 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\255\220\345\272\217\345\210\227\351\227\256\351\242\230\346\250\241\346\235\277.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\345\255\220\345\272\217\345\210\227\351\227\256\351\242\230\346\250\241\346\235\277.md" @@ -174,4 +174,46 @@ int longestPalindromeSubseq(string s) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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] +}; +``` + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\212\242\346\210\277\345\255\220.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\212\242\346\210\277\345\255\220.md" index f2e254112172834de47c89ca5971c834fffb7642..c84c6ce5c6adc45a28ee18d1b1e8bbceec051a44 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\212\242\346\210\277\345\255\220.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\212\242\346\210\277\345\255\220.md" @@ -257,8 +257,16 @@ int[] dp(TreeNode root) {

- ======其他语言代码====== + +[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] +} +``` + + + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\344\274\230\345\255\220\347\273\223\346\236\204.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\344\274\230\345\255\220\347\273\223\346\236\204.md" index 808f6a6190f089bb1c44eb68560a2e75aa947c2a..718bd302d0e159c6cf50c49a5755433fa218e4d6 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\344\274\230\345\255\220\347\273\223\346\236\204.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\344\274\230\345\255\220\347\273\223\346\236\204.md" @@ -156,5 +156,38 @@ for (int i = 1; i < m; i++)

+======其他语言代码====== + +### 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] + } +} +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.md" index 7117ba90a21bc7b5ca43771f341be8525a815c5e..3c7085cba91c2a216693c5b5c66eab8ac1d9581f 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227.md" @@ -1,4 +1,4 @@ -# 最长公共子序列 +# 最长公共子序列

@@ -146,9 +146,11 @@ else:

- ======其他语言代码====== +[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] +}; + +``` diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\347\274\226\350\276\221\350\267\235\347\246\273.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\347\274\226\350\276\221\350\267\235\347\246\273.md" index 5d634fb055522df9353e9c7605e07203c32ea5b6..0eafdd48ec59fc04485560a5c7479d0e4baaf5ea 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\347\274\226\350\276\221\350\267\235\347\246\273.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\347\274\226\350\276\221\350\267\235\347\246\273.md" @@ -290,11 +290,13 @@ class Node {

- ======其他语言代码====== -[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] -```` \ No newline at end of file +```` + + + +### 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]; +}; + +``` + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\350\264\252\345\277\203\347\256\227\346\263\225\344\271\213\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\350\264\252\345\277\203\347\256\227\346\263\225\344\271\213\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230.md" index 268654b4e55904a23dde71120229f949e7880181..c81fe9aed256392212cc9597d2481e33300c05b4 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\350\264\252\345\277\203\347\256\227\346\263\225\344\271\213\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\350\264\252\345\277\203\347\256\227\346\263\225\344\271\213\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230.md" @@ -1,4 +1,4 @@ -# 贪心算法之区间调度问题 +# 贪心算法之区间调度问题

@@ -157,9 +157,12 @@ int findMinArrowShots(int[][] intvs) {

- ======其他语言代码====== +[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 -``` \ No newline at end of file +``` + + + +### 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; +}; +``` + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\350\277\233\351\230\266.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\350\277\233\351\230\266.md" index 53ebb0211269ebd71315917af923fe91300623f4..a7e58354aab215757e5b1b29a0221ebd7c4b6ab8 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\350\277\233\351\230\266.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\350\277\233\351\230\266.md" @@ -295,4 +295,40 @@ while (lo < hi) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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; +}; +``` + diff --git "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\351\227\256\351\242\230.md" "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\351\227\256\351\242\230.md" index af76a51226413bb4a165e5dab89a20c94642d0b8..c7430f7c3b2839ae3299ed04bbf87363b2f2845d 100644 --- "a/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\351\227\256\351\242\230.md" +++ "b/\345\212\250\346\200\201\350\247\204\345\210\222\347\263\273\345\210\227/\351\253\230\346\245\274\346\211\224\351\270\241\350\233\213\351\227\256\351\242\230.md" @@ -76,7 +76,7 @@ PS:这有点像 Big O 表示法计算​算法的复杂度。 **「选择」其实就是去选择哪层楼扔鸡蛋**。回顾刚才的线性扫描和二分思路,二分查找每次选择到楼层区间的中间去扔鸡蛋,而线性扫描选择一层层向上测试。不同的选择会造成状态的转移。 现在明确了「状态」和「选择」,**动态规划的基本思路就形成了**:肯定是个二维的 `dp` 数组或者带有两个状态参数的 `dp` 函数来表示状态转移;外加一个 for 循环来遍历所有选择,择最优的选择更新状态: - + ```python # 当前状态为 K 个鸡蛋,面对 N 层楼 # 返回这个状态下的最优结果 @@ -261,4 +261,110 @@ def superEggDrop(self, K: int, N: int) -> int:

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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); +}; + +``` + diff --git "a/\346\212\200\346\234\257/linuxshell.md" "b/\346\212\200\346\234\257/linuxshell.md" index 055b0c63543d0da094d5a9fbe6839e30f1b22347..db6d15b61865af8a09cebfa0787540675e0f166f 100644 --- "a/\346\212\200\346\234\257/linuxshell.md" +++ "b/\346\212\200\346\234\257/linuxshell.md" @@ -103,10 +103,10 @@ $ logout 类似的,还有一种后台运行常用的做法是这样: ```shell -$ nohub some_cmd & +$ nohup some_cmd & ``` -`nohub`命令也是类似的原理,不过通过我的测试,还是`(cmd &)`这种形式更加稳定。 +`nohup`命令也是类似的原理,不过通过我的测试,还是`(cmd &)`这种形式更加稳定。 ### 三、单引号和双引号的区别 @@ -157,4 +157,4 @@ $ sudo /home/fdl/bin/connect.sh

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\345\240\206\350\257\246\350\247\243\345\256\236\347\216\260\344\274\230\345\205\210\347\272\247\351\230\237\345\210\227.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\345\240\206\350\257\246\350\247\243\345\256\236\347\216\260\344\274\230\345\205\210\347\272\247\351\230\237\345\210\227.md" index 72c98cfe343300165355ba7ef0fe68b52d574bbc..0b497b0e84a93f237e87090f77d2cb65163043c6 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\345\240\206\350\257\246\350\247\243\345\256\236\347\216\260\344\274\230\345\205\210\347\272\247\351\230\237\345\210\227.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\345\240\206\350\257\246\350\247\243\345\256\236\347\216\260\344\274\230\345\205\210\347\272\247\351\230\237\345\210\227.md" @@ -238,5 +238,122 @@ public Key delMax() {

+======其他语言代码====== + +### 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; +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\346\223\215\344\275\234\351\233\206\351\224\246.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\346\223\215\344\275\234\351\233\206\351\224\246.md" index 8aef92e4683faeaf47103da254f0a5aaf9dfc189..00ef773e6e5d7d7d4dfba6436b35271b1904be18 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\346\223\215\344\275\234\351\233\206\351\224\246.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\346\223\215\344\275\234\351\233\206\351\224\246.md" @@ -1,4 +1,4 @@ -# 二叉搜索树操作集锦 +# 二叉搜索树操作集锦

@@ -121,7 +121,6 @@ boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) { } ``` - **一、在 BST 中查找一个数是否存在** 根据我们的指导思想,可以这样写代码: @@ -310,7 +309,18 @@ void BST(TreeNode root, int target) {

-======其他语言代码====== + +======其他语言代码====== + +[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++ @@ -348,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代码: @@ -400,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 @@ -448,4 +496,225 @@ class Solution: while node.right: node = node.right return node -``` \ No newline at end of file +``` + +### java +``` +/** +* 第【98】题的扩展解法: +* 对于BST,有一个重要的性质,即“BST的中序遍历是单调递增的”。抓住这个性质,我们可以通过中序遍历来判断该二叉树是不是BST。 +* 我们定义preNode节点表示上一个遍历的节点,在中序遍历的时候,比较当前节点和preNode节点的大小,一旦有节点小于或等于前一个节点,则不满足BST的规则,直接返回false,否则遍历结束,返回true。 +*/ +TreeNode preNode = null; +public boolean isValidBST(TreeNode root) { + if (root == null) return true; + + boolean leftRes = isValidBST(root.left); + + if (preNode != null && root.val <= preNode.val) { + return false; + } + preNode = root; + + boolean rightRes = isValidBST(root.right); + + 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 + } +}; +``` diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\346\240\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\346\240\210.md" index 93a77f1e63fbfd060b778a5e1bc8453b9674024f..b605baf4100d7da6db63f0b285278bf4b3d42111 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\346\240\210.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\346\240\210.md" @@ -180,9 +180,16 @@ vector nextGreaterElements(vector& nums) {

- ======其他语言代码====== +[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; } } -``` \ No newline at end of file +``` + +[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 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 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, + 1:1 + 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; +} +``` + +部分做题规律如下,仅供做题套路参考,实际可以自由发挥。 + +当前项向左找第一个比自己大的位置:从左向右维护一个单调递减栈 +当前项向左找第一个比自己小的位置:从左向右维护一个单调递增栈 +当前项向右找第一个比自己大的位置:从右向左维护一个单调递减栈 +当前项向右找第一个比自己小的位置:从右向左维护一个单调递增栈 + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\351\230\237\345\210\227.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\351\230\237\345\210\227.md" index 54f5e6ef0ce68069865b4fe52c3b8ee10c191b19..0e03a7c77eb821b7e735cb6426b6cb87de3bad70 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\351\230\237\345\210\227.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\215\225\350\260\203\351\230\237\345\210\227.md" @@ -209,10 +209,11 @@ vector maxSlidingWindow(vector& nums, int k) {

- ======其他语言代码====== -### 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; + +}; +``` + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\256\236\347\216\260\350\256\241\347\256\227\345\231\250.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\256\236\347\216\260\350\256\241\347\256\227\345\231\250.md" index 384a86f92a5eade5aa1db36660de010dad3e9b03..f556cfedda59678057caf779e13e6580e65bf309 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\256\236\347\216\260\350\256\241\347\256\227\345\231\250.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\345\256\236\347\216\260\350\256\241\347\256\227\345\231\250.md" @@ -26,7 +26,7 @@ **-----------** 我们最终要实现的计算器功能如下: - + 1、输入一个字符串,可以包含`+ - * /`、数字、括号以及空格,你的算法返回运算结果。 2、要符合运算法则,括号的优先级最高,先乘除后加减。 @@ -306,4 +306,108 @@ def calculate(s: str) -> int:

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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 + +``` + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\350\256\276\350\256\241Twitter.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\350\256\276\350\256\241Twitter.md" index 96db45541c5c4b60f8fca783d8eb114709a736b5..ae4c03171e58a185e10d30a854f8869262a7d528 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\350\256\276\350\256\241Twitter.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\350\256\276\350\256\241Twitter.md" @@ -301,9 +301,12 @@ PS:本文前两张图片和 GIF 是我第一次尝试用平板的绘图软件

- ======其他语言代码====== +[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); } }; -``` \ No newline at end of file +``` + + + +### 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) +``` + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\200\222\345\275\222\345\217\215\350\275\254\351\223\276\350\241\250\347\232\204\344\270\200\351\203\250\345\210\206.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\200\222\345\275\222\345\217\215\350\275\254\351\223\276\350\241\250\347\232\204\344\270\200\351\203\250\345\210\206.md" index ed08996a39bef67759a71d500e8731690c7deef5..fbbb36b5261d593302f60b33b4a8887733505a57 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\200\222\345\275\222\345\217\215\350\275\254\351\223\276\350\241\250\347\232\204\344\270\200\351\203\250\345\210\206.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\200\222\345\275\222\345\217\215\350\275\254\351\223\276\350\241\250\347\232\204\344\270\200\351\203\250\345\210\206.md" @@ -217,41 +217,187 @@ ListNode reverseBetween(ListNode head, int m, int n) {

- ======其他语言代码====== +[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;inext; + //获取待反转节点的第一个节点 + head=pre->next; + //迭代反转n-m次,将head的next节点移动到需要反转链表部分的首部 + for(int i=m;inext; + 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; - + 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); + # 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; +}; +``` + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" index a9d34465f37ae602943848a02ad0b3aededcaf2c..6b2513405d1c442bb0e94e52f8db0b7609c6eee3 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\347\263\273\345\210\227/\351\230\237\345\210\227\345\256\236\347\216\260\346\240\210\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" @@ -230,4 +230,147 @@ public boolean empty() {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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() + */ +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/FloodFill\347\256\227\346\263\225\350\257\246\350\247\243\345\217\212\345\272\224\347\224\250.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/FloodFill\347\256\227\346\263\225\350\257\246\350\247\243\345\217\212\345\272\224\347\224\250.md" index 28b0fa5a4b4e4c46c773c3612184709fa9963645..358519045eeef5f16dcbe93f342fb9dc85059925 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/FloodFill\347\256\227\346\263\225\350\257\246\350\247\243\345\217\212\345\272\224\347\224\250.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/FloodFill\347\256\227\346\263\225\350\257\246\350\247\243\345\217\212\345\272\224\347\224\250.md" @@ -239,4 +239,89 @@ int fill(int[][] image, int x, int y,

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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; +} +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\345\272\224\347\224\250.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\345\272\224\347\224\250.md" index 1217326aed926d8c44eecf2c96d6f2628260f01b..5e63c07f4b51ad6726ca0975c9859eceae33b824 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\345\272\224\347\224\250.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\345\272\224\347\224\250.md" @@ -18,7 +18,7 @@ [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/) @@ -251,9 +251,18 @@ boolean equationsPossible(String[] equations) {

- ======其他语言代码====== +[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; +}; +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\350\257\246\350\247\243.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\350\257\246\350\247\243.md" index 32e37de8aa5c7972d616d3554ac59791412c67bf..b431e57365c7a42ea4cf5e4520189cdfe010a17f 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\350\257\246\350\247\243.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/UnionFind\347\256\227\346\263\225\350\257\246\350\247\243.md" @@ -317,4 +317,138 @@ Union-Find 算法的复杂度可以这样分析:构造函数初始化数据结

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### 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; + }; +} +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/twoSum\351\227\256\351\242\230\347\232\204\346\240\270\345\277\203\346\200\235\346\203\263.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/twoSum\351\227\256\351\242\230\347\232\204\346\240\270\345\277\203\346\200\235\346\203\263.md" index a1a92c5546d436f29969f7fc5a0bdb0b73a48761..9f189b47c38d9d0f018a0e6a9fb3c43fe5b088c1 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/twoSum\351\227\256\351\242\230\347\232\204\346\240\270\345\277\203\346\200\235\346\203\263.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/twoSum\351\227\256\351\242\230\347\232\204\346\240\270\345\277\203\346\200\235\346\203\263.md" @@ -184,14 +184,21 @@ int[] twoSum(int[] nums, int target) {

- ======其他语言代码====== +[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 +```python class Solution: def twoSum(self, nums, target): """ @@ -211,3 +218,85 @@ class Solution: # 如果不存在的话继续处理剩余的数 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); + } +} +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\350\257\246\350\247\243.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\350\257\246\350\247\243.md" index 278f7d3902cd7901042aae68f68e29ff177be56a..def1f6f9d6727e8c9cd353c8b37740ae319feced 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\350\257\246\350\247\243.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\350\257\246\350\247\243.md" @@ -509,8 +509,16 @@ int right_bound(int[] nums, int target) {

+======其他语言代码====== + +[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 @@ -605,3 +613,207 @@ def right_bound(nums, 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; +}; + +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\277\241\345\260\201\345\265\214\345\245\227\351\227\256\351\242\230.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\277\241\345\260\201\345\265\214\345\245\227\351\227\256\351\242\230.md" index a31453cd66fd7bff0d1996e28d3cd627e0835c66..c4144b812d0ff3ef3e2fcc9ec53ab3760d5095d4 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\277\241\345\260\201\345\265\214\345\245\227\351\227\256\351\242\230.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\344\277\241\345\260\201\345\265\214\345\245\227\351\227\256\351\242\230.md" @@ -135,4 +135,123 @@ public int lengthOfLIS(int[] nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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); +}; + +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\211\215\347\274\200\345\222\214\346\212\200\345\267\247.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\211\215\347\274\200\345\222\214\346\212\200\345\267\247.md" index 5a3d3ce1c5c145106dfaab238b6a0a777c83ed98..9994d14cc84f2506bf2c291c864a463b089fa56c 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\211\215\347\274\200\345\222\214\346\212\200\345\267\247.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\211\215\347\274\200\345\222\214\346\212\200\345\267\247.md" @@ -159,4 +159,80 @@ for (int i = 1; i < count.length; i++)

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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; +} +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\344\272\244\351\233\206\351\227\256\351\242\230.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\344\272\244\351\233\206\351\227\256\351\242\230.md" index 7e1aebd6edff6bc49b0bdf89a6ea366c54f646e3..34a4d00ab077e2fca8d7f783b510bfe63f70f1b6 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\344\272\244\351\233\206\351\227\256\351\242\230.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\344\272\244\351\233\206\351\227\256\351\242\230.md" @@ -132,5 +132,77 @@ def intervalIntersection(A, B):

+======其他语言代码====== + +[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 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; +}; +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230\344\271\213\345\214\272\351\227\264\345\220\210\345\271\266.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230\344\271\213\345\214\272\351\227\264\345\220\210\345\271\266.md" index 1aaaa17de3726cce749b49b442ae6417dbd1f657..7cffdf4d15828f7ebc3c237b134ccf4f69724241 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230\344\271\213\345\214\272\351\227\264\345\220\210\345\271\266.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\214\272\351\227\264\350\260\203\345\272\246\351\227\256\351\242\230\344\271\213\345\214\272\351\227\264\345\220\210\345\271\266.md" @@ -92,7 +92,13 @@ def merge(intervals):

======其他语言代码====== -~~~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> merge(vector>& intervals) { + // len 为 intervals 的长度 + int len = intervals.size(); + if (len < 1) + return {}; + + // 按区间的 start 升序排列 + sort(intervals.begin(), intervals.end()); + + // 初始化 res 数组 + vector> res; + res.push_back(intervals[0]); + + for (int i = 1; i < len; i++) { + vector 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 +} +``` diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\217\214\346\214\207\351\222\210\346\212\200\345\267\247.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\217\214\346\214\207\351\222\210\346\212\200\345\267\247.md" index 11f1b56ab80c1b82eca0fea335c96cc198d04ec9..6ee321e3348e09a8a6b0e915383d27a389af5c19 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\217\214\346\214\207\351\222\210\346\212\200\345\267\247.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\217\214\346\214\207\351\222\210\346\212\200\345\267\247.md" @@ -18,7 +18,7 @@ [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) @@ -234,9 +234,121 @@ void reverse(int[] nums) {

- ======其他语言代码====== +[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)。 \ No newline at end of file diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\233\236\346\272\257\347\256\227\346\263\225\350\257\246\350\247\243\344\277\256\350\256\242\347\211\210.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\233\236\346\272\257\347\256\227\346\263\225\350\257\246\350\247\243\344\277\256\350\256\242\347\211\210.md" index 53e180cc6f0f2873b0e423d7057af97091360fe5..e6f2d2308e53a404eb5c7c49eed0ba2c3307a9c8 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\233\236\346\272\257\347\256\227\346\263\225\350\257\246\350\247\243\344\277\256\350\256\242\347\211\210.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\233\236\346\272\257\347\256\227\346\263\225\350\257\246\350\247\243\344\277\256\350\256\242\347\211\210.md" @@ -309,5 +309,235 @@ def backtrack(...):

+======其他语言代码====== + +[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> res = new LinkedList<>(); + +/* 主函数,输入一组不重复的数字,返回它们的全排列 */ +List> permute(int[] nums) { + // 记录「路径」 + LinkedList track = new LinkedList<>(); + backtrack(nums, track); + return res; +} + +// 路径:记录在 track 中 +// 选择列表:nums 中不存在于 track 的那些元素 +// 结束条件:nums 中的元素全都在 track 中出现 +void backtrack(int[] nums, LinkedList 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> res = new ArrayList<>(); + + // 输入棋盘边长 n,返回所有合法的放置 + public List> 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 construct(char[][] chess) { + // 数组转List + List 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; +}; +``` + + -======其他语言代码====== \ No newline at end of file diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\255\227\347\254\246\344\270\262\344\271\230\346\263\225.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\255\227\347\254\246\344\270\262\344\271\230\346\263\225.md" index 966aafd5ec10231b3ed632252f05fa0c7b0058af..2fc5a2325c6eb56041fc5b0dfc288148a413bef5 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\255\227\347\254\246\344\270\262\344\271\230\346\263\225.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\255\227\347\254\246\344\270\262\344\271\230\346\263\225.md" @@ -21,7 +21,7 @@ **-----------** 对于比较小的数字,做运算可以直接使用编程语言提供的运算符,但是如果相乘的两个因数非常大,语言提供的数据类型可能就会溢出。一种替代方案就是,运算数以字符串的形式输入,然后模仿我们小学学习的乘法算术过程计算出结果,并且也用字符串表示。 - + ![](../pictures/%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%98%E6%B3%95/title.png) 需要注意的是,`num1` 和 `num2` 可以非常长,所以不可以把他们直接转成整型然后运算,唯一的思路就是模仿我们手算乘法。 @@ -100,9 +100,12 @@ string multiply(string num1, string num2) {

- ======其他语言代码====== +[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(); } -``` \ No newline at end of file +``` + + + +### 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'; +}; +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\270\270\347\224\250\347\232\204\344\275\215\346\223\215\344\275\234.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\270\270\347\224\250\347\232\204\344\275\215\346\223\215\344\275\234.md" index b56e56c316babb4d52ffc9255e9a2eda04ce9383..6ce01a85092ad9445a25f5324e866b4ec9d2f761 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\270\270\347\224\250\347\232\204\344\275\215\346\223\215\344\275\234.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\345\270\270\347\224\250\347\232\204\344\275\215\346\223\215\344\275\234.md" @@ -171,9 +171,16 @@ http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel

- ======其他语言代码====== +[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 @@ -193,4 +200,53 @@ class Solution: # 当二进制串全消除完之后,返回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; +} +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\264\227\347\211\214\347\256\227\346\263\225.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\264\227\347\211\214\347\256\227\346\263\225.md" index 0ce3b64cf7fe17f23ba9523d162df28888a5aa5b..90339a757e9a6a5ecf02592e5f41c35cd6826f62 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\264\227\347\211\214\347\256\227\346\263\225.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\264\227\347\211\214\347\256\227\346\263\225.md" @@ -210,5 +210,98 @@ for (int feq : count)

+======其他语言代码====== + +[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); + } +} +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\273\221\345\212\250\347\252\227\345\217\243\346\212\200\345\267\247.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\273\221\345\212\250\347\252\227\345\217\243\346\212\200\345\267\247.md" index 2174689df6e2b6231a77139462ccc3354aac1877..f710f7ccd447a7f91ad96543eeb34bf38e7d6fb9 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\273\221\345\212\250\347\252\227\345\217\243\346\212\200\345\267\247.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\346\273\221\345\212\250\347\252\227\345\217\243\346\212\200\345\267\247.md" @@ -371,9 +371,22 @@ class Solution: return s[start:start+min_len] if min_len != float("Inf") else "" ``` +**_____________** + +**刷算法,学套路,认准 labuladong,公众号和 [在线电子书](https://labuladong.gitbook.io/algo/) 持续更新最新文章**。 + +**本小抄即将出版,微信扫码关注公众号,后台回复「小抄」限时免费获取,回复「进群」可进刷题群一起刷题,带你搞定 LeetCode**。 + +

+ +

+ +======其他语言代码====== + +### python 第3题 Python3 代码(提供: [FaDrYL](https://github.com/FaDrYL) ): -```Python3 +```python def lengthOfLongestSubstring(self, s: str) -> int: # 子字符串 sub = "" @@ -396,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; +}; +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\347\203\247\351\245\274\346\216\222\345\272\217.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\347\203\247\351\245\274\346\216\222\345\272\217.md" index 22ed46bea93a17e20867fa9c37bfa8492ff3d3bb..cf54566aaa495b20400d15e1c9afa7251516e9e0 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\347\203\247\351\245\274\346\216\222\345\272\217.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\347\203\247\351\245\274\346\216\222\345\272\217.md" @@ -148,9 +148,12 @@ void reverse(int[] arr, int i, int j) {

- ======其他语言代码====== +[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 +}; +``` + diff --git "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\351\200\222\345\275\222\350\257\246\350\247\243.md" "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\351\200\222\345\275\222\350\257\246\350\247\243.md" index cc68a61e779970179a87c4277aacc86c89d1e9e6..992b8ecaa66682be4231cf083e2a5bcfd6b2ec98 100644 --- "a/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\351\200\222\345\275\222\350\257\246\350\247\243.md" +++ "b/\347\256\227\346\263\225\346\200\235\347\273\264\347\263\273\345\210\227/\351\200\222\345\275\222\350\257\246\350\247\243.md" @@ -275,4 +275,79 @@ https://leetcode.com/tag/divide-and-conquer/

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### 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); +} +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/LRU\347\256\227\346\263\225.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/LRU\347\256\227\346\263\225.md" index 5215f188a277dbf2bc01f93ae9cf5b495bd5eca2..ce6419d9228edaf8aecde595053b490df37a749f 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/LRU\347\256\227\346\263\225.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/LRU\347\256\227\346\263\225.md" @@ -345,9 +345,12 @@ class LRUCache {

- ======其他语言代码====== +[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); +} + +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/koko\345\201\267\351\246\231\350\225\211.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/koko\345\201\267\351\246\231\350\225\211.md" index 2322bba49f75ed8456c32c3325518f6dbb80620b..422c93ac0247c081a4f9f9f7294084a2cd492408 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/koko\345\201\267\351\246\231\350\225\211.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/koko\345\201\267\351\246\231\350\225\211.md" @@ -168,10 +168,15 @@ for (int i = 0; i < n; i++)

- ======其他语言代码====== -#### 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) +} +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/k\344\270\252\344\270\200\347\273\204\345\217\215\350\275\254\351\223\276\350\241\250.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/k\344\270\252\344\270\200\347\273\204\345\217\215\350\275\254\351\223\276\350\241\250.md" index 1766b4a159b118e642db0793bf743338ba3f269b..58d2e34641ca0a489d3b1d832957e8c318f354c7 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/k\344\270\252\344\270\200\347\273\204\345\217\215\350\275\254\351\223\276\350\241\250.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/k\344\270\252\344\270\200\347\273\204\345\217\215\350\275\254\351\223\276\350\241\250.md" @@ -156,4 +156,80 @@ ListNode reverseKGroup(ListNode head, int k) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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; +} +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" index af43a75da58eee6a91c3b1c4a75d4a3c66fd8608..6e5361d538f4c0672679ec2ae4934e433b761f9b 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\270\200\350\241\214\344\273\243\347\240\201\350\247\243\345\206\263\347\232\204\346\231\272\345\212\233\351\242\230.md" @@ -148,9 +148,18 @@ int bulbSwitch(int n) {

- ======其他语言代码====== +[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 @@ -171,6 +180,20 @@ class Solution: 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 @@ -184,14 +207,7 @@ public: ``` -由[JodyZ0203](https://github.com/JodyZ0203)提供 319. 灯泡开关 Python3 解法代码: -```Python -class Solution: - def bulbSwitch(self, n: int) -> int: - # 平方根电灯个数之后向下取整即可 - return floor(sqrt (n)) -``` 由[JodyZ0203](https://github.com/JodyZ0203)提供 319. 灯泡开关 C++ 解法代码: @@ -204,3 +220,42 @@ public: } }; ``` + + + +### 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)); +}; +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\345\210\244\345\256\232\345\255\220\345\272\217\345\210\227.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\345\210\244\345\256\232\345\255\220\345\272\217\345\210\227.md" index f5228915d0694515d4c7903a42ebe356c7567775..34715adb3fc71e0a1d02e0354b0473cbfdd3deab 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\345\210\244\345\256\232\345\255\220\345\272\217\345\210\227.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\344\272\214\345\210\206\346\237\245\346\211\276\345\210\244\345\256\232\345\255\220\345\272\217\345\210\227.md" @@ -167,10 +167,15 @@ boolean isSubsequence(String s, String t) {

+======其他语言代码====== -======其他语言代码====== -[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; +} +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\210\244\346\226\255\345\233\236\346\226\207\351\223\276\350\241\250.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\210\244\346\226\255\345\233\236\346\226\207\351\223\276\350\241\250.md" index bb1a6aa05febf2ec7b095b4162ef784014116697..068dec03e6298ec6e882396b125bb75faf10c570 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\210\244\346\226\255\345\233\236\346\226\207\351\223\276\350\241\250.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\210\244\346\226\255\345\233\236\346\226\207\351\223\276\350\241\250.md" @@ -236,36 +236,131 @@ p.next = reverse(q);

- ======其他语言代码====== -C++版本: +[234.回文链表](https://leetcode-cn.com/problems/palindrome-linked-list) + + + +### C++ + ```cpp - bool isPalindrome(ListNode* head) { - if (head == nullptr || head->next == nullptr) //为空或者只有一个节点时,直接判断为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; - 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即可 } + + 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; +} + ``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\220\210\346\263\225\346\213\254\345\217\267\345\210\244\345\256\232.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\220\210\346\263\225\346\213\254\345\217\267\345\210\244\345\256\232.md" index c183b510d67d9f69b4cb8f821a32dee0ceff95d0..6c2b5dee4bff1c97dd2a0c7c67bf636b36372525 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\220\210\346\263\225\346\213\254\345\217\267\345\210\244\345\256\232.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\220\210\346\263\225\346\213\254\345\217\267\345\210\244\345\256\232.md" @@ -111,10 +111,11 @@ char leftOf(char c) {

- ======其他语言代码====== -### 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 '['; +} +``` + + + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\246\202\344\275\225\345\216\273\351\231\244\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\246\202\344\275\225\345\216\273\351\231\244\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.md" index 1e7d81bcf8c9456e229acfcbcdbc072e4a5f60ec..984240b46ae3679b110ebd33fb3bfa577bd5fe5d 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\246\202\344\275\225\345\216\273\351\231\244\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\246\202\344\275\225\345\216\273\351\231\244\346\234\211\345\272\217\346\225\260\347\273\204\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240.md" @@ -75,6 +75,14 @@ ListNode deleteDuplicates(ListNode head) { ======其他语言代码====== +[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 def removeDuplicates(self, nums: List[int]) -> int: n = len(nums) @@ -113,4 +121,70 @@ def deleteDuplicates(self, head: ListNode) -> ListNode: # 断开与后面重复元素的连接 slow.next = None return head -``` \ No newline at end of file +``` + + + +### javascript + +[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; +} +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\255\220\351\233\206\346\216\222\345\210\227\347\273\204\345\220\210.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\255\220\351\233\206\346\216\222\345\210\227\347\273\204\345\220\210.md" index cda2e83e18635aec8954cee97ec32b10a59d2f66..67a78ff744ee53ab576e79c9cd8ff74b3e05601a 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\255\220\351\233\206\346\216\222\345\210\227\347\273\204\345\220\210.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\255\220\351\233\206\346\216\222\345\210\227\347\273\204\345\220\210.md" @@ -285,6 +285,16 @@ void backtrack(int[] nums, LinkedList track) {

======其他语言代码====== +[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> res = new ArrayList<>(); - public List> permute(int[] nums) { - res.clear(); - dfs(nums, 0);// - return res; + List> res = new ArrayList<>(); + public List> permute(int[] nums) { + res.clear(); + dfs(nums, 0);// + return res; + } + + public void dfs(int[] n, int start) {//start表示要被替换元素的位置 + if( start >= n.length) { + List list = new ArrayList(); + 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; + } + } +} + +``` + + + +### 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; } - public void dfs(int[] n, int start) {//start表示要被替换元素的位置 - if( start >= n.length) { - List list = new ArrayList(); - for(int i : n) { - list.add(i); - } - res.add(list); - return; - } + // 做选择 + 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)) { - 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; + // 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; +}; +``` -``` \ No newline at end of file diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\272\247\344\275\215\350\260\203\345\272\246.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\272\247\344\275\215\350\260\203\345\272\246.md" index 4fbca5f8bb09a29e092bc33be850475a6b11fa78..16a2bde5a1ef1a959295dc7797b04e99c9085cef 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\272\247\344\275\215\350\260\203\345\272\246.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\345\272\247\344\275\215\350\260\203\345\272\246.md" @@ -232,5 +232,77 @@ private int distance(int[] intv) {

+======其他语言代码====== + +[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) + }; +} +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\211\223\345\215\260\347\264\240\346\225\260.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\211\223\345\215\260\347\264\240\346\225\260.md" index 8e71f3e0e7119e05d64abe094a9537016088bf36..a4fe2ebea5b4fcd1998381b5e7d8e9a874bc57e9 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\211\223\345\215\260\347\264\240\346\225\260.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\211\223\345\215\260\347\264\240\346\225\260.md" @@ -177,10 +177,11 @@ int countPrimes(int n) {

- ======其他语言代码====== -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*2,i*3等,在之前的循环中已经验证了 { - for(int j = i * i; j < n; j += i) //内循环优化,i*i之前的比如i*2,i*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*2,i*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; }; ``` diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\216\245\351\233\250\346\260\264.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\216\245\351\233\250\346\260\264.md" index 87357fe25086f353c3753a88a2e82ae14dd31e24..feeed735e10b356c7a80c08a8e6227b911b90443 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\216\245\351\233\250\346\260\264.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\216\245\351\233\250\346\260\264.md" @@ -210,9 +210,14 @@ if (l_max < r_max) {

- ======其他语言代码====== +[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; } } -``` \ No newline at end of file +``` + + + +### 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; +}; +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.md" index 58732779d27c9b5448b2b40f6a287961f4bd8295..1f526395c480fc5910bd7e0dc5e48b3e94f2cded 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.md" @@ -130,9 +130,12 @@ string longestPalindrome(string s) {

- ======其他语言代码====== +[5.最长回文子串](https://leetcode-cn.com/problems/longest-palindromic-substring) + +### java + [cchromt](https://github.com/cchroot) 提供 Java 代码: ```java @@ -167,4 +170,79 @@ class Solution { } ``` + +[enrilwang](https://github.com/enrilwang) 提供 Python 代码: + +```python +# 中心扩展算法 +class Solution: + def longestPalindrome(self, s: str) -> str: + #用n来装字符串长度,res来装答案 + n = len(s) + res = str() + #字符串长度小于2,就返回本身 + if n < 2: return s + for i in range(n-1): + #oddstr是以i为中心的最长回文子串 + oddstr = self.centerExtend(s,i,i) + #evenstr是以i和i+1为中心的最长回文子串 + evenstr = self.centerExtend(s,i,i+1) + temp = oddstr if len(oddstr)>len(evenstr) else evenstr + if len(temp)>len(res):res=temp + + return res + + def centerExtend(self,s:str,left,right)->str: + + while left >= 0 and right < len(s) and s[left] == s[right]: + left -= 1 + right += 1 + #这里要注意,跳出while循环时,恰好s[left] != s[right] + return s[left+1:right] + + +``` + + 做完这题,大家可以去看看 [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) +} +``` + + + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\260\264\345\241\230\346\212\275\346\240\267.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\260\264\345\241\230\346\212\275\346\240\267.md" index e107ef5d337a6b849cfe01ca6ef5a5e8865af9ba..269d5747d5775ab57ee79fe34d680ab15ce6e859 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\260\264\345\241\230\346\212\275\346\240\267.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\260\264\345\241\230\346\212\275\346\240\267.md" @@ -147,5 +147,99 @@ $$ -->

+======其他语言代码====== + +[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 +}; +``` -======其他语言代码====== \ No newline at end of file diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\266\210\345\244\261\347\232\204\345\205\203\347\264\240.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\266\210\345\244\261\347\232\204\345\205\203\347\264\240.md" index e434b5032aa053e7a07cd6e1d609cc2b5059d2f7..05a9c1456cf130878268888d7bb5646173d101e1 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\266\210\345\244\261\347\232\204\345\205\203\347\264\240.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\346\266\210\345\244\261\347\232\204\345\205\203\347\264\240.md" @@ -132,9 +132,45 @@ public int missingNumber(int[] nums) {

- ======其他语言代码====== +[剑指 Offer 53 - II. 0~n-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& nums) { ``` + +### javascript + +[传送门:剑指 Offer 53 - II. 0~n-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; +}; + +``` + diff --git "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\347\274\272\345\244\261\345\222\214\351\207\215\345\244\215\347\232\204\345\205\203\347\264\240.md" "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\347\274\272\345\244\261\345\222\214\351\207\215\345\244\215\347\232\204\345\205\203\347\264\240.md" index defccd33c9be92b7f91fdfaa7413b9032364350d..39ad698e6a5022e4aec00679b11812db6eec1019 100644 --- "a/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\347\274\272\345\244\261\345\222\214\351\207\215\345\244\215\347\232\204\345\205\203\347\264\240.md" +++ "b/\351\253\230\351\242\221\351\235\242\350\257\225\347\263\273\345\210\227/\347\274\272\345\244\261\345\222\214\351\207\215\345\244\215\347\232\204\345\205\203\347\264\240.md" @@ -138,9 +138,14 @@ vector findErrorNums(vector& nums) {

- ======其他语言代码====== +[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] +}; +``` +