├── .gitignore ├── .idea ├── $PRODUCT_WORKSPACE_FILE$ ├── algorithm.iml ├── hotswap_agent.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── algorithm.iml └── src └── com └── mahai ├── _001_permutation └── Solution.java ├── _002_combination └── Solution.java ├── _003_linkeList_invert └── Solution.java ├── _004_linkeList_quick_slow_pointer ├── Solution.java └── Test.java ├── _005_dp_min_triangle_path └── Solution.java ├── _006_dp_min_coins └── Solution.java ├── _007_candy └── Solution.java ├── _008_duplicate_interval └── Solution.java ├── _009_knapsack_recursive └── Solution.java ├── _010_knapsack_dp └── Solution.java ├── _011_knapsack_value_recursive └── Solution.java └── _012_knapsack_value_dp └── Solution.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 3 | 4 | # User-specific stuff 5 | .idea/**/workspace.xml 6 | .idea/**/tasks.xml 7 | .idea/**/usage.statistics.xml 8 | .idea/**/dictionaries 9 | .idea/**/shelf 10 | 11 | # Generated files 12 | .idea/**/contentModel.xml 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/artifacts 32 | # .idea/compiler.xml 33 | # .idea/jarRepositories.xml 34 | # .idea/modules.xml 35 | # .idea/*.iml 36 | # .idea/modules 37 | # *.iml 38 | # *.ipr 39 | 40 | # CMake 41 | cmake-build-*/ 42 | 43 | # Mongo Explorer plugin 44 | .idea/**/mongoSettings.xml 45 | 46 | # File-based project format 47 | *.iws 48 | 49 | # IntelliJ 50 | out/ 51 | 52 | # mpeltonen/sbt-idea plugin 53 | .idea_modules/ 54 | 55 | # JIRA plugin 56 | atlassian-ide-plugin.xml 57 | 58 | # Cursive Clojure plugin 59 | .idea/replstate.xml 60 | 61 | # Crashlytics plugin (for Android Studio and IntelliJ) 62 | com_crashlytics_export_strings.xml 63 | crashlytics.properties 64 | crashlytics-build.properties 65 | fabric.properties 66 | 67 | # Editor-based Rest Client 68 | .idea/httpRequests 69 | 70 | # Android studio 3.1+ serialized cache file 71 | .idea/caches/build_file_checksums.ser 72 | -------------------------------------------------------------------------------- /.idea/$PRODUCT_WORKSPACE_FILE$: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 1.8 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 1.8 31 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /.idea/algorithm.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/hotswap_agent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # algorithm 2 | 码海的算法之旅 3 | 4 | ## 公众号文章 5 | 6 | ### [一文学会排列组合](https://mp.weixin.qq.com/s/agYLGYc83cgONllSdAe-Vg) 7 | 8 | 9 | * 排列: _001_permutation, 递归和字典序解法 10 | * 组合: _002_combination, 组合解法 11 | 12 | ### [我画了20张图,终于让女朋友学会了翻转链表](https://mp.weixin.qq.com/s/Thxzq5JBWVsKNGWYSH6sDA) 13 | 14 | 15 | * 文中链表翻转相关的所有解法: _003_linkeList_invert 16 | 17 | ### [一文学会链表快慢指针解题技巧](https://mp.weixin.qq.com/s/lMB9i92MPSQvj6jpt1NYFQ) 18 | 19 | * 文中链表快慢指针相关的所有解法: _004_linkeList_quick_slow_pointer 20 | 21 | ### [一文学会动态规划解题技巧](https://mp.weixin.qq.com/s/15HSidWyGg5eN--ICNNjFg) 22 | 23 | * 文中求三角形最短路径和解法: _005_dp_min_triangle_path 24 | 25 | * 凑零钱 dp 解法: _006_dp_min_coins 26 | 27 | ### [贪心算法](https://mp.weixin.qq.com/s/oChrUOKQY5_C0tnFcuE2gA) 28 | 29 | * 分糖果解法: _007_candy 30 | 31 | * 最小移除区间: _008_duplicate_interval 32 | 33 | ### [0-1背包问题](https://mp.weixin.qq.com/s/zWaCXktazYCB8c9XNlyCDg) 34 | 35 | * 背包问题(只有重量不考虑价值)递归解法: _009_knapsack_recursive 36 | * 背包问题(只有重量不考虑价值)dp 解法: _010_knapsack_dp 37 | * 背包问题(既考虑重量又考虑价值)递归解法: _011_knapsack_value_recursive 38 | * 背包问题(既考虑重量又考虑价值)dp 解法: _012_knapsack_value_dp 39 | 40 | 41 | 42 | 公众号「码海」,欢迎扫码关注 43 | 44 | ![](https://user-gold-cdn.xitu.io/2019/12/31/16f5ae6b2bd87aab?w=430&h=430&f=jpeg&s=41401) 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /algorithm.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/com/mahai/_001_permutation/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._001_permutation; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Solution { 6 | 7 | /** 8 | * 字典序法 9 | * @param arr 10 | * @return boolean 如果还有下一个全排列数,则返回 true, 否则返回 false 11 | */ 12 | public static boolean next_permutation(int[] arr) { 13 | int beforeIndex = 0; //记录从右到左寻找第一个左邻小于右邻的数对应的索引 14 | int currentIndex; 15 | boolean isAllReverse = true; // 是否存在从右到左第一个左邻小于右邻的数对应的索引 16 | // 1. 从右到左(从个位数往高位数)寻找第一个左邻小于右邻的数 17 | for(currentIndex = arr.length - 1; currentIndex > 0; --currentIndex){ 18 | beforeIndex = currentIndex - 1; 19 | if(arr[beforeIndex] < arr[currentIndex]){ 20 | isAllReverse = false; 21 | break; 22 | } 23 | } 24 | //如果不存在,说明这个数已经是字典排序法里的最大值,此时已经找到所有的全排列了,直接打印即可 25 | if(isAllReverse){ 26 | return false; 27 | } else { 28 | // 2. 再从右往左找第一个比第一步找出的数更大的数 29 | int firstLargeIndex = 0; 30 | for(firstLargeIndex = arr.length - 1; firstLargeIndex > beforeIndex; --firstLargeIndex) { 31 | if (arr[firstLargeIndex] > arr[beforeIndex]) { 32 | break; 33 | } 34 | } 35 | // 3. 交换 上述 1, 2 两个步骤中得出的两个数 36 | swap(arr, beforeIndex, firstLargeIndex); 37 | 38 | // 4. 交换 上述 1, 2 两个步骤中得出的两个数 39 | Arrays.sort(arr, beforeIndex + 1, arr.length); 40 | return true; 41 | } 42 | } 43 | 44 | 45 | /** 46 | * 递归解法 47 | * @param arr 48 | * @param k 49 | */ 50 | public static void permutation(int[] arr, int k) { 51 | // 当 k 指向最后一个元素时,递归终止,打印此时的排列排列 52 | if (k == arr.length - 1) { 53 | System.out.println(Arrays.toString(arr)); 54 | } else { 55 | for (int i = k; i < arr.length; i++) { 56 | // 将 k 与之后的元素 i 依次交换,然后可以认为选中了第 k 位 57 | swap(arr, k, i); 58 | // 第 k 位选择完成后,求剩余元素的全排列 59 | permutation(arr, k+1); 60 | // 这一步很关键:将 k 与 i 换回来,保证是初始的顺序 61 | swap(arr, k, i); 62 | } 63 | } 64 | } 65 | 66 | public static void swap (int[] arr, int i, int j) { 67 | int t = arr[i]; 68 | arr[i] = arr[j]; 69 | arr[j] = t; 70 | } 71 | 72 | 73 | public static void main(String[] args) { 74 | int[] arr = {1,2,3,4}; 75 | // 递归解法 76 | permutation(arr, 0); 77 | System.out.println("================="); 78 | // 字典序法 79 | while (next_permutation(arr)) { 80 | System.out.println(Arrays.toString(arr)); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/com/mahai/_002_combination/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._002_combination; 2 | 3 | 4 | public class Solution { 5 | 6 | /** 7 | * 获取 select 中元素为 1 的个数 8 | * @param select 9 | * @return 10 | */ 11 | public static int selectedNum(int[] select) { 12 | int selectNum = 0; // 已被选中的元素个数 13 | for (int i = 0;i < select.length; i++) { 14 | if (select[i] == 1) { 15 | selectNum++; 16 | } 17 | } 18 | return selectNum; 19 | } 20 | 21 | public static final int COMBINATION_CNT = 5; // 组合中需要被选中的个数 22 | public static void combination(int[] arr, int k, int[] select) { 23 | int selectNum = selectedNum(select); 24 | if (selectNum == COMBINATION_CNT) { 25 | int j; 26 | for (j = 0; j < select.length; j++) { 27 | if (select[j] == 1) { 28 | System.out.print(arr[j]); 29 | } 30 | } 31 | System.out.print("\n"); 32 | } else { 33 | 34 | if (k >= arr.length) { 35 | return; 36 | } 37 | 38 | // 第 k 位被选中 39 | select[k] = 1; 40 | // 则从第 k+1 位选择 COMBINATION_CNT - selectNum 个元素 41 | combination(arr, k+1, select); 42 | 43 | // 第 k 位未被选中 44 | select[k] = 0; 45 | // 则从第 k+1 位选择 COMBINATION_CNT - selectNum 个元素 46 | combination(arr, k+1, select); 47 | } 48 | } 49 | 50 | 51 | public static void main(String[] args) { 52 | int[] arr = {1,2,3,4,5,6,7,8,9}; 53 | int[] select = {0,0,0,0,0,0,0,0,0}; 54 | // 一开始从 0 开始选 组合数 55 | combination(arr, 0, select); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/mahai/_003_linkeList_invert/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._003_linkeList_invert; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: ronaldo 10 | * Date: 12/26/19 11 | * Time: 12:44 AM 12 | * To change this template use File | Settings | File Templates. 13 | * Description: 14 | */ 15 | public class Solution { 16 | 17 | public static class Node { 18 | public int data;// 节点的对象,即内容 19 | 20 | // 节点的引用,指向下一个节点,注意:属性不能定义为public,这里只是为了演示方便,生产中还是要定义成 private! 21 | public Node next = null; 22 | 23 | public Node(int val) { 24 | data = val; 25 | } 26 | 27 | } 28 | 29 | /** 30 | * 链表 31 | */ 32 | public static class LinkedList { 33 | int length = 0; // 链表长度,非必须,可不加 34 | Node head = new Node(0); // 哨兵节点 35 | 36 | /** 37 | * 尾插法构造链表 38 | * @param val 39 | */ 40 | public void addNode(int val) { 41 | Node tmp = head; 42 | while (tmp.next != null) { 43 | tmp = tmp.next; 44 | } 45 | tmp.next = new Node(val); 46 | } 47 | 48 | /** 49 | * 头插法构造链表 50 | * @param val 51 | */ 52 | public void headInsert(int val) { 53 | // 构造新结点 54 | Node newNode = new Node(val); 55 | 56 | // 新结点指向头结点之后的结点 57 | newNode.next = head.next; 58 | 59 | // 头结点指向新结点 60 | head.next = newNode; 61 | } 62 | 63 | /** 64 | * 删除指定的结点 65 | * @param deletedNode 66 | */ 67 | public void removeSelectedNode(Node deletedNode) { 68 | // 如果此结点是尾结点我们还是要从头遍历到尾结点的前继结点,再将尾结点删除 69 | if (deletedNode.next == null) { 70 | Node tmp = head; 71 | while (tmp.next != deletedNode) { 72 | tmp = tmp.next; 73 | } 74 | // 找到尾结点的前继结点,把尾结点删除 75 | tmp.next = null; 76 | } else { 77 | Node nextNode = deletedNode.next; 78 | // 将删除结点的后继结点的值赋给被删除结点 79 | deletedNode.data = nextNode.data;; 80 | // 将 nextNode 结点删除 81 | deletedNode.next = nextNode.next;; 82 | nextNode.next = null; 83 | } 84 | } 85 | 86 | /** 87 | * 递归翻转结点 node 开始的链表 88 | * @param node 89 | * @return 90 | */ 91 | public Node invertLinkedList(Node node) { 92 | if (node.next == null) { 93 | return node; 94 | } 95 | 96 | // 步骤 1: 先翻转 node 之后的链表 97 | Node newHead = invertLinkedList(node.next); 98 | 99 | // 步骤 2: 再把 node 节点后继结点的后继结点(3)指向 node,node 的后继节点设置为空 100 | node.next.next = node; 101 | node.next = null; 102 | 103 | // 步骤 3: 返回翻转后的头结点 104 | return newHead; 105 | } 106 | 107 | /** 108 | * 迭代翻转 109 | */ 110 | public void iterationInvertLinkedList() { 111 | // 步骤 1 112 | Node pre = head.next; 113 | Node cur = pre.next; 114 | pre.next = null; 115 | 116 | while (cur != null) { 117 | /** 118 | * 务必注意:在 cur 指向 pre 之前一定要先保留 cur 的后继结点,不然 cur 指向 pre 后就再也找不到后继结点了 119 | * 也就无法对 cur 后继之后的结点进行翻转了 120 | */ 121 | Node next = cur.next; 122 | cur.next = pre; 123 | pre = cur; 124 | cur = next; 125 | } 126 | // 此时 pre 为头结点的后继结点 127 | head.next = pre; 128 | } 129 | 130 | /** 131 | * 迭代翻转 from 到 to 的结点 132 | */ 133 | public void iterationInvertLinkedList(int fromIndex, int toIndex) throws Exception { 134 | // 步骤 1 135 | Node fromPre = null; // from-1结点 136 | Node from = null; // from 结点 137 | Node to = null; // to 结点 138 | Node toNext = null; // to+1 结点 139 | 140 | Node tmp = head.next; 141 | int curIndex = 1; // 头结点的index为1 142 | while (tmp != null) { 143 | if (curIndex == fromIndex-1) { 144 | fromPre = tmp; 145 | } else if (curIndex == fromIndex) { 146 | from = tmp; 147 | } else if (curIndex == toIndex) { 148 | to = tmp; 149 | } else if (curIndex == toIndex+1) { 150 | toNext = tmp; 151 | } 152 | tmp = tmp.next; 153 | curIndex++; 154 | } 155 | 156 | if (from == null || to == null) { 157 | // from 或 to 都超过尾结点不翻转 158 | throw new Exception("不符合条件"); 159 | } 160 | 161 | // 以下使用循环迭代法翻转从 from 到 to 的结点 162 | Node pre = from; 163 | Node cur = pre.next; 164 | while (cur != toNext) { 165 | Node next = cur.next; 166 | cur.next = pre; 167 | pre = cur; 168 | cur = next; 169 | } 170 | 171 | if (fromPre != null) { 172 | fromPre.next = to; 173 | } else { 174 | // 如果 fromPre 为空,说明是从head 的后继节点开始翻转的 175 | head.next = to; 176 | } 177 | from.next = toNext; 178 | } 179 | 180 | /** 181 | * 顺序每 k 个一组翻转链表 182 | * @param k 183 | */ 184 | public void iterationInvertLinkedListEveryK(int k) { 185 | Node tmp = head.next; 186 | int step = 0; // 计数,用来找出首结点和尾结点 187 | 188 | Node startK = null; // k个一组链表中的头结点 189 | Node startKPre = head; // k个一组这段链表头结点的前置结点 190 | Node endK; // k个一组链表中的尾结点 191 | while (tmp != null) { 192 | // 提前保存 tmp 的下一个节点,因为由于翻转,tmp 的后继结点会变 193 | Node tmpNext = tmp.next; 194 | if (step == 0) { 195 | // k 个一组链表区间的头结点 196 | startK = tmp; 197 | step++; 198 | } else if (step == k-1) { 199 | // 此时找到了 k 个一组链表区间的尾结点(endK),对这段链表用迭代进行翻转 200 | endK = tmp; 201 | Node pre = startK; 202 | Node cur = startK.next; 203 | if (cur == null) { 204 | break; 205 | } 206 | Node endKNext = endK.next; 207 | while (cur != endKNext) { 208 | Node next = cur.next; 209 | cur.next = pre; 210 | pre = cur; 211 | cur = next; 212 | } 213 | // 翻转后此时 endK 和 startK 分别是是 k 个一组链表中的首尾结点 214 | startKPre.next = endK; 215 | startK.next = endKNext; 216 | 217 | // 当前的 k 个一组翻转完了,开始下一组 k 个一组的翻转 218 | startKPre = startK; 219 | step = 0; 220 | } else { 221 | step++; 222 | } 223 | tmp = tmpNext; 224 | } 225 | } 226 | 227 | /** 228 | * 逆序每 k 个一组翻转链表 229 | * @param k 230 | */ 231 | public void reverseIterationInvertLinkedListEveryK(int k) { 232 | // 先翻转链表 233 | iterationInvertLinkedList(); 234 | // k 个一组翻转链表 235 | iterationInvertLinkedListEveryK(k); 236 | // 再次翻转链表 237 | iterationInvertLinkedList(); 238 | } 239 | 240 | /** 241 | * 打印链表 242 | */ 243 | public void printList() { 244 | Node tmp = head.next; 245 | List arr = new ArrayList<>(); 246 | while (tmp != null) { 247 | arr.add(tmp.data); 248 | tmp = tmp.next; 249 | } 250 | int length = arr.size(); 251 | for (int i = 0; i < length; i++) { 252 | if (i != arr.size() -1) { 253 | System.out.print(arr.get(i) + "--->"); 254 | } else { 255 | System.out.print(arr.get(i)); 256 | } 257 | } 258 | 259 | System.out.println("\n"); 260 | } 261 | } 262 | 263 | public static void main(String[] args) throws Exception { 264 | LinkedList linkedList = new LinkedList(); 265 | int[] arr = {1,2,3,4,5}; 266 | for (int i = 0; i < arr.length; i++) { 267 | linkedList.addNode(arr[i]); 268 | } 269 | 270 | // 头插法构造链表 271 | // for (int i = 0; i < arr.length; i++) { 272 | // linkedList.headInsert(arr[i]); 273 | // } 274 | 275 | // 删除指定结点(假设指定值为 2 的结点) 276 | // Node tmp = linkedList.head; 277 | // while (tmp.data != 2) { 278 | // tmp = tmp.next; 279 | // } 280 | // linkedList.removeSelectedNode(tmp); 281 | 282 | // 递归翻转 283 | Node newHead = linkedList.invertLinkedList(linkedList.head.next); 284 | linkedList.head.next = newHead; 285 | linkedList.printList(); 286 | 287 | // 迭代翻转 288 | // linkedList.iterationInvertLinkedList(); 289 | // linkedList.printList(); 290 | 291 | // 翻转从链表从结点 from 到结点 to 292 | // linkedList.iterationInvertLinkedList(2, 3); 293 | // linkedList.printList(); 294 | 295 | // 每 k 个一组翻转链表 296 | // linkedList.iterationInvertLinkedListEveryK(1); 297 | // linkedList.printList(); 298 | 299 | // 逆序每 k 个一组翻转链表 300 | // linkedList.reverseIterationInvertLinkedListEveryK(3); 301 | // linkedList.printList(); 302 | } 303 | } -------------------------------------------------------------------------------- /src/com/mahai/_004_linkeList_quick_slow_pointer/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._004_linkeList_quick_slow_pointer; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: ronaldo 10 | * Date: 12/26/19 11 | * Time: 12:44 AM 12 | * To change this template use File | Settings | File Templates. 13 | * Description: 14 | */ 15 | public class Solution { 16 | 17 | public static class Node { 18 | public int data;// 节点的对象,即内容 19 | 20 | // 节点的引用,指向下一个节点,注意:属性不能定义为public,这里只是为了演示方便,生产中还是要定义成 private! 21 | public Node next = null; 22 | 23 | public Node(int val) { 24 | data = val; 25 | } 26 | 27 | } 28 | 29 | /** 30 | * 链表 31 | */ 32 | public static class LinkedList { 33 | int length = 0; // 链表长度,非必须,可不加 34 | Node head = new Node(0); // 哨兵节点 35 | 36 | /** 37 | * 尾插法构造链表 38 | * @param val 39 | */ 40 | public void addNode(int val) { 41 | Node tmp = head; 42 | while (tmp.next != null) { 43 | tmp = tmp.next; 44 | } 45 | tmp.next = new Node(val); 46 | length++; 47 | } 48 | 49 | /** 50 | * 在哨兵结点里定义了链表长度的情况下,找到中间结点 51 | * @return 52 | */ 53 | public Node findMiddleNode() { 54 | Node tmp = head.next; 55 | int middleLength = length / 2; 56 | while (middleLength > 0) { 57 | tmp = tmp.next; 58 | middleLength--; 59 | } 60 | return tmp; 61 | } 62 | 63 | /** 64 | * 在哨兵结点里未定义链表长度的情况下,先遍历找到链表的长度,再遍历 链表长度/2 找到中间结点 65 | * @return 66 | */ 67 | public Node findMiddleNodeWithoutHead() { 68 | Node tmp = head.next; 69 | int length = 1; 70 | // 选遍历一遍拿到链表长度 71 | while (tmp.next != null) { 72 | tmp = tmp.next; 73 | length++; 74 | } 75 | 76 | // 再遍历一遍拿到链表中间结点 77 | tmp = head.next; 78 | int middleLength = length / 2; 79 | while (middleLength > 0) { 80 | tmp = tmp.next; 81 | middleLength--; 82 | } 83 | return tmp; 84 | } 85 | 86 | /** 87 | * 使用快慢指针查找找到中间结点 88 | * @return 89 | */ 90 | public Node findMiddleNodeWithSlowFastPointer() { 91 | 92 | Node slow = head.next; 93 | Node fast = head.next; 94 | 95 | while (fast != null && fast.next != null) { 96 | // 快指针走两步 97 | fast = fast.next.next; 98 | // 慢指针走一步 99 | slow = slow.next; 100 | } 101 | 102 | return slow; 103 | } 104 | 105 | /** 106 | * 查找倒序第 k 个结点 107 | * @param k 108 | * @return 109 | * @throws Exception 110 | */ 111 | public Node findKthToTail(int k) throws Exception { 112 | Node slow = head.next; 113 | Node fast = head.next; 114 | 115 | // 快指针先移到第k个结点 116 | int tmpK = k - 1; 117 | while (tmpK > 0 && fast != null) { 118 | fast = fast.next; 119 | tmpK--; 120 | } 121 | 122 | // 临界条件:k大于链表长度 123 | if (fast == null) { 124 | throw new Exception("K结点不存在异常"); 125 | } 126 | 127 | // slow 和 fast 同时往后移,直到 fast 走到尾结点 128 | while (fast.next != null) { 129 | slow = slow.next; 130 | fast = fast.next; 131 | } 132 | return slow; 133 | } 134 | 135 | public void reversedKthToTail(int k) throws Exception { 136 | // 直接调已实现的 寻找倒序k个结点的方法,这里是 k+1 137 | Node KPreNode = findKthToTail(k+1); 138 | Node kNode = KPreNode.next; 139 | Node headNext = head.next; 140 | 141 | KPreNode.next = null; 142 | 143 | head.next = kNode; 144 | 145 | // 寻找尾结点 146 | Node tmp = kNode; 147 | while (tmp.next != null) { 148 | tmp = tmp.next; 149 | } 150 | tmp.next = headNext; 151 | } 152 | 153 | /** 154 | * 判断两步链表是否有相同的结点 155 | * @param list1 156 | * @param list2 157 | * @return 158 | */ 159 | public static Node detectCommonNode(LinkedList list1, LinkedList list2) { 160 | int length1 = 0; // 链表 list1 的长度 161 | int length2 = 0; // 链表 list2 的长度 162 | 163 | Node p1 = list1.head; 164 | Node p2 = list2.head; 165 | 166 | while (p1.next != null) { 167 | length1++; 168 | p1 = p1.next; 169 | } 170 | 171 | while (p2.next != null) { 172 | length2++; 173 | p2 = p2.next; 174 | } 175 | 176 | p1 = list1.head; 177 | p2= list2.head; 178 | 179 | // p1 或 p2 前进 |length1-length2| 步 180 | if (length1 >= length2) { 181 | int diffLength = length1-length2; 182 | while (diffLength > 0) { 183 | p1 = p1.next; 184 | diffLength--; 185 | } 186 | } else { 187 | int diffLength = length2-length1; 188 | while (diffLength > 0) { 189 | p2 = p2.next; 190 | diffLength--; 191 | } 192 | } 193 | // p1,p2分别往后遍历,边遍历边比较,如果相等,即为第一个相交结点 194 | while (p1 != null && p2.next != null) { 195 | p1 = p1.next; 196 | p2 = p2.next; 197 | if (p1.data == p2.data) { 198 | // p1,p2 都为相交结点,返回 p1 或 p2 199 | return p1; 200 | } 201 | } 202 | // 没有相交结点,返回空指针 203 | return null; 204 | } 205 | 206 | /** 207 | * 判断是否有环,返回快慢指针相遇结点,否则返回空指针 208 | * @return 209 | */ 210 | public Node detectCrossNode() { 211 | Node slow = head; 212 | Node fast = head; 213 | 214 | while (fast != null && fast.next != null) { 215 | fast = fast.next.next; 216 | slow = slow.next; 217 | 218 | if (fast == null) { 219 | return null; 220 | } 221 | 222 | if (slow.data == fast.data) { 223 | return slow; 224 | } 225 | } 226 | return null; 227 | } 228 | 229 | 230 | /** 231 | * 获得环的入口结点 232 | * @return 233 | */ 234 | public Node getRingEntryNode() { 235 | // 获取快慢指针相遇结点 236 | Node crossNode = detectCrossNode(); 237 | 238 | // 如果没有相遇点,则没有环 239 | if (crossNode == null) { 240 | return null; 241 | } 242 | 243 | // 分别定义两个指针,一个指向头结点,一个指向相交结点 244 | Node tmp1 = head; 245 | Node tmp2 = crossNode; 246 | 247 | // 两者相遇点即为环的入口结点 248 | while (tmp1.data != tmp2.data) { 249 | tmp1 = tmp1.next; 250 | tmp2 = tmp2.next; 251 | } 252 | 253 | return tmp1; 254 | } 255 | 256 | 257 | /** 258 | * 打印链表 259 | */ 260 | public void printList() { 261 | Node tmp = head.next; 262 | List arr = new ArrayList<>(); 263 | while (tmp != null) { 264 | arr.add(tmp.data); 265 | tmp = tmp.next; 266 | } 267 | int length = arr.size(); 268 | for (int i = 0; i < length; i++) { 269 | if (i != arr.size() -1) { 270 | System.out.print(arr.get(i) + "--->"); 271 | } else { 272 | System.out.print(arr.get(i)); 273 | } 274 | } 275 | 276 | System.out.println("\n"); 277 | } 278 | } 279 | 280 | public static void main(String[] args) throws Exception { 281 | LinkedList linkedList1 = new LinkedList(); 282 | int[] arr1 = {1,2,3,4,5,6}; 283 | 284 | 285 | for (int i = 0; i < arr1.length; i++) { 286 | linkedList1.addNode(arr1[i]); 287 | } 288 | 289 | Node middle = linkedList1.findKthToTail(6); 290 | System.out.println("middle = " + middle.data); 291 | 292 | } 293 | } -------------------------------------------------------------------------------- /src/com/mahai/_004_linkeList_quick_slow_pointer/Test.java: -------------------------------------------------------------------------------- 1 | package com.mahai._004_linkeList_quick_slow_pointer; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: ronaldo 6 | * Date: 2/2/20 7 | * Time: 8:15 PM 8 | * To change this template use File | Settings | File Templates. 9 | * Description: 10 | */ 11 | 12 | import com.sun.source.tree.BreakTree; 13 | 14 | import java.util.Arrays; 15 | import java.util.HashMap; 16 | /** 17 | * 18 | * VM Args:-Xss160k 19 | */ 20 | public class Test { 21 | 22 | 23 | public static int knapsack(int[] weight, int n, int w) { 24 | boolean[] states = new boolean[w+1]; // 默认值false 25 | states[0] = true; // 第一行的数据要特殊处理,可以利用哨兵优化 26 | if (weight[0] <= w) { 27 | states[weight[0]] = true; 28 | } 29 | for (int i = 1; i < n; ++i) { // 动态规划状态转移 30 | for (int j = 0; j <= w-weight[i]; ++j) {//把第i个物品放入背包 31 | if (states[j]==true) states[j+weight[i]] = true; 32 | 33 | } 34 | } 35 | for (int i = w; i >= 0; --i) { // 输出结果 36 | if (states[i] == true) return i; 37 | } 38 | return 0; 39 | } 40 | 41 | 42 | public static void main(String[] args) throws Throwable { 43 | int[] items = {2,2,4,6,3}; 44 | int n = 5; 45 | int w = 9; 46 | int sum = knapsack(items, 5, 9); 47 | System.out.println("sum = " + sum); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/com/mahai/_005_dp_min_triangle_path/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._005_dp_min_triangle_path; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: ronaldo 6 | * Date: 2/16/20 7 | * Time: 3:50 PM 8 | * To change this template use File | Settings | File Templates. 9 | * Description: 10 | */ 11 | public class Solution { 12 | private static int[][] triangle = { 13 | {2, 0, 0, 0}, 14 | {3, 4, 0, 0}, 15 | {6, 5, 7, 0}, 16 | {4, 1, 8, 3} 17 | }; 18 | 19 | /** 20 | * 动态规划求解三角形的最短路径 21 | * @return 22 | */ 23 | public static int traverse() { 24 | int ROW = 4; 25 | int[] mini = triangle[ROW - 1]; 26 | // 从倒数第二行求起,因为最后一行的值本身是固定的 27 | for (int i = ROW - 2; i >= 0; --i) { 28 | for (int j = 0; j < triangle[i].length; ++j) { 29 | 30 | // 为0的不考虑 31 | if (triangle[i][j] == 0) { 32 | continue; 33 | } 34 | 35 | mini[j] = triangle[i][j] + Math.min(mini[j], mini[j+1]); 36 | } 37 | } 38 | return mini[0]; 39 | } 40 | 41 | public static void main(String[] args) throws Throwable { 42 | int minPathSum = traverse(); 43 | System.out.println("sum = " + minPathSum); 44 | } 45 | } -------------------------------------------------------------------------------- /src/com/mahai/_006_dp_min_coins/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._006_dp_min_coins; 2 | 3 | import org.omg.CORBA.INTERNAL; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: ronaldo 10 | * Date: 2/16/20 11 | * Time: 5:21 PM 12 | * To change this template use File | Settings | File Templates. 13 | * Description: 14 | */ 15 | public class Solution { 16 | 17 | // 保存中间结果 18 | private static HashMap map = new HashMap(); 19 | 20 | /** 21 | * 带备忘录的递归求解 22 | * @param amount 23 | * @param coins 24 | * @return 25 | */ 26 | private static int exchangeRecursive(int amount, int[] coins) { 27 | if (map.get(amount) != null) { 28 | return map.get(amount); 29 | } 30 | 31 | // 说明零钱已经凑完 32 | if (amount == 0) { 33 | return 0; 34 | } 35 | 36 | // 说明没有满足的条件 37 | if (amount < 0) { 38 | return -1; 39 | } 40 | 41 | int result = Integer.MAX_VALUE; 42 | for (int i = 0; i < coins.length; i++) { 43 | int subMin = exchangeRecursive(amount - coins[i], coins); 44 | if (subMin == -1) continue; 45 | result = Math.min(subMin + 1, result); 46 | } 47 | 48 | // 说明没有符合问题的解 49 | if (result == Integer.MAX_VALUE) { 50 | return -1; 51 | } 52 | 53 | map.put(amount, result); 54 | return result; 55 | } 56 | 57 | // 动态规划求解 58 | private static int exchangeDP(int amount, int[] coins) { 59 | 60 | int[] dp = new int[amount + 1]; 61 | // 初始化每个值为 amount+1,这样当最终求得的 dp[amount] 为 amount+1 时,说明问题无解 62 | for (int i = 0; i < amount + 1; i++) { 63 | dp[i] = amount + 1; 64 | } 65 | 66 | // 0 硬币本来就没有,所以设置成 0 67 | dp[0] = 0; 68 | 69 | for (int i = 0; i < amount + 1; i++) { 70 | for (int j = 0; j < coins.length; j++) { 71 | if (i >= coins[j]) { 72 | dp[i] = Math.min(dp[i- coins[j]], dp[i]) + 1; 73 | } 74 | } 75 | } 76 | 77 | if (dp[amount] == amount + 1) { 78 | return -1; 79 | } 80 | return dp[amount]; 81 | } 82 | 83 | public static void main(String[] args) { 84 | int amount = 11; 85 | int[] coins = {1,2,5}; 86 | int result = exchangeDP(amount, coins); 87 | System.out.println("result = " + result); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/com/mahai/_007_candy/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._007_candy; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: ronaldo 8 | * Date: 2/23/20 9 | * Time: 8:54 PM 10 | * To change this template use File | Settings | File Templates. 11 | * Description: 12 | */ 13 | public class Solution { 14 | /** 15 | * 获取能分配给小孩的符合条件的最多糖果数 16 | */ 17 | private static int dispatchCandy(int[] gList, int[] sList) { 18 | Arrays.sort(gList); // 小孩对糖果的需求从小到大排列 19 | Arrays.sort(sList); // 糖果大小从小到大排列 20 | 21 | int maximumCandyNum = 0; 22 | for (int i = 0; i < gList.length; i++) { 23 | for (int j = 0; j < sList.length; j++) { 24 | // 选择最接近小孩需求的糖果,以便让更大的糖果满足需求更大的小孩 25 | if (gList[i] <= sList[j]) { 26 | maximumCandyNum++; 27 | // 糖果被选中,将其置为-1,代表无效了 28 | sList[j] = -1; 29 | // 糖果已选中,跳出 30 | break; 31 | } 32 | } 33 | } 34 | return maximumCandyNum; 35 | } 36 | 37 | public static void main(String[] args) { 38 | // 小孩对糖果的需求 39 | int[] gList = {1,2,4,6}; 40 | // 糖果实际大小 41 | int[] sList = {1,2,7,3}; 42 | int result = dispatchCandy(gList, sList); 43 | System.out.println("result = " + result); 44 | } 45 | } -------------------------------------------------------------------------------- /src/com/mahai/_008_duplicate_interval/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._008_duplicate_interval; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: ronaldo 10 | * Date: 2/25/20 11 | * Time: 11:38 PM 12 | * To change this template use File | Settings | File Templates. 13 | * Description: 14 | */ 15 | public class Solution { 16 | // 区间类,包括起始值和终止值 17 | private static class Interval { 18 | int start; 19 | int end; 20 | Interval(int start, int end) { 21 | this.start = start; 22 | this.end = end; 23 | } 24 | } 25 | 26 | /** 27 | * 递归求解 28 | * @param intervals 29 | * @return 30 | */ 31 | private static Integer removeDuplicateIntervalsByRecursive(Interval[] intervals) { 32 | // 将区间按起始点由小到大进行排序 33 | Arrays.sort(intervals, Comparator.comparingInt(a -> a.start)); 34 | // 首次遍历从 -1,0 开始 35 | return removeSubDuplicate(-1, 0, intervals); 36 | } 37 | 38 | // 保存中间结果 39 | private static HashMap map = new HashMap(); 40 | private static Integer removeSubDuplicate(int pre, int cur, Interval[] intervals) { 41 | 42 | String key = pre + "," + cur; 43 | if (map.get(key) != null) { 44 | return map.get(key); 45 | } 46 | 47 | if (cur == intervals.length) { 48 | return 0; 49 | } 50 | 51 | int notRemove = Integer.MAX_VALUE; 52 | if (pre == -1 || intervals[pre].end <= intervals[cur].start) { 53 | notRemove = removeSubDuplicate(cur, cur+1, intervals); 54 | } 55 | 56 | int remove = removeSubDuplicate(pre, cur+1, intervals) + 1; 57 | int result = Math.min(notRemove, remove); 58 | map.put(key, result); 59 | // 取两者的较小值 60 | return result; 61 | } 62 | 63 | /** 64 | * 判断两区间是否重叠 65 | */ 66 | private static boolean isOverlapping(Interval i, Interval j) { 67 | return j.end > i.start; 68 | } 69 | 70 | /** 71 | * 动态规划求解 72 | */ 73 | private static Integer removeSubDuplicateWithDP(Interval[] intervals) { 74 | // 将区间起始点由小到大进行排序 75 | Arrays.sort(intervals, Comparator.comparingInt(a -> a.start)); 76 | 77 | int[] dp = new int[intervals.length]; 78 | Arrays.fill(dp, 0); 79 | dp[0] = 1; // 将 dp[0] 置为 1, 因为就算所有的区间都重叠,则连续不重叠区间到少也为 1 80 | 81 | int result = 1; 82 | for (int i = 1; i < intervals.length; i ++) { 83 | int max = 0; 84 | for (int j = 0; j < i; j ++) { 85 | if (!isOverlapping(intervals[i], intervals[j])) { 86 | max = Math.max(dp[j], max); 87 | } 88 | } 89 | dp[i] = max + 1; 90 | } 91 | return intervals.length - dp[intervals.length - 1]; 92 | } 93 | 94 | /** 95 | * 贪心算法求解 96 | */ 97 | private static Integer removeSubDuplicateWithGreedy(Interval[] intervals) { 98 | // 将区间终点由小到大进行排序 99 | Arrays.sort(intervals, Comparator.comparingInt(a -> a.end)); 100 | 101 | int cur = 0; // 设置第一个为当前区间 102 | 103 | int count = 1; // 最大不重叠区间数,最小为1 104 | 105 | for (int i = 1; i < intervals.length; i++) { 106 | // 不重叠 107 | if (intervals[cur].end < intervals[i].start) { 108 | cur = i; 109 | count++; 110 | } 111 | } 112 | // 总区间个数减去最大不重叠区间数即最小被移除重叠区间 113 | return intervals.length - count; 114 | } 115 | 116 | 117 | public static void main(String[] args) { 118 | // 初始化区间 119 | Interval[] intervals = { 120 | new Interval(1, 2), 121 | new Interval(3, 5), 122 | new Interval(4, 7), 123 | new Interval(8, 10), 124 | new Interval(9, 11) 125 | }; 126 | int result = removeSubDuplicateWithGreedy(intervals); 127 | System.out.println("result = " + result); 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /src/com/mahai/_009_knapsack_recursive/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._009_knapsack_recursive; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: ronaldo 8 | * Date: 3/8/20 9 | * Time: 10:04 AM 10 | * To change this template use File | Settings | File Templates. 11 | * Description: 12 | */ 13 | public class Solution { 14 | 15 | // 最终的解:即背包可放入的最大重量 16 | private static int maxw = Integer.MIN_VALUE; 17 | 18 | // 每个物品的重量 19 | private static int[] weights = {2,2,4,6,3}; 20 | 21 | // 背包能承载的最大重量 22 | private static final int knapsackWeigth = 9; 23 | 24 | // 备忘录,缓存子问题 25 | private static HashMap mem = new HashMap(); 26 | 27 | private static void knapsack(int i, int w) { 28 | // 物品有多少个 29 | int weightSize = weights.length; 30 | 31 | // 物品选完或者背包里选的物品总质量超过了背包可承载的总重量,递归结束 32 | if (i > weightSize-1 || w >= knapsackWeigth) { 33 | if (w <= knapsackWeigth) { 34 | maxw = Math.max(maxw, w); 35 | } 36 | return; 37 | } 38 | 39 | String key = i + "," + w; 40 | // 有 value,说明子问题之前已经解过了,无需再计算! 41 | if (mem.get(key) != null) { 42 | return; 43 | } 44 | mem.put(key, 1); 45 | 46 | // 第 i 个物品不选 47 | knapsack(i + 1, w); 48 | 49 | if (w + weights[i] <= knapsackWeigth) { 50 | // 选了第 i 个物品 51 | knapsack(i + 1, w + weights[i]); 52 | } 53 | } 54 | 55 | public static void main(String[] args) { 56 | knapsack(0, 0); 57 | System.out.println("maxw = " + maxw); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/com/mahai/_010_knapsack_dp/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._010_knapsack_dp; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: ronaldo 6 | * Date: 3/8/20 7 | * Time: 3:58 PM 8 | * To change this template use File | Settings | File Templates. 9 | * Description: 10 | */ 11 | public class Solution { 12 | 13 | /** 14 | * 15 | * @param weights 各个物品的质量 16 | * @param knapsackWeight 背包可承受的最大质量 17 | * @return 18 | */ 19 | public static int knapsack(int[] weights, int knapsackWeight) { 20 | int n = weights.length; // 物品个数 21 | boolean[][] states = new boolean[n][knapsackWeight+1]; 22 | 23 | // 第一个物品不选 24 | states[0][0] = true; 25 | 26 | if (weights[0] <= knapsackWeight) { 27 | // 第一个物品选了 28 | states[0][weights[0]] = true; 29 | } 30 | 31 | for (int i = 1; i < n; i++) { 32 | for (int j = 0; j < knapsackWeight + 1; j++) { 33 | // 第 i 个物品不放入背包中 34 | if (states[i-1][j]) { 35 | states[i][j] = states[i-1][j]; 36 | } 37 | } 38 | 39 | for (int j = 0; j <= knapsackWeight-weights[i]; ++j) { 40 | //把第i个物品放入背包 41 | if (states[i-1][j]) { 42 | states[i][j+weights[i]] = true; 43 | } 44 | } 45 | } 46 | 47 | // 最后一个阶段决策后,从最后一行右到左取第一个值为 true 对应的重量 48 | for (int j = knapsackWeight; j >= 0; j--) { 49 | if (states[n-1][j]) { 50 | return j; 51 | } 52 | } 53 | return 0; 54 | } 55 | 56 | 57 | /** 58 | * 使用一维数组来保存每个阶段的解 59 | * @param weights 个物品的质量 60 | * @param knapsackWeight 背包可承受的最大质量 61 | * @return 62 | */ 63 | public static int knapsack2(int[] weights, int knapsackWeight) { 64 | int n = weights.length; // 物品个数 65 | boolean[] states = new boolean[knapsackWeight+1]; 66 | // 第一个物品不选 67 | states[0] = true; 68 | 69 | if (weights[0] <= knapsackWeight) { 70 | // 第一个物品选了 71 | states[weights[0]] = true; 72 | } 73 | 74 | for (int i = 1; i < n; i++) { 75 | for (int j = knapsackWeight - weights[i]; j >= 0; --j) { 76 | //把第i个物品放入背包 77 | if (states[j]) { 78 | states[j + weights[i]] = true; 79 | } 80 | } 81 | } 82 | 83 | // 最后一个阶段决策后,从最后一行右到左取第一个值为 true 对应的重量 84 | for (int j = knapsackWeight; j >= 0; j--) { 85 | if (states[j]) { 86 | return j; 87 | } 88 | } 89 | return 0; 90 | } 91 | 92 | public static void main(String[] args) { 93 | int[] weights = {2,2,4,6,3}; 94 | int result = knapsack2(weights, 9); 95 | System.out.println("result = " + result); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/com/mahai/_011_knapsack_value_recursive/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._011_knapsack_value_recursive; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: ronaldo 6 | * Date: 3/8/20 7 | * Time: 7:28 PM 8 | * To change this template use File | Settings | File Templates. 9 | * Description: 10 | */ 11 | public class Solution { 12 | 13 | // 最终的解:即背包可放入的最大重量 14 | private static int cv = Integer.MIN_VALUE; 15 | 16 | // 每个物品的重量 17 | private static int[] weights = {2,2,4,6,3}; 18 | 19 | // 每个物品的价值 20 | private static int[] values = {3,4,8,9,6}; 21 | 22 | // 背包能承载的最大重量 23 | private static final int knapsackWeigth = 9; 24 | 25 | private static void knapsack(int i, int w, int v) { 26 | // 物品有多少个 27 | int weightSize = weights.length; 28 | 29 | // 物品选完或者背包里选的物品总质量超过了背包可承载的总重量,递归结束 30 | if (i > weightSize-1 || w >= knapsackWeigth) { 31 | if (w <= knapsackWeigth) { 32 | cv = Math.max(cv, v); 33 | } 34 | return; 35 | } 36 | 37 | // 第 i 个物品不选 38 | knapsack(i + 1, w, v); 39 | 40 | if (w + weights[i] <= knapsackWeigth) { 41 | // 选了第 i 个物品 42 | knapsack(i + 1, w + weights[i], v + values[i]); 43 | } 44 | } 45 | 46 | public static void main(String[] args) { 47 | knapsack(0, 0, 0); 48 | System.out.println("cv = " + cv); 49 | 50 | } 51 | 52 | 53 | } -------------------------------------------------------------------------------- /src/com/mahai/_012_knapsack_value_dp/Solution.java: -------------------------------------------------------------------------------- 1 | package com.mahai._012_knapsack_value_dp; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: ronaldo 6 | * Date: 3/8/20 7 | * Time: 8:28 PM 8 | * To change this template use File | Settings | File Templates. 9 | * Description: 10 | */ 11 | public class Solution { 12 | 13 | // 最终的解:即背包可放入的最大重量 14 | private static int cv = Integer.MIN_VALUE; 15 | 16 | // 每个物品的重量 17 | private static int[] weights = {2,2,4,6,3}; 18 | 19 | // 每个物品的价值 20 | private static int[] values = {3,4,8,9,6}; 21 | 22 | // 背包能承载的最大重量 23 | private static final int knapsackWeigth = 9; 24 | 25 | /** 26 | * 27 | * @param weights 各个物品的重量 28 | * @param weights 各个物品的价值 29 | * @param knapsackWeight 背包可承受的最大质量 30 | * @return 31 | */ 32 | public static int knapsack(int[] weights, int values[], int knapsackWeight) { 33 | int n = weights.length; // 物品个数 34 | int[][] states = new int[n][knapsackWeight+1]; 35 | 36 | for (int i = 0; i < n; i++) { 37 | for (int j = 0; j < knapsackWeight+1; j++) { 38 | states[i][j] = -1; 39 | } 40 | } 41 | 42 | // 第一个物品不选 43 | states[0][0] = 0; 44 | 45 | if (weights[0] <= knapsackWeight) { 46 | // 第一个物品选了 47 | states[0][weights[0]] = values[0]; 48 | } 49 | 50 | for (int i = 1; i < n; i++) { 51 | for (int j = 0; j < knapsackWeight + 1; j++) { 52 | // 第 i 个物品不放入背包中 53 | if (states[i-1][j] > 0) { 54 | states[i][j] = states[i-1][j]; 55 | } 56 | } 57 | 58 | for (int j = 0; j <= knapsackWeight-weights[i]; ++j) { 59 | //把第i个物品放入背包 60 | if (states[i-1][j] >= 0) { 61 | states[i][j+weights[i]] = Math.max(states[i-1][j] + values[i], states[i][j+weights[i]]); 62 | } 63 | } 64 | } 65 | 66 | // 求出 67 | int max = Integer.MIN_VALUE; 68 | for (int j = knapsackWeight; j >= 0; j--) { 69 | max = Math.max(max, states[n-1][j]); 70 | } 71 | return max; 72 | } 73 | 74 | /** 75 | * 使用一维数组来保存每个阶段的解 76 | * @param weights 各个物品的重量 77 | * @param weights 各个物品的价值 78 | * @param knapsackWeight 背包可承受的最大质量 79 | * @return 80 | */ 81 | public static int knapsack2(int[] weights, int values[], int knapsackWeight) { 82 | int n = weights.length; // 物品个数 83 | 84 | // 改用一维数组来保存每个阶段的状态,减少空间复杂度 85 | int[] states = new int[knapsackWeight+1]; 86 | 87 | for (int j = 0; j < knapsackWeight+1; j++) { 88 | states[j] = -1; 89 | } 90 | 91 | // 第一个物品不选 92 | states[0] = 0; 93 | 94 | if (weights[0] <= knapsackWeight) { 95 | // 第一个物品选了 96 | states[weights[0]] = values[0]; 97 | } 98 | 99 | for (int i = 1; i < n; i++) { 100 | for (int j = knapsackWeight-weights[i]; j >= 0; --j) { 101 | //把第i个物品放入背包 102 | if (states[j] >= 0) { 103 | states[j+weights[i]] = Math.max(states[j] + values[i], states[j+weights[i]]); 104 | } 105 | } 106 | } 107 | 108 | // 所有阶段结束后求出 states 中的最大解 109 | int max = Integer.MIN_VALUE; 110 | for (int j = knapsackWeight; j >= 0; j--) { 111 | max = Math.max(max, states[j]); 112 | } 113 | return max; 114 | } 115 | 116 | 117 | public static void main(String[] args) { 118 | int result = knapsack(weights, values, knapsackWeigth); 119 | System.out.println("result = " + result); 120 | } 121 | } --------------------------------------------------------------------------------