diff --git a/README.md b/README.md index c96900239033fe40ae5b93db0e83136234a47667..0a74a829e4a4336ddcab054c0bc78c5e16604d9c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ English version repo and Gitbook is on [english branch](https://github.com/labul # labuladong 的算法小抄

-Website +Website GitHub

@@ -14,6 +14,10 @@ English version repo and Gitbook is on [english branch](https://github.com/labul

+![](pictures/souyisou.png) + +好消息,《labuladong 的算法小抄》纸质书出版啦!关注公众号查看详情👆 +

@@ -25,27 +29,36 @@ English version repo and Gitbook is on [english branch](https://github.com/labul 只想要答案的话很容易,题目评论区五花八门的答案,动不动就秀 python 一行代码解决,有那么多人点赞。问题是,你去做算法题,是去学习编程语言的奇技淫巧的,还是学习算法思维的呢?你的快乐,到底源自复制别人的一行代码通过测试,已完成题目 +1,还是源自自己通过逻辑推理和算法框架不看答案写出解法? -网上总有大佬喷我,说我写这玩意太基础了,根本没必要啰嗦。我只能说大家刷算法就是找工作吃饭的,不是打竞赛的,我也是一路摸爬滚打过来的,我们要的是清楚明白有所得,不是故弄玄虚无所指。不想办法做到通俗易懂,难道要上来先把《算法导论》吹上天,然后把人家都心怀敬仰地劝退? +网上总有大佬喷我,说我写的东西太基础,要么说不能借助框架思维来学习算法。我只能说大家刷算法就是找工作吃饭的,不是打竞赛的,我也是一路摸爬滚打过来的,我们要的是清楚明白有所得,不是故弄玄虚无所指。 + +不想办法做到通俗易懂,难道要上来先把《算法导论》吹上天,然后把人家都心怀敬仰地劝退? **做啥事情做多了,都能发现套路的,我把各种算法套路框架总结出来,相信可以帮助其他人少走弯路**。我这个纯靠自学的小童鞋,花了一年时间刷题和总结,自己写了一份算法小抄,后面有目录,这里就不废话了。 ### 使用方法 -1、**先给本仓库点个 star,满足一下我的虚荣心**,文章质量绝对值你一个 star。我还在继续创作,给我一点继续写文的动力,感谢。 +**1、先给本仓库点个 star,满足一下我的虚荣心**,文章质量绝对值你一个 star。我还在继续创作,给我一点继续写文的动力,感谢。 + +**2、建议收藏我的在线网站,每篇文章开头都有对应的力扣题目链接,可以边看文章边刷题**: + +Gitbook 地址:https://labuladong.gitbook.io/algo + +GitBook 在国内访问速度很慢,且常被攻击,我特意部署了两个镜像站点,大家可根据网络情况自行选择: + +GitHub Pages 地址:https://labuladong.github.io/algo -2、**建议收藏我的 Gitbook 网站,每篇文章开头都有对应的力扣题目链接,可以边看文章边刷题**: +Gitee Pages 地址:https://labuladong.gitee.io/algo -Gitbook 地址:https://labuladong.gitbook.io/algo/ -3、建议关注我的公众号 **labuladong**,坚持高质量原创,说是最良心最硬核的技术公众号都不为过。本仓库的文章就是从公众号里整理出来的**一部分**内容,公众号后台回复关键词【电子书】可以获得这份小抄的完整版本;回复【加群】可以加入我们的刷题群,和大家一起讨论算法问题,分享内推机会: +**3、建议关注我的公众号 labuladong,坚持高质量原创,说是最良心最硬核的技术公众号都不为过**。本仓库的文章就是从公众号里整理出来的**一部分**内容,公众号可以查看更多内容;公众号后台回复关键词【加群】可以加入我们的刷题群,和大家一起讨论算法问题,分享内推机会:

-4、欢迎关注 [我的知乎](https://www.zhihu.com/people/labuladong)。 +**4、欢迎关注 [我的知乎](https://www.zhihu.com/people/labuladong)**。 -我一直在写优质文章,但是后续的文章只发布到公众号/gitbook/知乎,不能开放到 GitHub。因为本仓库太火了,很多人直接拿我的文章去开付费专栏,价格还不便宜,我这免费写给您看,何必掏冤枉钱呢?所以多多关注本作者,多多宣传,谁也不希望劣币驱逐良币不是么? +我一直在写优质文章,但是后续的文章只发布到公众号/网站/知乎,不能开放到 GitHub。因为本仓库太火了,很多人直接拿我的文章去开付费专栏,价格还不便宜,我这免费写给您看,何必掏冤枉钱呢?所以多多关注本作者,多多宣传,谁也不希望劣币驱逐良币不是么? 其他的先不多说了,直接上干货吧,我们一起搞定 LeetCode,感受一下支配算法的乐趣。 diff --git "a/\345\207\272\347\211\210\346\216\250\345\271\2771.jpeg" "b/\345\207\272\347\211\210\346\216\250\345\271\2771.jpeg" new file mode 100644 index 0000000000000000000000000000000000000000..6205bfee59f2363a6117d26ac667b50d06cb43cf Binary files /dev/null and "b/\345\207\272\347\211\210\346\216\250\345\271\2771.jpeg" differ 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 a0cc4cef17ac792186ecfa556ea996fa0ef3c769..5cde805a167f69b03fb2b34d350b943fb129466b 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" @@ -431,4 +431,40 @@ KMP 算法也就是动态规划那点事,我们的公众号文章目录有一

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== +[MoguCloud](https://github.com/MoguCloud) 提供 实现 strStr() 的 Python 完整代码: +```py +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + # 边界条件判断 + if not needle: + return 0 + pat = needle + txt = haystack + + M = len(pat) + # dp[状态][字符] = 下个状态 + dp = [[0 for _ in range(256)] for _ in pat] + # base case + dp[0][ord(pat[0])] = 1 + # 影子状态 X 初始化为 0 + X = 0 + for j in range(1, M): + for c in range(256): + dp[j][c] = dp[X][c] + dp[j][ord(pat[j])] = j + 1 + # 更新影子状态 + X = dp[X][ord(pat[j])] + + 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 +``` 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 9f52fa49297363b9cb579eef2bf1ed9847b8fcd1..f9082d8def88e8632697704931409c8ab0f0659a 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" @@ -22,7 +22,7 @@ 上一篇文章 [几道智力题](https://labuladong.gitbook.io/algo) 中讨论到一个有趣的「石头游戏」,通过题目的限制条件,这个游戏是先手必胜的。但是智力题终究是智力题,真正的算法问题肯定不会是投机取巧能搞定的。所以,本文就借石头游戏来讲讲「假设两个人都足够聪明,最后谁会获胜」这一类问题该如何用动态规划算法解决。 -博弈类问题的套路都差不多,下文举例讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了这个技巧以后,别人再问你什么俩海盗分宝石,俩人拿硬币的问题,你就告诉别人:我懒得想,直接给你写个算法算一下得了。 +博弈类问题的套路都差不多,下文参考 [这个 YouTube 视频](https://www.youtube.com/watch?v=WxpIHvsu1RI) 的思路讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了这个技巧以后,别人再问你什么俩海盗分宝石,俩人拿硬币的问题,你就告诉别人:我懒得想,直接给你写个算法算一下得了。 我们「石头游戏」改的更具有一般性: @@ -215,4 +215,116 @@ int stoneGame(int[] piles) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + + + + + +* python3版本 + +由[SCUHZS](https://github.com/brucecat)提供 + +这里采取的是三维的做法 + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + n = len(piles) + + # 初始化一个n*n的矩阵 dp数组 + dp = [[None] * n for i in range(0, n)] + + # 在三角区域填充 + for i in range(n): + for j in range(i, n): + dp[i][j] = [0, 0] + + # 填入base case + for i in range(0, n): + dp[i][i][0] = piles[i] + dp[i][i][1] = 0 + + # 斜着遍历数组 + for l in range(2, n + 1): + for i in range(0, n-l+1): + j = l + i - 1 + + + # 先手选择最左边或最右边的分数 + left = piles[i] + dp[i + 1][j][1] + 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] + + res = dp[0][n - 1] + + return res[0] - res[1] > 0 + +``` + + + +压缩成一维数组,以减小空间复杂度,做法如下。 + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + dp = piles.copy() + + for i in range(len(piles) - 1, -1, -1): # 从下往上遍历 + for j in range(i, len(piles)): # 从前往后遍历 + dp[j] = max(piles[i] - dp[j], piles[j] - dp[j - 1]) # 计算之后覆盖一维数组的对应位置 + + return dp[len(piles) - 1] > 0 + + +``` + +* C++ 版本 + +由 [TCeason](https://github.com/TCeason) 提供 + +这里采用 hash map 来解决问题 + +```cpp +class Solution { +public: + unordered_map memo; + + int dfs(vector &piles, int index) { + // 从两边向中间获取 + // index 值为 1/2 piles.size() 时可以停止算法 + if (index == piles.size() / 2) + return 0; + + // 减少计算,快速返回已有结果 + if (memo.count(index)) + return memo[index]; + + // 防止第一次取最右时越界 + int n = piles.size() - 1; + + // 先手选择最左边或最右边后的分数 + int l = piles[index] + dfs(piles, index + 1); + int r = piles[n - index] + dfs(piles, index + 1); + + // 返回先手左或右边的最高分 + return memo[index] = max(l, r); + } + + bool stoneGame(vector& piles) { + // 最佳发挥时: + // 先手得分 * 2 > 总大小 则先手者胜利 + return dfs(piles, 0) * 2 > accumulate(begin(piles), end(piles), 0); + } +}; + +``` + 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 c905a6095e7e4d8206640c03719b02319f2615bb..edef47328c02568447653d4c6e287acd0c4867a6 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" @@ -10,8 +10,8 @@ ![](../pictures/souyisou.png) 相关推荐: - * [动态规划设计:最大子数组](../动态规划系列/最大子数组.md) - * [一文学会递归解题](../投稿/一文学会递归解题.md) + * [动态规划设计:最大子数组](https://labuladong.gitbook.io/algo) + * [一文学会递归解题](https://labuladong.gitbook.io/algo) 读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目: @@ -19,7 +19,7 @@ **-----------** -也许有读者看了前文 [动态规划详解](../动态规划系列/动态规划详解进阶.md),学会了动态规划的套路:找到了问题的「状态」,明确了 `dp` 数组/函数的含义,定义了 base case;但是不知道如何确定「选择」,也就是不到状态转移的关系,依然写不出动态规划解法,怎么办? +也许有读者看了前文 [动态规划详解](https://labuladong.gitbook.io/algo),学会了动态规划的套路:找到了问题的「状态」,明确了 `dp` 数组/函数的含义,定义了 base case;但是不知道如何确定「选择」,也就是不到状态转移的关系,依然写不出动态规划解法,怎么办? 不要担心,动态规划的难点本来就在于寻找正确的状态转移方程,本文就借助经典的「最长递增子序列问题」来讲一讲设计动态规划的通用技巧:**数学归纳思想**。 @@ -43,7 +43,7 @@ **我们的定义是这样的:`dp[i]` 表示以 `nums[i]` 这个数结尾的最长递增子序列的长度。** -PS:为什么这样定义呢?这是解决子序列问题的一个套路,后文[动态规划之子序列问题解题模板](../动态规划系列/子序列问题模板.md) 总结了几种常见套路。你读完本章所有的动态规划问题,就会发现 `dp` 数组的定义方法也就那几种。 +PS:为什么这样定义呢?这是解决子序列问题的一个套路,后文[动态规划之子序列问题解题模板](https://labuladong.gitbook.io/algo) 总结了几种常见套路。你读完本章所有的动态规划问题,就会发现 `dp` 数组的定义方法也就那几种。 根据这个定义,我们就可以推出 base case:`dp[i]` 初始值为 1,因为以 `nums[i]` 结尾的最长递增子序列起码要包含它自己。 @@ -164,7 +164,7 @@ public int lengthOfLIS(int[] nums) { 我们只要把处理扑克牌的过程编程写出来即可。每次处理一张扑克牌不是要找一个合适的牌堆顶来放吗,牌堆顶的牌不是**有序**吗,这就能用到二分查找了:用二分查找来搜索当前牌应放置的位置。 -PS:旧文[二分查找算法详解](../算法思维系列/二分查找详解.md)详细介绍了二分查找的细节及变体,这里就完美应用上了,如果没读过强烈建议阅读。 +PS:旧文[二分查找算法详解](https://labuladong.gitbook.io/algo)详细介绍了二分查找的细节及变体,这里就完美应用上了,如果没读过强烈建议阅读。 ```java public int lengthOfLIS(int[] nums) { @@ -215,4 +215,91 @@ public int lengthOfLIS(int[] nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +```python 动态规划 +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + n = len(nums) + f = [1] * (n) + + for i in range(n): + for j in range(i): + if nums[j] < nums[i]: + f[i] = max(f[i], f[j] + 1) + + res = 0 + for i in range(n): + res = max(res, f[i]) + return res +``` + +```python 二分查找 +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + stack = [] + + def find_index(num): + l, r = 0, len(stack) + while l < r: + mid = l + r >> 1 + if stack[mid] >= num: + r = mid + else: + l = mid + 1 + + return r + + + for num in nums: + if not stack or num > stack[-1]: + stack.append(num) + else: + position = find_index(num) + stack[position] = num + + return len(stack) +``` + + +[Kian](https://github.com/KianKw/) 提供 C++ 代码 + +```c++ +class Solution { +public: + int lengthOfLIS(vector& nums) { + /* len 为牌的数量 */ + int len = nums.size(); + vector top(len, 0); + /* 牌堆数初始化为0 */ + int piles = 0; + for (int i = 0; i < len; i++) { + /* nums[i] 为要处理的扑克牌 */ + int poker = nums[i]; + + /***** 搜索左侧边界的二分查找 *****/ + int left = 0, right = piles; + while (left < right) { + int mid = left + (right - left) / 2; + if (top[mid] > poker) { + right = mid; + } else if (top[mid] < poker) { + left = mid + 1; + } else if (top[mid] == poker) { + right = mid; + } + } + /*********************************/ + + /* 没找到合适的牌堆,新建一堆 */ + if (left == piles) + piles++; + /* 把这张牌放到牌堆顶 */ + top[left] = poker; + } + /* 牌堆数就是 LIS 长度 */ + return piles; + } +}; +``` + 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 bff29f43144578d334aea47a8665ac5a3e9f64d4..f51608e184bdace5d17659778966e09b0e70a506 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" @@ -146,6 +146,8 @@ int helper(vector& memo, int n) { ```cpp int fib(int N) { + if (N == 0) return 0; + if (N == 1) return 1; vector dp(N + 1, 0); // base case dp[1] = dp[2] = 1; @@ -200,7 +202,7 @@ int coinChange(int[] coins, int amount); 比如说 `k = 3`,面值分别为 1,2,5,总金额 `amount = 11`。那么最少需要 3 枚硬币凑出,即 11 = 5 + 5 + 1。 -你认为计算机应该如何解决这个问题?显然,就是把所有肯能的凑硬币方法都穷举出来,然后找找看最少需要多少枚硬币。 +你认为计算机应该如何解决这个问题?显然,就是把所有可能的凑硬币方法都穷举出来,然后找找看最少需要多少枚硬币。 **1、暴力递归** @@ -366,4 +368,45 @@ PS:为啥 `dp` 数组初始化为 `amount + 1` 呢,因为凑成 `amount` 金

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[DapangLiu](https://github.com/DapangLiu) 提供 509. 斐波那契数 Python3 解法代码: + +递归写法 + +```python +class Solution: + def fib(self, N: int) -> int: + if N <= 1: + return N + return self.fib(N-1) + self.fib(N-2) +``` + +动态规划写法 + +```python +class Solution: + def fib(self, N: int) -> int: + if N == 0: + return 0 + # init + result = [0 for i in range(N+1)] + result[1] = 1 + + # status transition + for j in range(2, N+1): + result[j] = result[j-1] + result[j-2] + return result[-1] +``` + +动态规划写法 (状态压缩) + +```python +class Solution: + def fib(self, n: int) -> int: + # current status only depends on two previous status + dp_0, dp_1 = 0, 1 + for _ in range(n): + dp_0, dp_1 = dp_1, dp_0 + dp_1 + return dp_0 +``` \ 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\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 be2b4638141b1426b851511d00098aefb8d2f7da..61f64a36a5587908c7036445dd36f02ca72fe865 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" @@ -16,7 +16,7 @@ 读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目: -[买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/) +[买卖股票的最佳时机](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/) @@ -32,7 +32,7 @@ 很多读者抱怨 LeetCode 的股票系列问题奇技淫巧太多,如果面试真的遇到这类问题,基本不会想到那些巧妙的办法,怎么办?**所以本文拒绝奇技淫巧,而是稳扎稳打,只用一种通用方法解决所用问题,以不变应万变**。 -这篇文章用状态机的技巧来解决,可以全部提交通过。不要觉得这个名词高大上,文学词汇而已,实际上就是 DP table,看一眼就明白了。 +这篇文章参考 [英文版高赞题解](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108870/Most-consistent-ways-of-dealing-with-the-series-of-stock-problems) 的思路,用状态机的技巧来解决,可以全部提交通过。不要觉得这个名词高大上,文学词汇而已,实际上就是 DP table,看一眼就明白了。 先随便抽出一道题,看看别人的解法: 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 35a0fb6a09d7dd6973829e92677169c2357a0538..081c583ac2748e9bf869248bbdb6095736c8a8f6 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" @@ -258,4 +258,66 @@ int[] dp(TreeNode root) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== +[Shantom](https://github.com/Shantom) 提供 198. House Robber I Python3 解法代码: + +```Python +class Solution: + def rob(self, nums: List[int]) -> int: + # 当前,上一间,上上间 + cur, pre1, pre2 = 0, 0, 0 + + for num in nums: + # 当前 = max(上上间+(抢当前),上间(放弃当前)) + cur = max(pre2 + num, pre1) + pre2 = pre1 + pre1 = cur + + return cur +``` +[Shantom](https://github.com/Shantom) 提供 213. House Robber II Python3 解法代码: + +```Python +class Solution: + def rob(self, nums: List[int]) -> int: + # 只有一间时不成环 + if len(nums) == 1: + return nums[0] + + # 该函数同198题 + def subRob(nums: List[int]) -> int: + # 当前,上一间,上上间 + cur, pre1, pre2 = 0, 0, 0 + for num in nums: + # 当前 = max(上上间+(抢当前),上间(放弃当前)) + cur = max(pre2 + num, pre1) + pre2 = pre1 + pre1 = cur + return cur + + # 不考虑第一间或者不考虑最后一间 + return max(subRob(nums[:-1]), subRob(nums[1:])) +``` +[Shantom](https://github.com/Shantom) 提供 337. House Robber III Python3 解法代码: + +```Python +class Solution: + def rob(self, root: TreeNode) -> int: + # 返回值0项为不抢该节点,1项为抢该节点 + def dp(root): + if not root: + return 0, 0 + + left = dp(root.left) + right = dp(root.right) + + # 抢当前,则两个下家不抢 + do = root.val + left[0] + right[0] + # 不抢当前,则下家随意 + do_not = max(left) + max(right) + + return do_not, do + + return max(dp(root)) +``` + 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 2dc21f2275e98eb47dfe5b42951dada0dac1bff0..caf68cfe986d3841317cef8c999d856f8b03d02b 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" @@ -54,7 +54,7 @@ return result; 当然,上面这个例子太简单了,不过请读者回顾一下,我们做动态规划问题,是不是一直在求各种最值,本质跟我们举的例子没啥区别,无非需要处理一下重叠子问题。 -前文不[同定义不同解法](../动态规划系列/动态规划之四键键盘.md) 和 [高楼扔鸡蛋进阶](../动态规划系列/高楼扔鸡蛋问题.md) 就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。 +前文不[同定义不同解法](https://labuladong.gitbook.io/algo) 和 [高楼扔鸡蛋进阶](https://labuladong.gitbook.io/algo) 就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。 再举个常见但也十分简单的例子,求一棵二叉树的最大值,不难吧(简单起见,假设节点中的值都是非负数): 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 bee894738bed410f27b9ab8a9d5a81f3fab9e93f..bff1721aa8a88a5f9794c960a84ab1cdd5d44f55 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 @@ -# 最长公共子序列 +# 最长公共子序列

@@ -147,4 +147,64 @@ else:

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + + +[Edwenc](https://github.com/Edwenc) 提供 C++ 代码: + +```C++ +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + // 先计算两条字符串的长度 + int m = text1.size(); + int n = text2.size(); + + // 构建dp矩阵 默认初始值0 + // 这里会多扩建一边和一列 + // 因为dp[i][j]的含义是:对于 s1[1..i] 和 s2[1..j],它们的LCS长度是 dp[i][j]。 + // 所以当i或者j为零时 LCS的长度默认为0 + vector< vector > dp ( m+1 , vector ( n+1 , 0 ) ); + + // 状态转移 + // i、j都从1开始遍历 因为下面的操作中都会-1 相当于从0开始 + for ( int i=1 ; i

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== +[ChenjieXu](https://github.com/ChenjieXu) 提供Python版本代码: + +```python3 +def minDistance(word1, word2): + m, n = len(word1), len(word2) + # 创建 DP 数组 + dp = [[0] * (n + 1) for _ in range(m + 1)] + + # base case初始化 + for i in range(m + 1): + dp[i][0] = i + for j in range(n + 1): + dp[0][j] = j + + # 自底向上求解 + for i in range(1, m + 1): + for j in range(1, n + 1): + # 状态转移方程 + if word1[i - 1] == word2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + else: + dp[i][j] = min(dp[i - 1][j] + 1, + dp[i][j - 1] + 1, + dp[i - 1][j - 1] + 1) + # 储存着整个 word1 和 word2 的最小编辑距离 + return dp[m][n] +```` \ 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/\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 70e27fc085797e3ba5af090b97e9f30a92f99f4f..3660bdc5ea6ddd091350e45a5f4c89e53691cf5c 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 @@ -# 贪心算法之区间调度问题 +# 贪心算法之区间调度问题

@@ -158,4 +158,43 @@ int findMinArrowShots(int[][] intvs) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### python +Edwenc 提供 第435题的python3 代码: + +```python +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + ### 思路是首先找到不重叠的区间的个数 + ### 然后再用总个数减去不重叠个数 + ### 获得的就是 需要移除的个数 + + # 首先获得区间的个数 为0的话就不用移除 + n = len(intervals) + if n==0: + return 0 + + # 按照每个区间的右端点值进行排序 + sorted_list = sorted( intervals , key=lambda x: x[1] ) + + # 不重叠区间个数至少是1 + count = 1 + + # end是所有不重叠的区间中 最大的右端点 + # end的初始值即是sorted_list[0]的右端点 + end = sorted_list[0][1] + + # 从1开始往后找 因为0在上面已经取过了 + for i in range(1,n): + # start是当前区间左端点值 + start = sorted_list[i][0] + # 如果当前左端点比最大右端点都大了(可能相等) + # 说明两区间不重叠 count+1 再更新end + if start>=end: + count += 1 + end = sorted_list[i][1] + + # 最后返回的是 需要移除的区间个数 + return n-count +``` \ 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/\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 70c63a01b7bcb7717a3f52ef62599f17663cbdde..68bd9b8655bc4294bec9f3cdb048c88f091a0453 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" @@ -243,7 +243,7 @@ def superEggDrop(self, K: int, N: int) -> int: return dp(K, N) ``` -这里就不展开其他解法了,留在下一篇文章 [高楼扔鸡蛋进阶](../动态规划系列/高楼扔鸡蛋进阶.md) +这里就不展开其他解法了,留在下一篇文章 [高楼扔鸡蛋进阶](https://labuladong.gitbook.io/algo) 我觉得吧,我们这种解法就够了:找状态,做选择,足够清晰易懂,可流程化,可举一反三。掌握这套框架学有余力的话,再去考虑那些奇技淫巧也不迟。 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 801b8fbc4ac2d8e8015d3657f905287aa381f88d..ffe8b5cddfc9ae35ce75a5b800854deb594f2c84 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 @@ -# 二叉搜索树操作集锦 +# 二叉搜索树操作集锦

@@ -310,4 +310,142 @@ void BST(TreeNode root, int target) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### c++ + +[dekunma](https://www.linkedin.com/in/dekun-ma-036a9b198/)提供第98题C++代码: + +```c++ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + bool isValidBST(TreeNode* root) { + // 用helper method求解 + return isValidBST(root, nullptr, nullptr); + } + + bool isValidBST(TreeNode* root, TreeNode* min, TreeNode* max) { + // base case, root为nullptr + if (!root) return true; + + // 不符合BST的条件 + if (min && root->val <= min->val) return false; + if (max && root->val >= max->val) return false; + + // 向左右子树分别递归求解 + return isValidBST(root->left, min, root) + && isValidBST(root->right, root, max); + } +}; +``` + +### python + +[ChenjieXu](https://github.com/ChenjieXu)提供第98题Python3代码: + +```python +def isValidBST(self, root): + # 递归函数 + def helper(node, lower = float('-inf'), upper = float('inf')): + if not node: + return True + + val = node.val + if val <= lower or val >= upper: + return False + # 右节点 + if not helper(node.right, val, upper): + return False + # 左节点 + if not helper(node.left, lower, val): + return False + return True + + return helper(root) + +``` + +[lixiandea](https://github.com/lixiandea)提供第100题Python3代码: + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: + ''' + 当前节点值相等且树的子树相等,则树相等。 + 递归退出条件:两个节点存在一个节点为空 + ''' + if p == None: + if q == None: + return True + else: + return False + if q == None: + return False + # 当前节点相同且左子树和右子树分别相同 + 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 +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def deleteNode(self, root: TreeNode, key: int) -> TreeNode: + # 如果没有树 直接返回None + if root == None: + return None + + # 如果要删除的结点 就是当前结点 + if root.val == key: + # 左子树为空 只有右子树需要被更新 直接返回 + if root.left == None: + return root.right + # 右子树为空 只有左子树需要被更新 直接返回 + if root.right== None: + return root.left + + # 找出此结点左子树的最大值 + # 用这个最大值 来代替当前结点 + # 再在左子树中递归地删除这个最大值结点 + big = self.getMax( root.left ) + root.val = big.val + root.left = self.deleteNode( root.left , big.val ) + + # 当前结点较大 它的左子树中需要删除节点 递归到左子树 + elif root.val > key: + root.left = self.deleteNode( root.left , key) + # 当前结点较小 它的右子树中需要删除节点 递归到右子树 + else: + root.right= self.deleteNode( root.right, key) + + return root + + # 辅助函数 + # 功能是找出此二叉搜索树中最大元素的结点 并返回此结点 + def getMax( self , node ): + # 一直找它的右子树 直到为空 + while node.right: + node = node.right + return node +``` \ 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/\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 fca0ca8a25f7583f7ed598d47a9a102261816f54..d49168b8a05bae04ad93b9eec3ae1a7d7e7f9a81 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" @@ -11,7 +11,7 @@ ![](../pictures/souyisou.png) 相关推荐: - * [回溯算法解题套路框架](https://labuladong.gitbook.io/algo) +* [回溯算法解题套路框架](https://labuladong.gitbook.io/algo) * [动态规划解题套路框架](https://labuladong.gitbook.io/algo) 读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目: @@ -20,7 +20,7 @@ [503.下一个更大元素II](https://leetcode-cn.com/problems/next-greater-element-ii) -[1118.一月有多少天](https://leetcode-cn.com/problems/number-of-days-in-a-month) +[739.每日温度](https://leetcode-cn.com/problems/daily-temperatures/) **-----------** @@ -82,7 +82,7 @@ vector nextGreaterElement(vector& nums) { ### 问题变形 -单调栈的使用技巧差不多了,来一个简单的变形,力扣第 1118 题「一月有多少天」: +单调栈的使用技巧差不多了,来一个简单的变形,力扣第 739 题「每日温度」: 给你一个数组 `T`,这个数组存放的是近几天的天气气温,你返回一个等长的数组,计算:**对于每一天,你还要至少等多少天才能等到一个更暖和的气温;如果等不到那一天,填 0**。 @@ -181,4 +181,26 @@ vector nextGreaterElements(vector& nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== +### java + +```java +// 739. Daily Temperatures +class Solution { + public int[] dailyTemperatures(int[] T) { + Stack stack = new Stack<>(); + int[] ans = new int[T.length]; + for (int i = 0; i < T.length; i++) { + // 如果压栈之后不满足单调递减,弹出元素,直至保持单调性 + while (!stack.isEmpty() && T[i] > T[stack.peek()]) { + int index = stack.pop(); + // 被弹出的元素(T[index])都是小于当前的元素(T[i]),由于栈内元素单调递减,大于被弹出元素(index)的最近的就是当前元素(i) + ans[index] = i - index; + } + stack.push(i); + } + return ans; + } +} +``` + 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 eb298a117238e6001041a0e3ae4526aa641c3c99..140391ec548ee633f217a7c958050bccf1906df2 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" @@ -210,4 +210,105 @@ vector maxSlidingWindow(vector& nums, int k) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### python3 + +由[SCUHZS](ttps://github.com/brucecat)提供 + + +```python +from collections import deque + +class MonotonicQueue(object): + def __init__(self): + # 双端队列 + self.data = deque() + + def push(self, n): + # 实现单调队列的push方法 + while self.data and self.data[-1] < n: + self.data.pop() + self.data.append(n) + + def max(self): + # 取得单调队列中的最大值 + return self.data[0] + + def pop(self, n): + # 实现单调队列的pop方法 + if self.data and self.data[0] == n: + self.data.popleft() + + +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + # 单调队列实现的窗口 + window = MonotonicQueue() + + # 结果 + res = [] + + for i in range(0, len(nums)): + + if i < k-1: + # 先填满窗口前k-1 + window.push(nums[i]) + else: + # 窗口向前滑动 + window.push(nums[i]) + res.append(window.max()) + window.pop(nums[i-k+1]) + return res + +``` + +### java + +```java +class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + int len = nums.length; + // 判断数组或者窗口长度为0的情况 + if (len * k == 0) { + return new int[0]; + } + + /* + 采用两端扫描的方法 + 将数组分成大小为 k 的若干个窗口, 对每个窗口分别从左往右和从右往左扫描, 记录扫描的最大值 + left[] 记录从左往右扫描的最大值 + right[] 记录从右往左扫描的最大值 + */ + int[] left = new int[len]; + int[] right = new int[len]; + + for (int i = 0; i < len; i = i + k) { + // 每个窗口中的第一个值 + left[i] = nums[i]; + // 窗口的最后边界 + int index = i + k - 1 >= len ? len - 1 : i + k - 1; + // 每个窗口的最后一个值 + right[index] = nums[index]; + // 对该窗口从左往右扫描 + for (int j = i + 1; j <= index; j++) { + left[j] = Math.max(left[j - 1], nums[j]); + } + // 对该窗口从右往左扫描 + for (int j = index - 1; j >= i; j--) { + right[j] = Math.max(right[j + 1], nums[j]); + } + } + + int[] arr = new int[len - k + 1]; + + // 对于第 i 个位置, 它一定是该窗口从右往左扫描数组中的最后一个值, 相对的 i + k - 1 是该窗口从左向右扫描数组中的最后一个位置 + // 对两者取最大值即可 + for (int i = 0; i < len - k + 1; i++) { + arr[i] = Math.max(right[i], left[i + k - 1]); + } + + return arr; + } +} +``` 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 399fbbbd2820c05fa4d4dcac7db32e458cf9d992..5f6c5f53c1da6e6bc30ac32b521ce0544d45c4f6 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" @@ -302,4 +302,121 @@ PS:本文前两张图片和 GIF 是我第一次尝试用平板的绘图软件

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[happy-yuxuan](https://github.com/happy-yuxuan) 提供 C++ 代码: + +```c++ +static int timestamp = 0; +class Tweet { +private: + int id; + int time; +public: + Tweet *next; + // id为推文内容,time为发文时间 + Tweet(int id, int time) { + this->id = id; + this->time = time; + next = nullptr; + } + int getId() const { + return this->id; + } + int getTime() const { + return this->time; + } +}; +class User { +private: + int id; +public: + Tweet *head; // 发布的Twitter,用链表表示 + unordered_set followed; // 用户关注了那些人 + User(int userId) { + this->id = userId; + head = nullptr; + // 要先把自己关注了 + followed.insert(id); + } + void follow(int userId) { + followed.insert(userId); + } + void unfollow(int userId) { + // 不可以取关自己 + if (userId != this->id) + followed.erase(userId); + } + void post(int contentId) { + Tweet *twt = new Tweet(contentId, timestamp); + timestamp++; + // 将新建的推文插入链表头 + // 越靠前的推文 timestamp 值越大 + twt->next = head; + head = twt; + } +}; +class Twitter { +private: + // 映射将 userId 和 User 对象对应起来 + unordered_map userMap; + // 判断该用户存不存在系统中,即userMap中存不存在id + inline bool contain(int id) { + return userMap.find(id) != userMap.end(); + } +public: + Twitter() { + userMap.clear(); + } + /* user 发表一条 tweet 动态 */ + void postTweet(int userId, int tweetId) { + if (!contain(userId)) + userMap[userId] = new User(userId); + userMap[userId]->post(tweetId); + } + /* 返回该 user 关注的人(包括他自己)最近的动态 id, + 最多 10 条,而且这些动态必须按从新到旧的时间线顺序排列。*/ + vector getNewsFeed(int userId) { + vector ret; + if (!contain(userId)) return ret; + // 构造一个自动通过Tweet发布的time属性从大到小排序的二叉堆 + typedef function Compare; + Compare cmp = [](const Tweet *a, const Tweet *b) { + return a->getTime() < b->getTime(); + }; + priority_queue, Compare> q(cmp); + // 关注列表的用户Id + unordered_set &users = userMap[userId]->followed; + // 先将所有链表头节点插入优先级队列 + for (int id : users) { + if (!contain(id)) continue; + Tweet *twt = userMap[id]->head; + if (twt == nullptr) continue; + q.push(twt); + } + while (!q.empty()) { + Tweet *t = q.top(); q.pop(); + ret.push_back(t->getId()); + if (ret.size() == 10) return ret; // 最多返回 10 条就够了 + if (t->next) + q.push(t->next); + } + return ret; + } + /* follower 关注 followee */ + void follow(int followerId, int followeeId) { + // 若 follower 不存在,则新建 + if (!contain(followerId)) + userMap[followerId] = new User(followerId); + // 若 followee 不存在,则新建 + if (!contain(followeeId)) + userMap[followeeId] = new User(followeeId); + userMap[followerId]->follow(followeeId); + } + /* follower 取关 followee,如果 Id 不存在则什么都不做 */ + void unfollow(int followerId, int followeeId) { + if (contain(followerId)) + userMap[followerId]->unfollow(followeeId); + } +}; +``` \ 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/\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 830e50372aa2c2595bf0e5a2e65860ecfb8628cd..1f8bca28488c0424757078438a16e6527bc648f4 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" @@ -218,4 +218,40 @@ ListNode reverseBetween(ListNode head, int m, int n) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[DiamondI](https://github.com/DiamondI) 提供python3版本代码: + +思路:递归。时间复杂度为O(n),由于递归调用需要借助栈的空间,因此空间复杂度亦为O(n)。 + +```python3 +# 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 __reverseN(self, head: ListNode, n: int) -> ListNode: + if n == 1: + # 记录第 n + 1 个节点 + self.__successor = head.next; + return head; + # 以 head.next 为起点,需要反转前 n - 1 个节点 + last = self.__reverseN(head.next, n - 1); + + head.next.next = head; + # 让反转之后的 head 节点和后面的节点连起来 + head.next = self.__successor; + return last; + + def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: + # base case + if m == 1: + return self.__reverseN(head, n); + # 前进到反转的起点触发 base case + head.next = self.reverseBetween(head.next, m - 1, n - 1); + return head; +``` 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 502962f74815072b701439c867766c354c6fcccf..a413541bfba6a4a88cb23d829441670a8d7a7fdb 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" @@ -65,7 +65,7 @@ int binarySearch(int[] nums, int target) { ### 一、寻找一个数(基本的二分搜索) -这个场景是最简单的,肯能也是大家最熟悉的,即搜索一个数,如果存在,返回其索引,否则返回 -1。 +这个场景是最简单的,可能也是大家最熟悉的,即搜索一个数,如果存在,返回其索引,否则返回 -1。 ```java int binarySearch(int[] nums, int target) { @@ -104,7 +104,7 @@ int binarySearch(int[] nums, int target) { `while(left <= right)` 的终止条件是 `left == right + 1`,写成区间的形式就是 `[right + 1, right]`,或者带个具体的数字进去 `[3, 2]`,可见**这时候区间为空**,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可。 -`while(left < right)` 的终止条件是 `left == right`,写成区间的形式就是 `[left, right]`,或者带个具体的数字进去 `[2, 2]`,**这时候区间非空**,还有一个数 2,但此时 while 循环终止了。也就是说这区间 `[2, 2]` 被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的。 +`while(left < right)` 的终止条件是 `left == right`,写成区间的形式就是 `[right, right]`,或者带个具体的数字进去 `[2, 2]`,**这时候区间非空**,还有一个数 2,但此时 while 循环终止了。也就是说这区间 `[2, 2]` 被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的。 当然,如果你非要用 `while(left < right)` 也可以,我们已经知道了出错的原因,就打个补丁好了: 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 cf5227d1846b7acd5da9f88db12afa94f570da1d..ff2aebea5758f48b5a333a5cf3473433bff7c565 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" @@ -20,7 +20,7 @@ [141.环形链表II](https://leetcode-cn.com/problems/linked-list-cycle-ii) -[167.两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum) +[167.两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted) **-----------** @@ -80,6 +80,11 @@ ListNode detectCycle(ListNode head) { if (fast == slow) break; } // 上面的代码类似 hasCycle 函数 + if (fast == null || fast.next == null) { + // fast 遇到空指针说明没有环 + return null; + } + slow = head; while (slow != fast) { fast = fast.next; @@ -230,4 +235,25 @@ void reverse(int[] nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[ryandeng32](https://github.com/ryandeng32/) 提供 Python 代码 +```python +class Solution: + def hasCycle(self, head: ListNode) -> bool: + # 检查链表头是否为None,是的话则不可能为环形 + if head is None: + return False + # 快慢指针初始化 + slow = fast = head + # 若链表非环形则快指针终究会遇到None,然后退出循环 + while fast.next and fast.next.next: + # 更新快慢指针 + slow = slow.next + fast = fast.next.next + # 快指针追上慢指针则链表为环形 + if slow == fast: + return True + # 退出循环,则链表有结束,不可能为环形 + return False +``` 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 02737442826e16336e4497a5b2d76ad77aa9ccef..6c7f67daeb720399dfe2b1e5caef2c0ba1d74544 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" @@ -101,4 +101,47 @@ string multiply(string num1, string num2) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[Zane Wang](https://github.com/zanecat) 提供 Java 解法代码: +```java +public String multiply(String num1, String num2) { + // 初始化字符数组 + char[] s1 = num1.toCharArray(); + char[] s2 = num2.toCharArray(); + + // 结果长度最多为两字符串长度之和 + int[] res = new int[s1.length + s2.length]; + + // 从个位开始遍历,把两数字中每一位相乘 + for (int i = s1.length - 1; i >= 0; i--) { + for (int j = s2.length - 1; j >= 0; j--) { + // 计算乘积,并把乘积放在 res 对应的位置, 暂时不考虑进位 + res[i + j + 1] += (s1[i] - '0') * (s2[j] - '0'); + } + } + + // 从个位再次遍历,如果上一次遍历中两数乘积为两位数,进位并叠加到前面一位 + int carry = 0; + for (int i = res.length - 1; i >= 0; i--) { + int sum = res[i] + carry; + res[i] = sum % 10; + carry = sum / 10; + } + + //遍历res数组,构造最终答案字符串 + StringBuilder ans = new StringBuilder(); + int i = 0; + + // 首先找到不为0的第一位 + while (i < res.length - 1 && res[i] == 0) { + i++; + } + + // 将后面的数字附加到ans后面 + while (i < res.length) { + ans.append(res[i++]); + } + return ans.toString(); +} +``` \ 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 e65a1041f5d0b65707fc2c4389a9067fee5fe039..e213979693c724bd2a58a93c31f2a47cad78fd2d 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" @@ -12,8 +12,8 @@ **最新消息:关注公众号参与活动,有机会成为 [70k star 算法仓库](https://github.com/labuladong/fucking-algorithm) 的贡献者,机不可失时不再来**! 相关推荐: -* [东哥吃葡萄时竟然吃出一道算法题!](../高频面试系列/吃葡萄.md) -* [如何寻找缺失的元素](../高频面试系列/消失的元素.md) +* [东哥吃葡萄时竟然吃出一道算法题!](https://labuladong.gitbook.io/algo) +* [如何寻找缺失的元素](https://labuladong.gitbook.io/algo) 读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目: @@ -371,4 +371,31 @@ class Solution:

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + + + +第3题 Python3 代码(提供: [FaDrYL](https://github.com/FaDrYL) ): +```Python3 +def lengthOfLongestSubstring(self, s: str) -> int: + # 子字符串 + sub = "" + largest = 0 + + # 循环字符串,将当前字符加入子字符串,并检查长度 + for i in range(len(s)): + if s[i] not in sub: + # 当前字符不存在于子字符串中,加入当前字符 + sub += s[i] + else: + # 如果当前子字符串的长度超过了之前的记录 + if len(sub) > largest: + largest = len(sub) + # 将子字符串从当前字符处+1切片至最后,并加入当前字符 + sub = sub[sub.find(s[i])+1:] + s[i] + + # 如果最后的子字符串长度超过了之前的记录 + if len(sub) > largest: + return len(sub) + return largest +``` 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 513cbdca8c165141fc2532c5b9b07dc8857b55ca..cd163bf415909f389a0e12cb831603537daf2ad2 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" @@ -149,4 +149,74 @@ void reverse(int[] arr, int i, int j) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[L-WEIWEI](https://github.com/L-WWEEII) 提供 第969题的 Java 代码: + +```java +class Solution { + public List pancakeSort(int[] A) { + List ans = new ArrayList(); + int len = A.length; + if(len == 0){ + return ans; + } + // maxIndex[0] == 当前轮次的最大元素, maxIndex[1] == 最大元素下标 + int[] maxIndex = new int[2]; + maxIndex[0] = Integer.MIN_VALUE; + int maxCount = 0; + // maxCount == len 时,说明完成了整个数组的最大值沉底操作, + while(maxCount < len - 1){ + maxCount = maxValueDown(A, maxIndex, maxCount, ans); + // 每做完一次最大值沉底操作,初始化最大元素值 + maxIndex[0] = Integer.MIN_VALUE; + } + return ans; + } + public int maxValueDown(int[] A, int[] maxIndex, int maxCount, List ans){ + // 遍历条件为 i < A.length - maxCount , 每次最大值沉底时,maxCount + 1,因此下次遍历即可不对最后 maxCount 个元素做操作 + for(int i = 0; i < A.length - maxCount; i++){ + // 元素大于当前储存的元素时,将值与下标 copy 到 maxIndex 数组中 + if(A[i] > maxIndex[0]){ + maxIndex[0] = A[i]; + maxIndex[1] = i; + } + } + // 如果当前轮次最大元素的下标的下一位是上一轮次的最大下标,则不做翻转操作,直接返回 maxCount + 1 + if(maxIndex[1] + 1 == A.length - maxCount){ + return maxCount + 1; + } + // 使用最大值沉底时,当本轮最大值在首位时,不需要再将其先翻转至首位,所以不添加 + if(maxIndex[1] > 0){ + // 将该轮次要翻转的下标添加到结果集中,结果集中需要的是翻转的位置而不是下标,所以添加时下标得 + 1 + ans.add(maxIndex[1] + 1); + } + // 双指针原地交换数组中的值 + // 左指针指0 + int left = 0; + // 右指针指向当前轮次最大元素的下标 + int right = maxIndex[1]; + while(left < right){ + // 交换元素值 + A[left] += A[right]; + A[right] = A[left] - A[right]; + A[left] -= A[right]; + left++; + right--; + } + // 上面交换玩元素值后,当前轮次最大元素排在首位,再从上一轮次最大元素 - 1 的位置翻转 + // 则当前轮次的最大元素成功沉底 + ans.add(A.length - maxCount); + left = 0; + right = A.length - 1 - maxCount; + while(left < right){ + A[left] += A[right]; + A[right] = A[left] - A[right]; + A[left] -= A[right]; + left++; + right--; + } + return maxCount + 1; + } +} +``` 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 b9ca311c32ebb9ecd5f463f6903878aff6df1f8e..caa550abbf1279722f2c036a447ece1f942bf59a 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" @@ -346,4 +346,121 @@ class LRUCache {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[gowufang](https://github.com/gowufang)提供第146题C++代码: +```cpp +class LRUCache { + public: + struct node { + int val; + int key; + node* pre;//当前节点的前一个节点 + node* next;//当前节点的后一个节点 + node(){} + node(int key, int val):key(key), val(val), pre(NULL), next(NULL){} + }; + + LRUCache(int size) { + this->size = size; + head = new node(); + tail = new node(); + head->next = tail; + tail->pre = head; + } + + + void movetohead(node* cur)//相当于一个insert操作,在head 和 head的next之间插入一个节点 + { + node* next = head->next;//head的next先保存起来 + head->next = cur;//将当前节点移动到head的后面 + cur->pre = head;//当前节点cur的pre指向head + next->pre = cur; + cur->next = next; + } + + node* deletecurrentnode(node* cur)//移除当前节点 + { + cur->pre->next = cur->next; + cur->next->pre = cur->pre; + return cur; + } + void makerecently(node* cur) + { + node* temp = deletecurrentnode(cur);// 删除 cur,要重新插入到对头 + movetohead(temp);//cur放到队头去 + } + int get(int key) + { + int ret = -1; + if ( map.count(key)) + { + node* temp = map[key]; + makerecently(temp);// 将 key 变为最近使用 + ret = temp->val; + } + return ret; + } + + void put(int key, int value) { + if ( map.count(key)) + { + // 修改 key 的值 + node* temp = map[key]; + temp->val = value; + // 将 key 变为最近使用 + makerecently(temp); + } + else + { + node* cur = new node(key, value); + if( map.size()== size ) + { + // 链表头部就是最久未使用的 key + node *temp = deletecurrentnode(tail->pre); + map.erase(temp->key); + } + movetohead(cur); + map[key] = cur; + + } + + } + + unordered_map map; + int size; + node* head, *tail; + + }; +``` + +```python3 +""" +所谓LRU缓存,根本的难点在于记录最久被使用的键值对,这就设计到排序的问题, +在python中,天生具备排序功能的字典就是OrderDict。 +注意到,记录最久未被使用的键值对的充要条件是将每一次put/get的键值对都定义为 +最近访问,那么最久未被使用的键值对自然就会排到最后。 +如果你深入python OrderDict的底层实现,就会知道它的本质是个双向链表+字典。 +它内置支持了 +1. move_to_end来重排链表顺序,它可以让我们将最近访问的键值对放到最后面 +2. popitem来弹出键值对,它既可以弹出最近的,也可以弹出最远的,弹出最远的就是我们要的操作。 +""" +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) # 最近访问的放到链表最后,维护好顺序 + +``` 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 c44f08355eab6a4f74a29f536d66b4f9d6d6b915..e204552e863c1a6bf540a0193889041cc78d4430 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" @@ -171,6 +171,7 @@ for (int i = 0; i < n; i++) ======其他语言代码====== +#### c++ [cchroot](https://github.com/cchroot) 提供 C++ 代码: ```c++ @@ -203,3 +204,46 @@ public: } }; ``` + +### python +[tonytang731](https://https://github.com/tonytang731) 提供 Python3 代码: + +```python +import math + +class Solution: + def minEatingSpeed(self, piles, H): + # 初始化起点和终点, 最快的速度可以一次拿完最大的一堆 + start = 1 + end = max(piles) + + # while loop进行二分查找 + while start + 1 < end: + mid = start + (end - start) // 2 + + # 如果中点所需时间大于H, 我们需要加速, 将起点设为中点 + if self.timeH(piles, mid) > H: + start = mid + # 如果中点所需时间小于H, 我们需要减速, 将终点设为中点 + else: + end = mid + + # 提交前确认起点是否满足条件,我们要尽量慢拿 + if self.timeH(piles, start) <= H: + return start + + # 若起点不符合, 则中点是答案 + return end + + + + def timeH(self, piles, K): + # 初始化时间 + H = 0 + + #求拿每一堆需要多长时间 + for pile in piles: + H += math.ceil(pile / K) + + return H +``` 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 c71e65f05a754dd808261eadb162b221d5939215..e9c5b0fb5198a3f5155c78914ae5405f91683bb5 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" @@ -168,4 +168,67 @@ boolean isSubsequence(String s, String t) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== +[dekunma](https://www.linkedin.com/in/dekun-ma-036a9b198/) 提供C++代码 +**解法一:遍历(也可以用双指针):** +```C++ +class Solution { +public: + bool isSubsequence(string s, string t) { + // 遍历s + for(int i = 0; i < s.size(); i++) { + // 找到s[i]字符在t中的位置 + size_t pos = t.find(s[i]); + + // 如果s[i]字符不在t中,返回false + if(pos == std::string::npos) return false; + // 如果s[i]在t中,后面就只看pos以后的字串,防止重复查找 + else t = t.substr(pos + 1); + } + return true; + } +}; +``` + +**解法二:二分查找:** +```C++ +class Solution { +public: + bool isSubsequence(string s, string t) { + int m = s.size(), n = t.size(); + // 对 t 进行预处理 + vector index[256]; + for (int i = 0; i < n; i++) { + char c = t[i]; + index[c].push_back(i); + } + // 串 t 上的指针 + int j = 0; + // 借助 index 查找 s[i] + for (int i = 0; i < m; i++) { + char c = s[i]; + // 整个 t 压根儿没有字符 c + if (index[c].empty()) return false; + int pos = left_bound(index[c], j); + // 二分搜索区间中没有找到字符 c + if (pos == index[c].size()) return false; + // 向前移动指针 j + j = index[c][pos] + 1; + } + return true; + } + // 查找左侧边界的二分查找 + int left_bound(vector arr, int tar) { + int lo = 0, hi = arr.size(); + while (lo < hi) { + int mid = lo + (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 3e2406886f5d6709db893951d2585cf6f770f371..4d0225a5e02aa3ed0061d4c4b29857f2148b2d00 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" @@ -237,4 +237,35 @@ p.next = reverse(q);

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +C++版本: +```cpp + bool isPalindrome(ListNode* head) { + if (head == nullptr || head->next == nullptr) //为空或者只有一个节点时,直接判断为true + return true; + ListNode* slow = head, * fast = head; + while (fast != nullptr) {//首先找到中间节点 + slow = slow->next; + fast = fast->next == nullptr? fast->next:fast->next->next; //因为链表长度可能是奇数或偶数,所以需要进行判断 + } + + ListNode* temp = nullptr,* pre = nullptr;//pre始终保持后续链表的头部,temp节点则作为中间零时替换的节点 + while (slow != nullptr) {//利用头插法,将当前节点与后续链表断链处理,反转后半部分的链表 + temp = slow->next; + slow->next = pre;//建立连接 + pre = slow;//pre始终作为后续链表的头部 + slow = temp; + } + + while (head !=nullptr && pre != nullptr) {//同步进行比较 + if (head->val != pre->val) {//值有不一样的,说明不是回文联表,直接返回false了 + return false; + } + head = head->next;//head向下走,直到走到空 + pre = pre->next;//pre节点也向下走,直到走到空 + } + return true;//到此说明当前链表是回文链表返回true即可 + } + +``` 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 32239bdae460f4d342f1a499bb0ecfb3839e9df6..6516aa1b53ed0c47efd7abe27e8229e37bd23a5b 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" @@ -112,4 +112,51 @@ char leftOf(char c) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +### Python3 +```python +def isValid(self, s: str) -> bool: + left = [] + leftOf = { + ')':'(', + ']':'[', + '}':'{' + } + for c in s: + if c in '([{': + left.append(c) + elif left and leftOf[c]==left[-1]: # 右括号 + left不为空 + 和最近左括号能匹配 + left.pop() + else: # 右括号 + (left为空 / 和堆顶括号不匹配) + return False + + # left中所有左括号都被匹配则return True 反之False + return not left +``` + + +```java +//基本思想:每次遇到左括号时都将相对应的右括号')',']'或'}'推入堆栈 +//如果在字符串中出现右括号,则需要检查堆栈是否为空,以及顶部元素是否与该右括号相同。如果不是,则该字符串无效。 +//最后,我们还需要检查堆栈是否为空 +public boolean isValid(String s) { + Deque stack = new ArrayDeque<>(); + for(char c : s.toCharArray()){ + //是左括号就将相对应的右括号入栈 + if(c=='(') { + stack.offerLast(')'); + }else if(c=='{'){ + stack.offerLast('}'); + }else if(c=='['){ + stack.offerLast(']'); + }else if(stack.isEmpty() || stack.pollLast()!=c){//出现右括号,检查堆栈是否为空,以及顶部元素是否与该右括号相同 + return false; + } + } + return stack.isEmpty(); +} + +``` + + 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 04fe3bb3363ac9d659c3b39e1f88636a9ed24957..bdb6bbe9d2d57d38831ed5918eaaa08e448902c2 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" @@ -283,5 +283,42 @@ void backtrack(int[] nums, LinkedList track) {

+======其他语言代码====== -======其他语言代码====== \ No newline at end of file +[userLF](https://github.com/userLF)提供全排列的java代码: + +```java +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; + } + + 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; + } + } +} + +``` \ 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 2ab9e10af6be126fc789c41b369a040bcbb6e0dd..3a580746077e238ebd17d17026aa7177bede719e 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" @@ -178,4 +178,41 @@ int countPrimes(int n) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +C++解法: +采用的算法是埃拉托斯特尼筛法 +埃拉托斯特尼筛法的具体内容就是:**要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。** +同时考虑到大于2的偶数都不是素数,所以可以进一步优化成:**要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的奇数倍剔除,剩下的奇数就是素数。** +此算法其实就是上面的Java解法所采用的。 + +这里提供C++的代码: +```C++ +class Solution { +public: + int countPrimes(int n) { + int res = 0; + bool prime[n+1]; + for(int i = 0; i < n; ++i) + prime[i] = true; + + for(int i = 2; i <= sqrt(n); ++i) //计数过程 + { //外循环优化,因为判断一个数是否为质数只需要整除到sqrt(n),反推亦然 + if(prime[i]) + { + for(int j = i * i; j < n; j += i) //内循环优化,i*i之前的比如i*2,i*3等,在之前的循环中已经验证了 + { + prime[j] = false; + } + } + } + for (int i = 2; i < n; ++i) + if (prime[i]) res++; //最后遍历统计一遍,存入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 e1fd62583f3a5a563afffc34df9af8e6a3092452..a500aac0f73498427cb2908cd78480037cc11b1c 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" @@ -211,4 +211,99 @@ if (l_max < r_max) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[Yifan Zhang](https://github.com/FanFan0919) 提供 java 代码 + +**双指针解法**:时间复杂度 O(N),空间复杂度 O(1) + +对cpp版本的解法有非常微小的优化。 +因为我们每次循环只会选 left 或者 right 处的柱子来计算,因此我们并不需要在每次循环中同时更新`maxLeft`和`maxRight`。 +我们可以先比较 `maxLeft` 和 `maxRight`,决定这次选择计算的柱子是 `height[left]` 或者 `height[right]` 后再更新对应的 `maxLeft` 或 `maxRight`。 +当然这并不会在时间上带来什么优化,只是提供一种思路。 + +```java +class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) return 0; + int left = 0, right = height.length - 1; + int maxLeft = height[left], maxRight = height[right]; + int res = 0; + + while (left < right) { + // 比较 maxLeft 和 maxRight,决定这次计算 left 还是 right 处的柱子 + if (maxLeft < maxRight) { + left++; + maxLeft = Math.max(maxLeft, height[left]); // update maxLeft + res += maxLeft - height[left]; + } else { + right--; + maxRight = Math.max(maxRight, height[right]); // update maxRight + res += maxRight - height[right]; + } + } + + return res; + } +} +``` + +附上暴力解法以及备忘录解法的 java 代码 + +**暴力解法**:时间复杂度 O(N^2),空间复杂度 O(1) +```java +class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) return 0; + int n = height.length; + int res = 0; + // 跳过最左边和最右边的柱子,从第二个柱子开始 + for (int i = 1; i < n - 1; i++) { + int maxLeft = 0, maxRight = 0; + // 找右边最高的柱子 + for (int j = i; j < n; j++) { + maxRight = Math.max(maxRight, height[j]); + } + // 找左边最高的柱子 + for (int j = i; j >= 0; j--) { + maxLeft = Math.max(maxLeft, height[j]); + } + // 如果自己就是最高的话, + // maxLeft == maxRight == height[i] + res += Math.min(maxLeft, maxRight) - height[i]; + } + return res; + } +} +``` + +**备忘录解法**:时间复杂度 O(N),空间复杂度 O(N) +```java +class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) return 0; + int n = height.length; + int res = 0; + // 数组充当备忘录 + int[] maxLeft = new int[n]; + int[] maxRight = new int[n]; + // 初始化 base case + maxLeft[0] = height[0]; + maxRight[n - 1] = height[n - 1]; + + // 从左向右计算 maxLeft + for (int i = 1; i < n; i++) { + maxLeft[i] = Math.max(maxLeft[i - 1], height[i]); + } + // 从右向左计算 maxRight + for (int i = n - 2; i >= 0; i--) { + maxRight[i] = Math.max(maxRight[i + 1], height[i]); + } + // 计算答案 + for (int i = 1; i < n; i++) { + res += Math.min(maxLeft[i], maxRight[i]) - height[i]; + } + 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 844b5cb6ff0f178fe304042ad519e56cf261fdc0..355953e3cef6c9c8a19a987f0afdd4178e64e867 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" @@ -89,6 +89,7 @@ int missingNumber(int[] nums) { for (int x : nums) sum += x; return expect - sum; +} ``` 你看,这种解法应该是最简单的,但说实话,我自己也没想到这个解法,而且我去问了几个大佬,他们也没想到这个最简单的思路。相反,如果去问一个初中生,他也许很快就能想到。 @@ -132,4 +133,49 @@ public int missingNumber(int[] nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[happy-yuxuan](https://github.com/happy-yuxuan) 提供 三种方法的 C++ 代码: + +```c++ +// 方法:异或元素和索引 +int missingNumber(vector& nums) { + int n = nums.size(); + int res = 0; + // 先和新补的索引异或一下 + res ^= n; + // 和其他的元素、索引做异或 + for (int i = 0; i < n; i++) + res ^= i ^ nums[i]; + return res; +} +``` + +```c++ +// 方法:等差数列求和 +int missingNumber(vector& nums) { + int n = nums.size(); + // 公式:(首项 + 末项) * 项数 / 2 + int expect = (0 + n) * (n + 1) / 2; + int sum = 0; + for (int x : nums) + sum += x; + return expect - sum; +} +``` + +```c++ +// 方法:防止整型溢出 +int missingNumber(vector& nums) { + int n = nums.size(); + int res = 0; + // 新补的索引 + res += n - 0; + // 剩下索引和元素的差加起来 + for (int i = 0; i < n; i++) + res += i - nums[i]; + return res; +} +``` + + 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 06e554f1c8476c128f14a26af855883d2abce129..eb21f4c8cb975e24e6875f7327c0d2f65c4b589c 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" @@ -139,4 +139,30 @@ vector findErrorNums(vector& nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[zhuli](https://github.com/1097452462 "zhuli")提供的Java代码: +```java +class Solution { + public int[] findErrorNums(int[] nums) { + int n = nums.length; + int dup = -1; + for (int i = 0; i < n; i++) { + // 元素是从 1 开始的 + int index = Math.abs(nums[i]) - 1; + // nums[index] 小于 0 则说明重复访问 + if (nums[index] < 0) + dup = Math.abs(nums[i]); + else + nums[index] *= -1; + } + int missing = -1; + for (int i = 0; i < n; i++) + // nums[i] 大于 0 则说明没有访问 + if (nums[i] > 0) + // 将索引转换成元素 + missing = i + 1; + return new int[]{dup, missing}; + } +} +```