|
2 | 2 |
|
3 | 3 | import java.util.*; |
4 | 4 |
|
| 5 | +// Based off Cracking the Coding Interview 6th Edition's Solution 1 |
| 6 | + |
5 | 7 | // Algorithm |
6 | 8 | // |
7 | 9 | // 1. Create Box class with `.canBeAbove(Box other)` function. |
8 | | -// 2. Sort Boxes in descending height order. |
| 10 | +// 2. Sort Boxes in descending height order. This ensures we don't have to look backwards in the list |
9 | 11 | // - This problem uses `<` instead of `<=` for Box heights. If we have 2 Boxes of equal height, |
10 | | -// and [width, height, depth] of [4,4,4] and [7,4,7], it doesn't matter which Box comes first |
11 | | -// after sorting, since both `Box`es can't be in the final solution. Box 1 cannot be on top of Box 2, |
12 | | -// and Box 2 cannot be on top of Box 1. |
13 | | -// 3. For each Box, you have 2 choices: |
14 | | -// 1. Use the Box |
15 | | -// 2. Don't use the Box. |
16 | | -// 4. Recursively try both options. Return the larger height returned from these 2 options. |
17 | | -// 5. Use a Map<Integer, Integer> cache to save solutions to sub-problems |
18 | | -// 1. "key" is the index `i` in the list of boxes |
19 | | -// 2. "value" is the max height possible from that Box to the end of our list of Boxes |
| 12 | +// such as [width, height, depth] of [4,4,4] and [7,4,7], it doesn't matter which Box comes first |
| 13 | +// after sorting, since both Boxes can't be in the final solution. Box 1 cannot be on top of Box 2, |
| 14 | +// and Box 2 cannot be on top of Box 1. |
| 15 | +// 3. Experiment with each Box as a bottom and build the biggest stack possible. |
| 16 | +// 4. Use a "Map<Integer, Integer>" cache to save solutions to sub-problems |
| 17 | +// 1. "key" is the index `i` in the List of boxes |
| 18 | +// 2. "value" is the max height possible with "Box i" at the bottom |
20 | 19 |
|
21 | 20 | public class StackOfBoxes { |
22 | 21 | public static int findMaxHeight(List<Box> boxes) { |
23 | 22 | if (boxes == null || boxes.size() == 0) { |
24 | 23 | return 0; |
25 | 24 | } |
26 | 25 | Collections.sort(boxes, (box1, box2) -> box2.height - box1.height); // sort in descending height order |
27 | | - return findMaxHeight(boxes, 0, null, new HashMap<>(boxes.size())); |
| 26 | + return findMaxHeight(boxes, -1, new HashMap<Integer, Integer>()); |
28 | 27 | } |
29 | | - |
30 | | - private static int findMaxHeight(List<Box> boxes, int i, Box bottom, Map<Integer, Integer> cache) { |
31 | | - if (i >= boxes.size()) { |
32 | | - return 0; |
33 | | - } else if (cache.containsKey(i)) { |
34 | | - return cache.get(i); |
| 28 | + |
| 29 | + private static int findMaxHeight(List<Box> boxes, int bottomIndex, Map<Integer, Integer> cache) { |
| 30 | + if (cache.containsKey(bottomIndex)) { |
| 31 | + return cache.get(bottomIndex); |
35 | 32 | } |
36 | | - |
37 | | - // height with this Box |
38 | | - Box newBottom = boxes.get(i); |
39 | | - int heightWith = 0; |
40 | | - if (newBottom.canBeAbove(bottom)) { |
41 | | - heightWith = newBottom.height + findMaxHeight(boxes, i + 1, newBottom, cache); |
| 33 | + |
| 34 | + int maxHeight = 0; |
| 35 | + Box bottom = bottomIndex == -1 ? null : boxes.get(bottomIndex); |
| 36 | + |
| 37 | + for (int i = bottomIndex + 1; i < boxes.size(); i++) { |
| 38 | + if (boxes.get(i).canBeAbove(bottom)) { |
| 39 | + int height = findMaxHeight(boxes, i, cache); |
| 40 | + maxHeight = Math.max(maxHeight, height); |
| 41 | + } |
42 | 42 | } |
43 | | - |
44 | | - // height without this Box |
45 | | - int heightWithout = findMaxHeight(boxes, i + 1, bottom, cache); |
46 | | - |
47 | | - int max = Math.max(heightWith, heightWithout); |
48 | | - cache.put(i, max); |
49 | | - return max; |
| 43 | + |
| 44 | + maxHeight += bottomIndex == -1 ? 0 : bottom.height; |
| 45 | + cache.put(bottomIndex, maxHeight); |
| 46 | + return maxHeight; |
50 | 47 | } |
51 | 48 | } |
52 | 49 |
|
53 | | -// Time Complexity: Without caching, the recursive part is O(2^n) since there are `n` boxes, |
54 | | -// and 2 options for each box (use or don't use). With caching, the recursive part is just O(n). |
55 | | -// The total time complexity is therefore O(n log n) due to sorting. |
| 50 | +// Time Complexity: O(n^2) |
56 | 51 | // Space Complexity: O(n) due to recursion. |
57 | 52 |
|
58 | | -// - A Dynamic Programming Iterative solution (using an array instead of a HashMap) |
59 | | -// is also possible. It will have the same Time/Space complexity as the above solution. |
| 53 | + |
| 54 | +// Alternate Solution: Cracking the Coding Interview 6th Edition Solution 2 |
| 55 | +// |
| 56 | +// I didn't like this alternate solution since caching was done weirdly: Function had 2 changing |
| 57 | +// parameters (Box bottom, int offset) but cache only used 1 parameter. |
| 58 | + |
| 59 | + |
| 60 | +// Follow-up Question - What if rotation of boxes is allowed? |
| 61 | +// |
| 62 | +// 1. For each box, rotate it to all 6 possibilities: [w h d], [w d h], [h w d] [h d w], [d w h], [d h w]. |
| 63 | +// 2. Notice it's impossible to stack 2 of these 6 boxes on top of each other, so if we insert all 6 boxes |
| 64 | +// into our list, we're still ensured only 1 of them can be selected for our solution. |
| 65 | +// 3. Create `List<Box> boxes` that is 6 times as large as the original list of boxes. Solve the problem for this list. |
0 commit comments