所謂排列,是指從給定的元素序列中依次取出元素,須要考慮取出順序。好比,取出元素3, 5,因取出順序的不一樣,則造成的序列{3, 5}與{5, 3}是不一樣的排列序列。對於長度爲n的元素序列取出k個元素,則共有A(n, k)種取法。所謂組合,也是從元素序列中依次取出元素,與排列不一樣的是不須要考慮取出順序;所以其取法數爲C(n, k)。java
LeetCode有兩個問題分屬於組合、排列:77. Combinations 與 46. Permutations。數組
要求給出對於序列1~n 的取出k個元素的各類取法。採用DFS模擬組合時,可看作節點i與節點j(j = i+1, … , n)都相鏈接,而後DFS遍歷整張有向圖,代碼實現以下:優化
public List<List<Integer>> combine(int n, int k) { List<List<Integer>> result = new ArrayList<>(); if (n <= 0 || n < k) { return result; } List<Integer> tmp = new ArrayList<>(); dfs(n, k, 1, tmp, result); return result; } // DFS for combination private void dfs(int n, int k, int start, List<Integer> tmp, List<List<Integer>> result) { if (tmp.size() == k) { result.add(new ArrayList<Integer>(tmp)); return; } for (int i = start; i <= n; i++) { tmp.add(i); dfs(n, k, i + 1, tmp, result); tmp.remove(tmp.size() - 1); // remove the last } }
DFS實現排列與組合相相似,惟一不一樣之處在於,節點i與其餘全部節點都鏈接。所以,所構造的圖是一個徹底連通圖。DFS實現排列以下:spa
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> result = new ArrayList<>(); if (nums.length == 0) { return result; } List<Integer> tmp = new ArrayList<>(); dfs(nums, tmp, result); return result; } // DFS for permutation private void dfs(int[] nums, List<Integer> tmp, List<List<Integer>> result) { int n = nums.length; if (tmp.size() == n) { result.add(new ArrayList<>(tmp)); return; } for (int i = 0; i < n; i++) { // nums[i] has not been visited if (!tmp.contains(nums[i])) { tmp.add(nums[i]); dfs(nums, tmp, result); tmp.remove(tmp.size() - 1); } } }
上述代碼中,能夠用一個visit數組來標記節點是否被訪問,這樣優化將contains的時間複雜度從\(O(n)\)優化到\(O(1)\)。code