├── .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 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 1.8
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
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 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 | 
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 | }
--------------------------------------------------------------------------------