首页 > 代码库 > Leetcode: Matchsticks to Square && Grammar: reverse an primative array

Leetcode: Matchsticks to Square && Grammar: reverse an primative array

Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match girl has, please find out a way you can make one square by using up all those matchsticks. You should not break any stick, but you can link them up, and each matchstick must be used exactly one time.

Your input will be several matchsticks the girl has, represented with their stick length. Your output will either be true or false, to represent whether you could make one square using all the matchsticks the little match girl has.

Example 1:
Input: [1,1,2,2,2]
Output: true

Explanation: You can form a square with length 2, one side of the square came two sticks with length 1.
Example 2:
Input: [3,3,3,3,4]
Output: false

Explanation: You cannot find a way to form a square with all the matchsticks.
Note:
The length sum of the given matchsticks is in the range of 0 to 10^9.
The length of the given matchstick array will not exceed 15.

DFS, my solution is to fill each edge of the square one by one. DFS to construct the 1st, then 2nd, then 3rd, then 4th. For each edge I scan all the matches but only pick some, the others are discarded. These brings about two disadvantages: 1. I need to keep track of visited elem. 2. after the 1st edge is constructed, I have to re-scan to make the 2nd, 3rd, 4th, thus waste time! So my code get TLE in big case

 1 public class Solution {
 2     public boolean makesquare(int[] nums) {
 3         if (nums==null || nums.length==0) return false;
 4         int sideLen = 0;
 5         for (int num : nums) {
 6             sideLen += num;
 7         }
 8         if (sideLen % 4 != 0) return false;
 9         sideLen /= 4;
10         return find(nums, sideLen, 0, 0, new HashSet<Integer>());
11     }
12     
13     public boolean find(int[] nums, int sideLen, int completed, int curLen, HashSet<Integer> visited) {
14         if (curLen > sideLen) return false;
15         if (curLen == sideLen) {
16             completed++;
17             curLen = 0;
18             if (completed==4 && visited.size()==nums.length) return true;
19             if (completed==4 && visited.size()<nums.length) return false;
20         }
21         for (int i=0; i<nums.length; i++) {
22             if (!visited.contains(i)) {
23                 visited.add(i);
24                 if (find(nums, sideLen, completed, curLen+nums[i], visited))
25                     return true;
26                 visited.remove(i);
27             }
28         }
29         return false;
30     }
31 }

 

Better Solution: DFS, but only scan all the matches once! If a match can not make the 1st edge, then we do not discard it, we try 2nd, 3rd, 4th edge

referred to: https://discuss.leetcode.com/topic/72107/java-dfs-solution-with-explanation

 1 public class Solution {
 2     public boolean makesquare(int[] nums) {
 3         if (nums == null || nums.length < 4) return false;
 4         int sum = 0;
 5         for (int num : nums) sum += num;
 6         if (sum % 4 != 0) return false;
 7         
 8         return dfs(nums, new int[4], 0, sum / 4);
 9     }
10     
11     private boolean dfs(int[] nums, int[] sums, int index, int target) {
12         if (index == nums.length) {
13             if (sums[0] == target && sums[1] == target && sums[2] == target) {
14             return true;
15             }
16             return false;
17         }
18         
19         for (int i = 0; i < 4; i++) {
20             if (sums[i] + nums[index] > target) continue;
21             sums[i] += nums[index];
22             if (dfs(nums, sums, index + 1, target)) return true;
23             sums[i] -= nums[index];
24         }
25         
26         return false;
27     }
28 }

Updates on 12/19/2016 Thanks @benjamin19890721 for pointing out a very good optimization: Sorting the input array DESC will make the DFS process run much faster. Reason behind this is we always try to put the next matchstick in the first subset. If there is no solution, trying a longer matchstick first will get to negative conclusion earlier. Following is the updated code. Runtime is improved from more than 1000ms to around 40ms. A big improvement.

 1 public class Solution {
 2     public boolean makesquare(int[] nums) {
 3         if (nums==null || nums.length<4) return false;
 4         int sideLen = 0;
 5         for (int num : nums) {
 6             sideLen += num;
 7         }
 8         if (sideLen % 4 != 0) return false;
 9         sideLen /= 4;
10         Arrays.sort(nums);
11         reverse(nums);
12         return find(nums, new int[4], 0, sideLen);
13     }
14     
15     public boolean find(int[] nums, int[] edges, int index, int sideLen) {
16         if (index == nums.length) {
17             if (edges[0]==sideLen && edges[1]==sideLen && edges[2]==sideLen)
18                 return true;
19             else return false;
20         }
21         
22         for (int i=0; i<4; i++) {
23             if (edges[i]+nums[index] > sideLen) continue;
24             edges[i] += nums[index];
25             if (find(nums, edges, index+1, sideLen)) return true;
26             edges[i] -= nums[index];
27         }
28         return false;
29     }
30     
31     public void reverse(int[] nums) {
32         int l=0, r=nums.length-1;
33         while (l < r) {
34             int temp = nums[l];
35             nums[l] = nums[r];
36             nums[r] = temp;
37             l++;
38             r--;
39         }
40     }
41 }

 

Grammar: How to sort array in descending order: http://www.java67.com/2016/07/how-to-sort-array-in-descending-order-in-java.html

How to sort object array in descending order

First, let‘s see the example of sorting an object array into ascending order. Then we‘ll see how to sort a primitive array in descending order. In order to sort a reference type array e.g. String array, Integer array or Employee array, you need to pass the Array.sort() method a reverse Comparator.

Fortunately, you don‘t need to code it yourself, you can use Collections.reverseOrder(Comparator comp) to get a reverse order Comparator. Just pass your Comparator to this method and it will return the opposite order Comparator.

If you are using Comparator method to sort in natural order, you can also use the overloaded Collection.reverseOrder() method. It returns a Comparator which sorts in the opposite of natural order. In fact, this is the one you will be using most of the time.

Here is an example of sorting Integer array in descending order:

Integer[] cubes = new Integer[] { 8, 27, 64, 125, 256 };
Arrays.sort(cubes, Collections.reverseOrder());

How to sort primitive array in descending order

Now, let‘s see how to sort a primitive array e.g. int[], long[],  float[] or char[] in descending order. As I told, there are no Arrays.sort() method which can sort the array in the reverse order. Many programmers make mistake of calling the above Array.sort() method as follows:

int[] squares = { 4, 25, 9, 36, 49 };
Arrays.sort(squares, Collections.reverseOrder());


This is compile time error "The method sort(int[]) in the type Arrays is not applicable for the arguments (int[], Comparator<Object>)" because there is no such method in the java.util.Arrays class.

The only way to sort a primitive array in descending order is first to sort it in ascending order and then reverse the array in place as shown here. Since in place reversal is an efficient algorithm and doesn‘t require extra memory, you can use it sort and reverse large array as well. You can also see a good book on data structure and algorithm e.g. Introduction to Algorithms to learn more about efficient sorting algorithm e.g. O(n) sorting algorithm like Bucket sort.


 

Leetcode: Matchsticks to Square && Grammar: reverse an primative array