From 5096147c88f93ed5ac8a3b45414353feb3675f47 Mon Sep 17 00:00:00 2001 From: ToMono Date: Mon, 10 Jun 2019 00:00:45 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E5=91=A8=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A#025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/id_25/LeetCode_42_025.java | 169 +++++++++++++++++ Week_01/id_25/LeetCode_62_025.java | 288 +++++++++++++++++++++++++++++ Week_01/id_25/NOTE.md | 69 +++++++ 3 files changed, 526 insertions(+) create mode 100644 Week_01/id_25/LeetCode_42_025.java create mode 100644 Week_01/id_25/LeetCode_62_025.java diff --git a/Week_01/id_25/LeetCode_42_025.java b/Week_01/id_25/LeetCode_42_025.java new file mode 100644 index 00000000..10eac6fe --- /dev/null +++ b/Week_01/id_25/LeetCode_42_025.java @@ -0,0 +1,169 @@ +package highFrequencyLeetcode.leetcode_42; + +import java.util.Stack; + +/** + * Hard + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *

+ * 思维全过程记录方案:

+ * 1 背基础结构和算法 | 记录在课程笔记

+ * 2 看题 -> 悟题 思考过程 | 记录在wiki

+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解

+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *

+ * 题解方案topics: + * array、双指针、stack + * + * @author li tong + * @date 2019/6/3 10:18 + * @see Object + * @since 1.0 + */ +public class LeetCode_42_025 { + + public static void main(String[] args) { + int[] testcase = new int[]{2, 0, 2}; // 0, 2, 0 + System.out.println("FORCE=" + bruteForce(testcase)); + System.out.println(); + System.out.println("DP_ONE=" + dpOne(testcase)); + System.out.println(); + System.out.println("DP_TWO=" + dpTwo(testcase)); + System.out.println(); + System.out.println("STACK=" + stack(testcase)); + } + + /** + * 解法1 暴力求解

+ * 看题解,提取主干思路:

+ *   从左向右遍历整个下标 每个下标从左、右分别找出最高的杆子

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   看了题解才知道居然要从左、右分头遍历,这个一开始是想不到的 + * + * @param columns + * @return + */ + public static int bruteForce(int[] columns) { + int result = 0, length = columns.length; + for (int i = 0; i < length; i++) { + // Caution 1 声明位置在for循环里面而不是外面 + int maxl = 0, maxr = 0; + for (int j = i; j < length; j++) { + maxr = Math.max(maxr, columns[j]); + } + for (int j = i; j >= 0; j--) { + maxl = Math.max(maxl, columns[j]); + } + result += Math.min(maxl, maxr) - columns[i]; + } + return result; + } + + /** + * 解法2 DP求解

+ * 看题解,提取主干思路:

+ *   在解法一的基础上 由于是求极值问题 考虑DP

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   由于在暴力求解中DP递推方程已得出,现在需要思考

+ *   1 如何用dp的方式遍历 还是和暴力一样吗?

+ *   2 如何归并?

+ *   看完样例代码,发现遍历的顺序和暴力是有区别的,为什么?

+ * + * @param columns + * @return + */ + public static int dpOne(int[] columns) { + // Caution 1 if length == 0 + if (columns.length == 0) { + return 0; + } + int result = 0, length = columns.length; + int[] maxl = new int[columns.length]; + int[] maxr = new int[columns.length]; + maxl[0] = columns[0]; + // Caution 2 length - 1 + maxr[length - 1] = columns[length - 1]; + for (int i = 1; i < length; i++) { + // Caution 3 i - 1 i + 1 + maxl[i] = Math.max(maxl[i - 1], columns[i]); + } + // Caution 4 length - 2 + for (int i = length - 2; i >= 0; i--) { + maxr[i] = Math.max(maxr[i + 1], columns[i]); + } + for (int i = 1; i < columns.length - 1; i++) { + result += Math.min(maxl[i], maxr[i]) - columns[i]; + } + return result; + } + + /** + * 解法3 DP求解 单次遍历

+ * 看题解,提取主干思路:

+ *   双指针替换两次遍历

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   虽然用dp存储了一些计算结果,但还是和暴力一样遍历了两次,如何减少循环次数?

+ * + * @param columns + * @return + */ + public static int dpTwo(int[] columns) { + int result = 0, left = 0, right = columns.length - 1; + int maxl = 0, maxr = 0; + while (left < right) { + if (columns[left] < columns[right]) { + if (columns[left] >= maxl) { + maxl = columns[left]; + } else { + result += (maxl - columns[left]); + } + ++left; + } else { + if (columns[right] >= maxr) { + maxr = columns[right]; + } else { + result += (maxr - columns[right]); + } + --right; + } + } + return result; + } + + /** + * 解法4 栈解法

+ * 看题解,提取主干思路:

+ *   使用栈标记低谷和两界

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   1 很难想到

+ *   2 即便看了答案也是很难理解

+ * + * @param columns + * @return + */ + public static int stack(int[] columns) { + int result = 0, cursor = 0, length = columns.length; + Stack stack = new Stack<>(); + while (cursor < length) { +// System.out.print("CURSOR" + cursor + "->"); +// System.out.print("COLUMN"); + if (stack.isEmpty() || columns[cursor] < columns[stack.peek()]) { +// System.out.println(cursor + "+"); + stack.push(cursor++); + } else { + int bottom = stack.pop(); +// System.out.print(bottom + "- RESULT+"); + if (stack.isEmpty()) { +// System.out.println(0); + continue; + } + int left = stack.peek(); + int square = (Math.min(columns[left], columns[cursor]) - columns[bottom]) * (cursor - left - 1); +// System.out.println(square); + result += square; + } + } + return result; + } + +} diff --git a/Week_01/id_25/LeetCode_62_025.java b/Week_01/id_25/LeetCode_62_025.java new file mode 100644 index 00000000..eaee1842 --- /dev/null +++ b/Week_01/id_25/LeetCode_62_025.java @@ -0,0 +1,288 @@ +package highFrequencyLeetcode.leetcode_62; + +/** + * Medium + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *

+ * 思维全过程记录方案:

+ * 1 背基础结构和算法 | 记录在课程笔记

+ * 2 看题 -> 悟题 思考过程 | 记录在wiki

+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解

+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *

+ * 题解方案topics: + * array、dp + * + * @author li tong + * @date 2019/6/4 11:36 + * @see Object + * @since 1.0 + */ +public class LeetCode_62_025 { + + /** + * 观察时间复杂度 + */ + private static int count = 0; + + public static void main(String[] args) { + System.out.println(recursive(0, 0)); + System.out.println(recursive(1, 1)); + System.out.println(recursive(1, 2)); + System.out.println(recursive(2, 2)); + System.out.println(recursive(3, 2)); + System.out.println(recursive(3, 3)); + System.out.println("RECURSIVE=" + recursive(10, 10)); + System.out.println(); + + System.out.println("RECURSIVE_MEM=" + recursiveMem(10, 10, new int[11][11])); + System.out.println(); + + System.out.println("TRY_DP_ONE=" + tryDPOne(10, 10)); + System.out.println(); + + System.out.println("[LC PASS]DP_ONE=" + dpOne(51, 9)); + System.out.println(); + + System.out.println("[LC PASS]DP_TWO=" + dpTwo(10, 10)); + System.out.println(); + + System.out.println("[LC PASS]MATH=" + math(23, 12)); + System.out.println("Count=" + count); + } + + /** + * 解法1 递归

+ * 注:本解法时间复杂度高,无法通过leetcode(在较大参数时超时), + * 但是,是一种可以求得正解的解法和推理的重要思路,故保留方法 + *

+ * 自己思考,主干思路:

+ *   脑图图像

+ * 1 1 1 1

+ * 1 2 3 4

+ * 1 3 6 10 15

+ * + + + +

+ * 3 6 10 15

+ * + 3 4 5

+ *

+ *   总结规律,发现:f(m,n) = f(m-1,n) + f(m,n-1) + * + * @param m 棋盘长度 + * @param n 棋盘高度 + * @return + */ + public static int recursive(int m, int n) { + count++; + if (m < 1 || n < 1) { + return 0; + } else if (m == 1 || n == 1) { + return 1; + } + return recursive(m - 1, n) + recursive(m, n - 1); + } + + /** + * 解法2 缓存递归

+ * 注:同样因为加了参数无法通过leetcode提交,但是可以求得正解, + * 也是承前启后的推理思路,同时温习老师介绍的缓存思想,故保留 + * + * @param m + * @param n + * @param mem + * @return + */ + public static int recursiveMem(int m, int n, int[][] mem) { + count++; + if (m < 1 || n < 1) { + return 0; + } else if (m == 1 || n == 1) { + return 1; + // if (mem[m][n] == 0)非常重要 表示只有=0 才递归计算 否则就走缓存 + } else if (mem[m][n] == 0) { + mem[m][n] = recursiveMem(m - 1, n, mem) + recursiveMem(m, n - 1, mem); + } + return mem[m][n]; + } + + /** + * 解法3 伪DP

+ * 注:

+ * 1.没有缓存,无法通过leetcode提交,实质和解法1相同

+ * 2.是从递归过渡到DP的尝试过程,试图寻找DP方程,故保留 + * + * @param m + * @param n + * @return + */ + public static int tryDPOne(int m, int n) { + count++; + int[][] dp = new int[m + 1][n + 1]; + if (m < 1 || n < 1) { + return 0; + } else if (m == 1 || n == 1) { + return 1; + // f22 = dp11 == 0 -> dp11 = f12 + f21 = 2 + } else if (dp[m][n] == 0) { + dp[m][n] = tryDPOne(m - 1, n) + tryDPOne(m, n - 1); + } + return dp[m][n]; + } + + /** + * 解法4 伪DP 不可用

+ * 注:

+ * 1.没找到循环公式,结果错误

+ * 2.是从递归过渡到DP的尝试过程,试图寻找DP方程,故保留 + * @param m + * @param n + * @return + */ + public static int tryDPTwo(int m, int n) { + count++; + int[][] dp = new int[m + 1][n + 1]; + if (m < 1 || n < 1) { + return 0; + } else if (m == 1 || n == 1) { + return 1; + // f22 = dp11 == 0 -> dp11 = dp01 + dp10 + } else if (dp[m][n] == 0) { + dp[m][n] = dp[m - 1][n] + dp[m - 1][n - 1]; + } + return dp[m][n]; + } + + /** + * 解法5 DP 已通过leetcode提交

+ * 自己思考结合题解提示,主干思路:

+ *   1.经过解法3、4的尝试,发现DP实现主路径

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   1 从递归到DP,不知道for循环怎么写

+ *   2 循环内的判断条件

+ * @param m + * @param n + * @return + */ + public static int dpOne(int m, int n) { + count++; + if (m == 0 || n == 0) { + return 0; + } + if (m == 1 || n == 1) { + return 1; + } + int[][] dp = new int[m + 1][n + 1]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i == 0 || j == 0) { + dp[i][j] = 1; + } else { + // f22 = dp11 == 0 -> dp11 = dp01 + dp10 + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } +// System.out.print("i" + i); +// System.out.print(", j" + j + ", "); +// System.out.println(dp[i][j]); + } + } + return dp[m - 1][n - 1]; + } + + /** + * 解法6 DP优化 已通过leetcode提交

+ * 注:实际复杂度提升效果有限(leetcode已验证),只是代码层面看起来简洁了一些

+ * 看题解,提取主干思路:

+ *   将二维DP降为一维

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   1.想不到降维

+ *   2.降维之后,这个一维DP代表的是什么东西?想了半天,画图,后面弄懂了

+ * 图像 斜角对称性

+ * 1 1 1 1

+ * 1 2 3 4

+ * 1 3 6 10 15

+ * + + + +

+ * 3 6 10 15

+ * + 3 4 5

+ * + * @param m + * @param n + * @return + */ + public static int dpTwo(int m, int n) { + count++; + if (m == 0 || n == 0) { + return 0; + } + if (m == 1 || n == 1) { + return 1; + } + int[] dp = new int[n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i == 0 || j == 0) { + dp[j] = 1; + } else { + // 1 2 3 4 6 10 15 20 35 70 + dp[j] += dp[j - 1]; +// System.out.print("i" + i); +// System.out.print(", j" + j + ", "); +// System.out.println(dp[j]); + } + } + } + return dp[n - 1]; + } + + /** + * 解法7 数学解法 已通过leetcode提交

+ * 看题解,提取主干思路:

+ *   发现数据规律:排列组合定理

+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):

+ *   1.排列组合公式都忘了,因此把公式复习了一遍,并且做题验证

+ *   2.如何把公式套用到程序里

+ *   3.double int

+ * 图像

+ * 1 1 1 1

+ * 1 2 3 4

+ * 1 3 6 10 15

+ * 1 4 10 20 35

+ * · · · 15 35 70

+ *

+ * 公式 C(4, 3) = A(4,3)/ A(3,3) = 4*3*2 / 3*2*1 = 4

+ * 证明 abcd => abc abd acd bcd

+ * 公式 C(4, 2) = 4 * 3 / 2 = 6

+ * 证明 abcd => ab ac ad bc bd cd

+ *

+ * 证明 C5,3 = C5,5-3 = C5,2

+ * abcde

+ * C53 = abc abd abe acd ace ade + C43 = 10

+ * C52 = 5*4/2 = 10

+ *

+ * n > k

+ * C(n, k) = A(n,k) / A(k,k) = C(n, n-k) = A(n,n-k) / A(n-k,n-k)

+ * C52 = 5 * 4 / 2! = C53 = 5 * 4 * 3 / 3!

+ * = (n * n-1 * n-2 ... * n - k + 1) / k!

+ * + * @param m + * @param n + * @return + */ + public static int math(int m, int n) { + // 总步数 + int t = n + m - 2; + // 单单往下(或往右)的步数 + int k = m - 1; + double res = 1; + for (int i = 1; i <= k; i++) { +// System.out.print("i" + i); +// System.out.print(", res=" + res + ", "); +// System.out.print((t - k + i)); + // C = ((t - k + 1) * (t - k + 2) * ... * t) / k! + res = res * (t - k + i) / i; +// System.out.println(", res=" + res + ", "); + } + return (int)res; + } + + // DFS + +} diff --git a/Week_01/id_25/NOTE.md b/Week_01/id_25/NOTE.md index 107ea7d6..18313e8a 100644 --- a/Week_01/id_25/NOTE.md +++ b/Week_01/id_25/NOTE.md @@ -1 +1,70 @@ # 学习笔记 +1 对学习方法的总结 + 观察了其他组的同学和我们组的做法 + 感觉我们组的方法还是比较科学的:我们自己建立了一套题库,并且组内优先review。 + +2 自己学习态度的变化 + 平时在休息或思考时,分配了更多的精力思考算法问题 + +3 具体的算法学习思维 + 3.1 抓主干 + 3.2 总结规律 自己五分钟无解的 就先看答案 + 我也把这一过程写在了每个题目的注解里面 + * 思维全过程记录方案:

+ * 1 背基础结构和算法 | 记录在课程笔记

+ * 2 看题 -> 悟题 思考过程 | 记录在wiki

+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解

+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + 3.3 反复练习 + 有思路的就不断优化 没思路的 就先背诵 然后理解 再默写 如此往复 + 根据这个套路 我制定了一份基于斐波那契遗忘曲线的复习表 + 一定不要忘记老师说的 五毒神掌 + ![复习表](http://ww1.sinaimg.cn/large/466e120ely1g3vboe39o3j216a0pztaq.jpg) + +4 在学习笔记里 主要提及宏观上的学习领悟 +关于具体的题解思路 都详尽的写在题目里的注解里了 + +5 Review + 总体感触 用什么语言的都有 精彩纷呈 + 5.1 LeetCode_242_18.java + 优点 + 1. 和我写题的方式很像,把每一种解法都写出来了,也就是一个类里面,有多个解法,尽可能的涵盖了各种角度 + 缺点 + 1. 没有注解 + 尽管老师说要少些注解,但是由于我们是在练习、学习,必要的地方还是要加上注解,一个是给自己记忆,另一方面方便别的同学阅读, + 另外,即使方法体、代码段里没有注解,在方法签名上也应有详细的doc和注解,阐述题意、解法、思路。 + + 5.2 LeetCode_26_015.py + 优点 + 1. 看了这个同学的readme,说自己不是科班,罗列了很多的学到的知识点,可以看出非常认真。 + 缺点 + 1. 同上,代码缺少必要的注解和说明 + 2. 解法偏少,只有一种解法。建议:除了自己思考的解法以外,也把其他解法实现一遍。 + + 5.3 LeetCode_021_29.go + 优点 + 1. 这个同学做了很多题。 + 2. 代码格式整齐 清爽 有适当的注解 + 3. 脑图画的很好 + 缺点 + 1. 没有题解doc + + 5.4 LeetCode_1047_022.js + 优点 + 1. 代码格式整齐 清爽 + 2. 脑图画的很好 + 缺点 + 1. 解法偏少 + 2. 没有题解doc + + 5.5 Leetcode_189_8.java + * 最喜欢的是这个同学的提交 + 优点 + 1. readme里面分门别类,格式整齐、详细的介绍了题目、难点分析、解题思路、参考代码 + 2. 脑图画的很好 + 缺点 + 1. 部分代码没有格式化 + 举例: + 1. k%nums.length,符号和变量中间要有空格,改为k % nums.length + 2. nums[i+k],改为 nums[i + k] + 3. for (int i=0; i Date: Mon, 10 Jun 2019 00:04:50 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E5=A4=8D=E4=B9=A0?= =?UTF-8?q?=E8=A1=A8#025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/id_25/NOTE.md | 7 +- Week_01/id_25/TaskView.html | 140 ++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 Week_01/id_25/TaskView.html diff --git a/Week_01/id_25/NOTE.md b/Week_01/id_25/NOTE.md index 18313e8a..832d00e0 100644 --- a/Week_01/id_25/NOTE.md +++ b/Week_01/id_25/NOTE.md @@ -22,9 +22,12 @@ ![复习表](http://ww1.sinaimg.cn/large/466e120ely1g3vboe39o3j216a0pztaq.jpg) 4 在学习笔记里 主要提及宏观上的学习领悟 -关于具体的题解思路 都详尽的写在题目里的注解里了 + 关于具体的题解思路 都详尽的写在题目里的注解里了 -5 Review +5 分享一下记忆曲线复习表 即是TaskView.html + 直接浏览器打开就能用 里面包含一段斐波那契递归函数呦 + +6 Review 总体感触 用什么语言的都有 精彩纷呈 5.1 LeetCode_242_18.java 优点 diff --git a/Week_01/id_25/TaskView.html b/Week_01/id_25/TaskView.html new file mode 100644 index 00000000..4310a7f9 --- /dev/null +++ b/Week_01/id_25/TaskView.html @@ -0,0 +1,140 @@ + + + + + Title + + + + + + + + + + + + + + + + + +
+ + + \ No newline at end of file