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..44246ab0099b4f5c8b32a34c715529d7befdf19f 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,75 @@ 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 + + +``` + 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..a2a04608ec126b6c0594f63556b95e3bf1084a87 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,45 @@ public int lengthOfLIS(int[] nums) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[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\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..39b13dd6de824bf00c4fc0c6bdb54fa236b10cb9 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" @@ -147,4 +147,29 @@ else:

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +[Shawn](https://github.com/Shawn-Hx) 提供 Java 代码: + +```java +public int longestCommonSubsequence(String text1, String text2) { + // 字符串转为char数组以加快访问速度 + char[] str1 = text1.toCharArray(); + char[] str2 = text2.toCharArray(); + + int m = str1.length, n = str2.length; + // 构建dp table,初始值默认为0 + int[][] dp = new int[m + 1][n + 1]; + // 状态转移 + for (int i = 1; i <= m; i++) + for (int j = 1; j <= n; j++) + if (str1[i - 1] == str2[j - 1]) + // 找到LCS中的字符 + dp[i][j] = dp[i-1][j-1] + 1; + else + dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); + + return dp[m][n]; +} +``` + 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..b30ceebb9b7bed1b1b110d1a03d83ab48ca8a8b1 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" @@ -310,4 +310,36 @@ void BST(TreeNode root, int target) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== +[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); + } +}; +``` 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..f5edbd42685aeb6942009b5fa4f341f5ea100bca 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" @@ -230,4 +230,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/\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..67e6539ca1fbb5dc1f7c7e3ac80d7a8c297c074c 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 上拿下如下题目: 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 23e0b6556ea402f1a87329a214866e3f36b391a0..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" @@ -432,4 +432,35 @@ class LRUCache { 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/\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..3bded5a32287cfec435f1237c974e563702c9e98 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,28 @@ char leftOf(char c) {

-======其他语言代码====== \ No newline at end of file +======其他语言代码====== + +```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/\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; + } +}; +``` + + +