diff --git a/Week_01/id_25/LeetCode_24_025.java b/Week_01/id_25/LeetCode_24_025.java new file mode 100644 index 00000000..201a2dee --- /dev/null +++ b/Week_01/id_25/LeetCode_24_025.java @@ -0,0 +1,168 @@ +package com.mootal.algo.day8_24; + +/** + * Medium + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *
+ * 思维全过程记录方案:
+ * 1 背基础结构和算法 | 记录在课程笔记
+ * 2 看题 -> 悟题 思考过程 | 记录在wiki
+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解
+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *
+ * 问题: + * Given a linked list, swap every two adjacent nodes and return its head. + * You may not modify the values in the list's nodes, only nodes itself may be changed. + * 1->2->3->4 | 2->1->4->3 + *
+ * 题解方案topics: + * linked list + * + * @author li tong + * @date 2019/6/10 9:41 + * @see Object + * @since 1.0 + */ +public class SwapNodes24 { + + private static class ListNode { + int val; + + ListNode next; + + ListNode(int x) { + val = x; + } + + @Override + public String toString() { + return "{" + val + + ", next=" + next + + '}'; + } + } + + public static void main(String[] args) { + ListNode a = new ListNode(1); + ListNode b = new ListNode(2); + ListNode c = new ListNode(3); + ListNode d = new ListNode(4); + a.next = b; + b.next = c; + c.next = d; +// System.out.println(swapListRecursive(a)); +// understandSwapListRecursive(a); + System.out.println(swapPairs(a)); +// System.out.println(swapPairs2(a)); + } + + /** + * 解法1 递归法
+ * 看题解,主干思路:
+ * 定义后序二步节点 找到返回方程
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 如何找到返回方程 把单链表反转递归法又理解了一遍 + * + * @param head + * @return + */ + public static ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + System.out.println(); + return head; + } + ListNode after = head.next; + // 1 难点1 定义后序二步节点 + ListNode nextTwo = after.next; + System.out.println(head + "就位,交代去程工作后," + head.val + "睡眠," + nextTwo + "进入到下一层"); + ListNode back = swapPairs(nextTwo); + System.out.println("after=" + after + "返回到上一层,head=" + head + "唤醒,开始第一步:before指向back=" + back); + // 2 难点2 谁指向谁? + head.next = back; + System.out.println("第一步完成后,head=" + head + ",开始第二步:after" + after + "指向before"); + // 3 难点3 + after.next = head; + System.out.println("第二步完成后,after=" + after); + System.out.println(); + // 4 难点4 返回什么?back 还是 after? + return after; + } + + /** + * 解法2 遍历法
+ * 看题解,主干思路:
+ * 关键是要理解两步两步走
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 想不到定义两个Node + * + * @param head + * @return + */ + public static ListNode swapPairs2(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode newHead = new ListNode(0); + newHead.next = head; + ListNode temp = newHead; + ListNode one = null; + ListNode two = null; + // {dummy->1->2->3->4->null} + while (temp.next != null && temp.next.next != null) { + // one -> 1 + one = temp.next; + // two -> 2 + two = temp.next.next; + // 1-> = 2.next = 3; + one.next = two.next; + // 2-> = 1 + two.next = one; + // now newHead should point to 2 + // if the below is not done newHead->1; + temp.next = two; + // temp was pointing to newHead + // temp->1 + temp = one; + // now { dummy->2->1->3->4 } + } + return newHead.next; + } + + /** + * !重要 完整笔记 自己理解递归方式链表反转 + * 4->3->2->1 + * + * @param before + * @return + */ + public static ListNode understandSwapListRecursive(ListNode before) { + if (before == null || before.next == null) { + System.out.println("最底层的世界空无一物," + before + "返回"); + System.out.println(); + return before; + } + ListNode after = before.next; + System.out.println("before=" + before + "就位,交代去程工作后,before=" + before.val + "睡眠,after=" + after + "进入到下一层"); + ListNode back = understandSwapListRecursive(after); + System.out.println("back=" + back + "返回到上一层,before=" + before + "唤醒,开始第一步:before指向null"); + /** + * 回归这里是难点 + * 第一次失败 back.next = before; before.next = null; 第二次失败 before.next = null; back.next = before; + * 模板中这里不写,也能正常执行,先调试看看 + */ + before.next = null; + System.out.println("第一步完成后,before=" + before + ",开始第二步:after" + after + "指向before"); + after.next = before; + System.out.println("第二步完成后,after=" + after + ",back=" + back); + System.out.println(); + return back; + } + + public static void print(ListNode n) { + while (n != null) { + System.out.print(n.val); + n = n.next; + } + } + +} diff --git a/Week_01/id_25/LeetCode_72_025.java b/Week_01/id_25/LeetCode_72_025.java new file mode 100644 index 00000000..3de5f359 --- /dev/null +++ b/Week_01/id_25/LeetCode_72_025.java @@ -0,0 +1,178 @@ +package com.mootal.algo.day9_72; + +/** + * Hard + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *
+ * 思维全过程记录方案:
+ * 1 背基础结构和算法 | 记录在课程笔记
+ * 2 看题 -> 悟题 思考过程 | 记录在wiki
+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解
+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *
+ * 问题: + * Given two words word1 and word2, + * find the minimum number of operations required to convert word1 to word2. + *
+ * 题解方案topics: + * string、dp + * + * @author li tong + * @date 2019/6/11 10:06 + * @see Object + * @since 1.0 + */ +public class EditDistance72 { + + public static void main(String[] args) { +// String word1 = "abc", word2 = "abc"; +// String word1 = "horse", word2 = "rose"; + String word1 = "rose", word2 = "horse"; + System.out.println(minDistance(word1, word2)); + System.out.println(minDistanceDP(word1, word2)); + } + + /** + * 递归解法
+ * 自己思考结合题解提示,主干思路:
+ * 1.先过一遍所有可能的解决方案 找到递归思路 2 用mem优化
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 1 看题解思路 自己尝试实现代码 写出相似度50% 再看答案 + * 2 边界条件处理 + * 3 多种路径组合出正确的递归写法 + * 4 如何合并到最终结果
+ * + * @param word1 + * @param word2 + * @return + */ + public static int minDistance(String word1, String word2) { + char[] w1 = word1.toCharArray(); + char[] w2 = word2.toCharArray(); + int l1 = w1.length, l2 = w2.length; + int[][] mem = new int[l1][l2]; +// return help(0, w1, 0, w2, mem); + return helpWithMem(0, w1, 0, w2, mem); + } + + private static int help(int i, char[] w1, int j, char[] w2, int[][] mem) { + int insertCnt, deleteCnt, replaceCnt; + if (i == w1.length) { + // 长短不一致的情况 例如 rose -> horse + // 这里自己实现起来很困难 很难想到 + return w2.length - j; + } + if (j == w2.length) { + // 长短不一致的情况 例如 horse -> rose + // 这里自己实现起来很困难 很难想到 + return w1.length - i; + } + int res = 0; + if (w1[i] == w2[j]) { + // 直接return +// System.out.println("i=" + i); + res = help(i + 1, w1, j + 1, w2, mem); +// System.out.println("i=" + i); + return res; + } else { + deleteCnt = help(i + 1, w1, j, w2, mem); + System.out.println(deleteCnt); + insertCnt = help(i, w1, j + 1, w2, mem); + System.out.println(insertCnt); + replaceCnt = help(i + 1, w1, j + 1, w2, mem); + System.out.println(replaceCnt); + // 这里很难想到是取三者的最小值,还要加1是什么意思 + res = Math.min(insertCnt, Math.min(deleteCnt, replaceCnt)) + 1; + // 自己写的代码 意思接近了 +// if (w1[i + 1] == w2[j]) { +// i = i + 1; +// help(i + 1, w1, j, w2); +// deleteCnt++; +// } else if (w1[i] == w2[j + 1]) { +// j = j + 1; +// help(i, w1, j, w2); +// insertCnt++; +// } else { +// i = i + 1; +// j = j + 1; +// help(i, w1, j, w2); +// replaceCnt++; +// } + } + return res; + } + + private static int helpWithMem(int i, char[] w1, int j, char[] w2, int[][] mem) { + int insertCnt, deleteCnt, replaceCnt; + if (i == w1.length) { + return w2.length - j; + } + if (j == w2.length) { + return w1.length - i; + } + if (mem[i][j] > 0) { + return mem[i][j]; + } + int res = 0; + if (w1[i] == w2[j]) { +// System.out.println("i=" + i); + res = helpWithMem(i + 1, w1, j + 1, w2, mem); +// System.out.println("i=" + i); + return res; + } else { + deleteCnt = helpWithMem(i + 1, w1, j, w2, mem); +// System.out.println(deleteCnt); + insertCnt = helpWithMem(i, w1, j + 1, w2, mem); +// System.out.println(insertCnt); + replaceCnt = helpWithMem(i + 1, w1, j + 1, w2, mem); +// System.out.println(replaceCnt); + mem[i][j] = Math.min(insertCnt, Math.min(deleteCnt, replaceCnt)) + 1; + } + return mem[i][j]; + } + + /** + * DP解法
+ * 看题解,主干思路:
+ * dp[i][j] 表示从 word1 的前i个字符转换到 word2 的前j个字符所需要的步骤
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 找出状态转移方程 思路:手写例子 简化变量 + * DP特点 + * 1 求极值 + * 2 子问题最优解 + * 3 dp和递归不同的是 dp是循环 递归是栈 + * 4 dp倾向于倒序
+ * . Ø a b c d
+ * Ø 0 1 2 3 4
+ * b 1 1 1 2 3
+ * b 2 2 1 2 3
+ * c 3 3 2 1 2
+ * + * @param word1 + * @param word2 + * @return + */ + public static int minDistanceDP(String word1, String word2) { + char[] w1 = word1.toCharArray(); + char[] w2 = word2.toCharArray(); + int l1 = w1.length, l2 = w2.length; + int[][] dp = new int[l1 + 1][l2 + 1]; + for (int i = 0; i <= l1; ++i) { + dp[i][0] = i; + } + for (int i = 0; i <= l2; ++i) { + dp[0][i] = i; + } + for (int i = 1; i <= l1; ++i) { + for (int j = 1; j <= l2; ++j) { + if (w1[i - 1] == w2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1; + } + } + } + return dp[l1][l2]; + } + +} diff --git a/Week_02/id_25/LeetCode_24_025.java b/Week_02/id_25/LeetCode_24_025.java new file mode 100644 index 00000000..10c9f421 --- /dev/null +++ b/Week_02/id_25/LeetCode_24_025.java @@ -0,0 +1,168 @@ +package com.mootal.algo.day8_24; + +/** + * Medium + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *
+ * 思维全过程记录方案:
+ * 1 背基础结构和算法 | 记录在课程笔记
+ * 2 看题 -> 悟题 思考过程 | 记录在wiki
+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解
+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *
+ * 问题: + * Given a linked list, swap every two adjacent nodes and return its head. + * You may not modify the values in the list's nodes, only nodes itself may be changed. + * 1->2->3->4 | 2->1->4->3 + *
+ * 题解方案topics: + * linked list + * + * @author li tong + * @date 2019/6/10 9:41 + * @see Object + * @since 1.0 + */ +public class LeetCode_24_025 { + + private static class ListNode { + int val; + + ListNode next; + + ListNode(int x) { + val = x; + } + + @Override + public String toString() { + return "{" + val + + ", next=" + next + + '}'; + } + } + + public static void main(String[] args) { + ListNode a = new ListNode(1); + ListNode b = new ListNode(2); + ListNode c = new ListNode(3); + ListNode d = new ListNode(4); + a.next = b; + b.next = c; + c.next = d; +// System.out.println(swapListRecursive(a)); +// understandSwapListRecursive(a); + System.out.println(swapPairs(a)); +// System.out.println(swapPairs2(a)); + } + + /** + * 解法1 递归法
+ * 看题解,主干思路:
+ * 定义后序二步节点 找到返回方程
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 如何找到返回方程 把单链表反转递归法又理解了一遍 + * + * @param head + * @return + */ + public static ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + System.out.println(); + return head; + } + ListNode after = head.next; + // 1 难点1 定义后序二步节点 + ListNode nextTwo = after.next; + System.out.println(head + "就位,交代去程工作后," + head.val + "睡眠," + nextTwo + "进入到下一层"); + ListNode back = swapPairs(nextTwo); + System.out.println("after=" + after + "返回到上一层,head=" + head + "唤醒,开始第一步:before指向back=" + back); + // 2 难点2 谁指向谁? + head.next = back; + System.out.println("第一步完成后,head=" + head + ",开始第二步:after" + after + "指向before"); + // 3 难点3 + after.next = head; + System.out.println("第二步完成后,after=" + after); + System.out.println(); + // 4 难点4 返回什么?back 还是 after? + return after; + } + + /** + * 解法2 遍历法
+ * 看题解,主干思路:
+ * 关键是要理解两步两步走
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 想不到定义两个Node + * + * @param head + * @return + */ + public static ListNode swapPairs2(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode newHead = new ListNode(0); + newHead.next = head; + ListNode temp = newHead; + ListNode one = null; + ListNode two = null; + // {dummy->1->2->3->4->null} + while (temp.next != null && temp.next.next != null) { + // one -> 1 + one = temp.next; + // two -> 2 + two = temp.next.next; + // 1-> = 2.next = 3; + one.next = two.next; + // 2-> = 1 + two.next = one; + // now newHead should point to 2 + // if the below is not done newHead->1; + temp.next = two; + // temp was pointing to newHead + // temp->1 + temp = one; + // now { dummy->2->1->3->4 } + } + return newHead.next; + } + + /** + * !重要 完整笔记 自己理解递归方式链表反转 + * 4->3->2->1 + * + * @param before + * @return + */ + public static ListNode understandSwapListRecursive(ListNode before) { + if (before == null || before.next == null) { + System.out.println("最底层的世界空无一物," + before + "返回"); + System.out.println(); + return before; + } + ListNode after = before.next; + System.out.println("before=" + before + "就位,交代去程工作后,before=" + before.val + "睡眠,after=" + after + "进入到下一层"); + ListNode back = understandSwapListRecursive(after); + System.out.println("back=" + back + "返回到上一层,before=" + before + "唤醒,开始第一步:before指向null"); + /** + * 回归这里是难点 + * 第一次失败 back.next = before; before.next = null; 第二次失败 before.next = null; back.next = before; + * 模板中这里不写,也能正常执行,先调试看看 + */ + before.next = null; + System.out.println("第一步完成后,before=" + before + ",开始第二步:after" + after + "指向before"); + after.next = before; + System.out.println("第二步完成后,after=" + after + ",back=" + back); + System.out.println(); + return back; + } + + public static void print(ListNode n) { + while (n != null) { + System.out.print(n.val); + n = n.next; + } + } + +} diff --git a/Week_02/id_25/LeetCode_72_025.java b/Week_02/id_25/LeetCode_72_025.java new file mode 100644 index 00000000..f4430354 --- /dev/null +++ b/Week_02/id_25/LeetCode_72_025.java @@ -0,0 +1,178 @@ +package com.mootal.algo.day9_72; + +/** + * Hard + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *
+ * 思维全过程记录方案:
+ * 1 背基础结构和算法 | 记录在课程笔记
+ * 2 看题 -> 悟题 思考过程 | 记录在wiki
+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解
+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *
+ * 问题: + * Given two words word1 and word2, + * find the minimum number of operations required to convert word1 to word2. + *
+ * 题解方案topics: + * string、dp + * + * @author li tong + * @date 2019/6/11 10:06 + * @see Object + * @since 1.0 + */ +public class LeetCode_72_025 { + + public static void main(String[] args) { +// String word1 = "abc", word2 = "abc"; +// String word1 = "horse", word2 = "rose"; + String word1 = "rose", word2 = "horse"; + System.out.println(minDistance(word1, word2)); + System.out.println(minDistanceDP(word1, word2)); + } + + /** + * 递归解法
+ * 自己思考结合题解提示,主干思路:
+ * 1.先过一遍所有可能的解决方案 找到递归思路 2 用mem优化
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 1 看题解思路 自己尝试实现代码 写出相似度50% 再看答案 + * 2 边界条件处理 + * 3 多种路径组合出正确的递归写法 + * 4 如何合并到最终结果
+ * + * @param word1 + * @param word2 + * @return + */ + public static int minDistance(String word1, String word2) { + char[] w1 = word1.toCharArray(); + char[] w2 = word2.toCharArray(); + int l1 = w1.length, l2 = w2.length; + int[][] mem = new int[l1][l2]; +// return help(0, w1, 0, w2, mem); + return helpWithMem(0, w1, 0, w2, mem); + } + + private static int help(int i, char[] w1, int j, char[] w2, int[][] mem) { + int insertCnt, deleteCnt, replaceCnt; + if (i == w1.length) { + // 长短不一致的情况 例如 rose -> horse + // 这里自己实现起来很困难 很难想到 + return w2.length - j; + } + if (j == w2.length) { + // 长短不一致的情况 例如 horse -> rose + // 这里自己实现起来很困难 很难想到 + return w1.length - i; + } + int res = 0; + if (w1[i] == w2[j]) { + // 直接return +// System.out.println("i=" + i); + res = help(i + 1, w1, j + 1, w2, mem); +// System.out.println("i=" + i); + return res; + } else { + deleteCnt = help(i + 1, w1, j, w2, mem); + System.out.println(deleteCnt); + insertCnt = help(i, w1, j + 1, w2, mem); + System.out.println(insertCnt); + replaceCnt = help(i + 1, w1, j + 1, w2, mem); + System.out.println(replaceCnt); + // 这里很难想到是取三者的最小值,还要加1是什么意思 + res = Math.min(insertCnt, Math.min(deleteCnt, replaceCnt)) + 1; + // 自己写的代码 意思接近了 +// if (w1[i + 1] == w2[j]) { +// i = i + 1; +// help(i + 1, w1, j, w2); +// deleteCnt++; +// } else if (w1[i] == w2[j + 1]) { +// j = j + 1; +// help(i, w1, j, w2); +// insertCnt++; +// } else { +// i = i + 1; +// j = j + 1; +// help(i, w1, j, w2); +// replaceCnt++; +// } + } + return res; + } + + private static int helpWithMem(int i, char[] w1, int j, char[] w2, int[][] mem) { + int insertCnt, deleteCnt, replaceCnt; + if (i == w1.length) { + return w2.length - j; + } + if (j == w2.length) { + return w1.length - i; + } + if (mem[i][j] > 0) { + return mem[i][j]; + } + int res = 0; + if (w1[i] == w2[j]) { +// System.out.println("i=" + i); + res = helpWithMem(i + 1, w1, j + 1, w2, mem); +// System.out.println("i=" + i); + return res; + } else { + deleteCnt = helpWithMem(i + 1, w1, j, w2, mem); +// System.out.println(deleteCnt); + insertCnt = helpWithMem(i, w1, j + 1, w2, mem); +// System.out.println(insertCnt); + replaceCnt = helpWithMem(i + 1, w1, j + 1, w2, mem); +// System.out.println(replaceCnt); + mem[i][j] = Math.min(insertCnt, Math.min(deleteCnt, replaceCnt)) + 1; + } + return mem[i][j]; + } + + /** + * DP解法
+ * 看题解,主干思路:
+ * dp[i][j] 表示从 word1 的前i个字符转换到 word2 的前j个字符所需要的步骤
+ * 自己学习 思考 实践时的难点(难点是如何突破的见wiki):
+ * 找出状态转移方程 思路:手写例子 简化变量 + * DP特点 + * 1 求极值 + * 2 子问题最优解 + * 3 dp和递归不同的是 dp是循环 递归是栈 + * 4 dp倾向于倒序
+ * . Ø a b c d
+ * Ø 0 1 2 3 4
+ * b 1 1 1 2 3
+ * b 2 2 1 2 3
+ * c 3 3 2 1 2
+ * + * @param word1 + * @param word2 + * @return + */ + public static int minDistanceDP(String word1, String word2) { + char[] w1 = word1.toCharArray(); + char[] w2 = word2.toCharArray(); + int l1 = w1.length, l2 = w2.length; + int[][] dp = new int[l1 + 1][l2 + 1]; + for (int i = 0; i <= l1; ++i) { + dp[i][0] = i; + } + for (int i = 0; i <= l2; ++i) { + dp[0][i] = i; + } + for (int i = 1; i <= l1; ++i) { + for (int j = 1; j <= l2; ++j) { + if (w1[i - 1] == w2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1; + } + } + } + return dp[l1][l2]; + } + +} diff --git a/Week_03/id_25/LeetCode_200_025.java b/Week_03/id_25/LeetCode_200_025.java new file mode 100644 index 00000000..c8ce9c7b --- /dev/null +++ b/Week_03/id_25/LeetCode_200_025.java @@ -0,0 +1,123 @@ +package com.mootal.algo.day15_37; + +import java.util.LinkedList; + +/** + * Medium + * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q) + *
+ * 思维全过程记录方案:
+ * 1 背基础结构和算法 | 记录在课程笔记
+ * 2 看题 -> 悟题 思考过程 | 记录在wiki
+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解
+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交 + *
+ * 问题: + * Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. + * An island is surrounded by water and is formed by connecting adjacent lands horizontally + * or vertically. You may assume all four edges of the grid are all surrounded by water. + *
+ * 题解方案topics: + * DFS、BFS、Union Find + *
+ * https://www.cnblogs.com/grandyang/p/4402656.html
+ * https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java
+ * http://wykvictor.github.io/2016/06/01/BFS-1.html
+ *
+ * @author li tong
+ * @date 2019/6/17 18:28
+ * @see Object
+ * @since 1.0
+ */
+public class LeetCode_200_025 {
+
+ public static void main(String[] args) {
+ char[][] grid = new char[5][5];
+ for (int i = 0; i < grid.length; i++) {
+ for (int j = 0; j < grid.length; j++) {
+ grid[i][j] = '0';
+ }
+ }
+ init(grid);
+ for (int i = 0; i < grid.length; i++) {
+ for (int j = 0; j < grid.length; j++) {
+ System.out.print(grid[i][j]);
+ }
+ System.out.println();
+ }
+ System.out.println(numIslands(grid));
+ }
+
+ /**
+ * [
+ * [1, 1, 0, 0, 0],
+ * [0, 1, 0, 0, 1],
+ * [0, 0, 0, 1, 1],
+ * [0, 0, 0, 0, 0],
+ * [0, 0, 0, 0, 1]
+ * ]
+ * return 3.
+ */
+ public static int numIslands(char[][] grid) {
+ if (grid == null || grid.length == 0 || grid[0].length == 0) {
+ return 0;
+ }
+ int m = grid.length, n = grid[0].length, res = 0;
+ boolean[][] visited = new boolean[m][n];
+ int[] dx = {1, -1, 0, 0};
+ int[] dy = {0, 0, 1, -1};
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < n; ++j) {
+ if (grid[i][j] == '0' || visited[i][j])
+ continue;
+ ++res;
+// dfs(grid, visited, i, j);
+ LinkedList
+ * 属于背包问题 BackPack
+ * https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html
+ * http://www.cs.ust.hk/mjg_lib/Classes/COMP3711H_Fall16/lectures/19_DPII.pdf
+ * https://www.jianshu.com/p/f38f4af13177
+ * https://www.cnblogs.com/xym4869/p/8513801.html
+ * https://www.cnblogs.com/fengziwei/p/7750849.html
+ * 一、问题描述:有n 个物品,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?
+ *
+ * 二、总体思路:根据动态规划解题步骤(问题抽象化、建立模型、寻找约束条件、判断是否满足最优性原理、
+ * 找大问题与小问题的递推关系式、填表、寻找解组成)找出01背包问题的最优解以及解组成,然后编写代码实现;
+ *
+ * 三、动态规划的原理及过程:
+ *
+ * eg:number=4,capacity=8
+ * 1、原理
+ *
+ * 动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,
+ * 解决一个个小问题,最终达到解决原问题的效果。但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,
+ * 而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,
+ * 避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。
+ * 2、过程
+ * 3、空间优化
+ * 四、蛮力法检验:
+ *
+ * 1) 蛮力法是解决01背包问题最简单最容易的方法,但是效率很低
+ *
+ * 2) (X1,X2,…,Xn)其中Xi=0或1表示第i件商品选或不选,共有n(n-1)/2种可能;
+ *
+ * 3) 最简单的方式就是把所有拿商品的方式都列出来,最后再做判断此方法是否满足装包条件,
+ * 并且通过比较和记录找出最优解和解组成(如果满足则记录此时的价值和装的方式,当下一次的装法优于这次,
+ * 则更新记录,如此下去到最后便会找到最优解,同时解组成也找到);
+ *
+ * 4) n件商品,共有n(n-1)/2种可能,故蛮力法的效率是指数级别的,可见效率很低;
+ *
+ * 5) 蛮力法效率低不建议采取,但可以用于检验小规模的动态规划解背包问题的正确性和可行性,
+ * 如下图输出可见,解01背包问题用动态规划是可行的:
+ * 五、总结:
+ *
+ * 对于01背包问题,用蛮力法与用动态规划解决得到的最优解和解组成是一致的,所以动态规划解决此类问题是可行的。
+ * 动态规划效率为线性,蛮力法效率为指数型,结合以上内容和理论知识可以得出,解决此问题用动态规划比用蛮力法适合得多。
+ * 对于动态规划不足的是空间开销大,数据的存储得用到二维数组;好的是,当前问题的解只与上一层的子问题的解相关,
+ * 所以,可以把动态规划的空间进行优化,使得空间效率从O(n*c)转化为O(c),
+ * 遗憾的是,虽然优化了空间,但优化后只能求出最优解,解组成的探索方式在该方法运行的时候已经被破坏掉;
+ * 总之动态规划和优化后的动态规划各有优缺点,可以根据实际问题的需求选择不同的方式。
+ * 六、引申:
+ *
+ * 动态规划可以解决哪些类型的问题?
+ *
+ * 待解决的原问题较难,但此问题可以被不断拆分成一个个小问题,而小问题的解是非常容易获得的;
+ * 如果单单只是利用递归的方法来解决原问题,那么采用的是分治法的思想,
+ * 动态规划具有记忆性,将子问题的解都记录下来,以免在递归的过程中重复计算,从而减少了计算量。
+ *
+ * (注解文档查看快捷键 选中类名或方法名 按ctrl + Q)
+ *
+ * 思维全过程记录方案:
+ * 1 背基础结构和算法 | 记录在课程笔记
+ * 2 看题 -> 悟题 思考过程 | 记录在wiki
+ * 3 悟题 -> 写题 实现难点 | 记录在代码注解
+ * 4 写题 -> 优化 多种解法 | 记录在leetcode提交
+ *
+ * 问题:
+ * You are given coins of different denominations and a total amount of money amount.
+ * Write a function to compute the fewest number of coins that you need to make up that amount.
+ * If that amount of money cannot be made up by any combination of the coins, return -1.
+ *
+ * 题解方案topics:
+ * dp
+ *
+ * @author li tong
+ * @date 2019/6/17 18:19
+ * @see Object
+ * @since 1.0
+ */
+public class LeetCode_322_025 {
+
+ public static void main(String[] args) {
+ int[] coins = new int[]{1, 2, 5};
+ int amount = 11;
+ System.out.println(coinChange2(coins, amount));
+ }
+
+ public static int coinChange2(int[] coins, int amount) {
+ if (coins == null || coins.length == 0) {
+ return 0;
+ }
+ int[] dp = new int[amount + 1];
+ dp[0] = 0;
+ for (int i = 1; i < dp.length; i++) {
+ dp[i] = amount + 1;
+ }
+ for (int coin : coins) {
+ for (int i = coin; i <= amount; i++) {
+ dp[i] = Math.min(dp[i], dp[i - coin] + 1);
+ }
+ }
+ return dp[amount] > amount ? -1 : dp[amount];
+ }
+
+ public static int coinChange1(int[] coins, int amount) {
+ if (coins == null || coins.length == 0) {
+ return 0;
+ }
+ int[] dp = new int[amount + 1];
+ dp[0] = 0;
+ for (int i = 1; i < dp.length; i++) {
+ dp[i] = amount + 1;
+ }
+ for (int i = 1; i <= amount; i++) {
+ for (int j = 0; j < coins.length; j++) {
+ if (coins[j] <= i) {
+ dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
+ }
+ }
+ }
+ return dp[amount] > amount ? -1 : dp[amount];
+ }
+
+}