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 q = new LinkedList<>(); + q.push(i * n + j); + bfs(q, grid, visited, m, n, dx, dy); + } + } + return res; + } + + private static void bfs(LinkedList q, char[][] grid, boolean[][] visited, int m, int n, int[] dx, int[] dy) { + while (!q.isEmpty()) { + int t = q.peekFirst(); + q.pop(); + for (int k = 0; k < 4; ++k) { + int x = t / n + dx[k], y = t % n + dy[k]; + if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == '0' || visited[x][y]) continue; + visited[x][y] = true; + q.push(x * n + y); + } + } + } + + private static void dfs(char[][] grid, boolean[][] visited, int x, int y) { + if (x < 0 || y < 0 + || x >= grid.length || y >= grid[0].length + || grid[x][y] == '0' || visited[x][y]) { + return; + } + visited[x][y] = true; + // 上 + dfs(grid, visited, x - 1, y); + // 下 + dfs(grid, visited, x + 1, y); + // 左 + dfs(grid, visited, x, y - 1); + // 右 + dfs(grid, visited, x, y + 1); + } + + private static void init(char[][] board) { + board[0][0] = '1'; + board[0][1] = '1'; + board[1][1] = '1'; + board[1][4] = '1'; + board[2][3] = '1'; + board[2][4] = '1'; + board[4][4] = '1'; + } + +} diff --git a/Week_03/id_25/LeetCode_322_025.java b/Week_03/id_25/LeetCode_322_025.java new file mode 100644 index 00000000..da148387 --- /dev/null +++ b/Week_03/id_25/LeetCode_322_025.java @@ -0,0 +1,123 @@ +package com.mootal.algo.day_16; + +/** + * Medium + *

+ * 属于背包问题 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]; + } + +}