@xmruibi
2015-03-03T00:00:25.000000Z
字数 9591
阅读 887
Algorithm
Backtracking Summary -- Permutation&Combiniation Problems:
回溯法的是来解决多维向量的枚举问题,其核心:
- 递归的枚举每个维度的值
- 既然是递归,那就用终止条件。一般的终止条件是找到了满足条件的解,或者已经超出了解的范围,无需再递归下去。
- 找到某一维的候选值。解排列组合的重中之重。常用的技巧是使用标志位来记录某一维是否可用。
- make_move和unmake_move在对每个候选值递归调用前后发生,这里的unmake_move非常的重要,保证了回溯法的正确!
Refenrece: http://www.csie.ntnu.edu.tw/~u91029/Backtracking.html
backtrack( [v1,...,vn] ) // [v1,...,vn] multi-dimension array{/* generated one set of multi-dimension array */if ( solution[] is well-generated ){check and record solution;return;}/* 窮舉這個維度的所有數值,並遞迴到下一個維度 */for ( x = each value of current dimension ){solution[dimension] = x;backtrack( dimension + 1 );}}main: call backtrack( [] ); // 從第一個維度開始依序枚舉
1.Permutations
2.Permutations II
3.Next Permutation
4.Permutation Sequence
Boolean isUsed;public List<List<Integer>> permute(int[] num) {isUsed = new Boolean[num.length];List<List<Integer>> result = new ArrayList<List<Integer>>();helper(0,num,new ArrayList<Integer>(),result);return result;}private void helper(int start, int[] num, List<Integer> curList,List<List<Integer>> result){if(start == num.length){result.add(new ArrayList<Integer>(curList));return;}for(int i=0;i<num.length;i++){if(!isUsed[i]){curList.add(num[i]);isUsed[i] = true;helper(start+1,num,curList); //startisUsed[i] = false;curList.remove(start);}}}
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
想要避免產生重複排列,在枚舉某一個位置的字母時,避免重複填入一樣的字母。
若將輸入的字串由小到大排序,字母會依照順序出現,所以只需檢查方上一个字母,判斷一不一樣,就可以避免填入一樣的字母了。
Boolean isUsed;public List<List<Integer>> permute(int[] num) {isUsed = new Boolean[num.length];List<List<Integer>> result = new ArrayList<List<Integer>>();Array.sort(num); //!!!!helper(0,num,new ArrayList<Integer>(),result);return result;}private void helper(int start, int[] num, List<Integer> curList,List<List<Integer>> result){if(start == num.length){result.add(new ArrayList<Integer>(curList));return;}for(int i=0;i<num.length;i++){// Notice this!! avoid senarios like: abb, abb;if (i>0&&num[i]==num[i-1]&&(!isUsed[i-1]))continue;if(!isUsed[i]){curList.add(num[i]);isUsed[i] = true;helper(start+1,num,curList); //startisUsed[i] = false;curList.remove(start);}}}
1). Lexicographical order
- Step1:从最右开始,找到第一个比右边小的数字4(因为4<7, 而7>5>2>1),再从最右开始,找到4右边比4大的数字5(因为4>2>1而4<5)。4、5也即从右往左找到的第一个对严格的升序对。
- Step2:交换4、5。
- Step3:倒置5右边的7421为1247.即得到下一个排列839651247.
private static void LexicographicalOrder(int[] num) {//从最右开始,找到第一个比右边小的数字num[minFromRight]int minFromRight=-1;for(int i=num.length-1;i>0;i--){if(num[i]>num[i-1]){minFromRight = i-1;break;}}// 再从最右开始,找到num[minFromRight]右边比它大的数字并交换if(minFromRight>=0)for(int i=num.length-1;i>minFromRight;i--){if(num[i]>num[minFromRight]){int tmp = num[i];num[i]=num[minFromRight];num[minFromRight] = tmp;minFromRight++;break;}}elseminFromRight++;// 从minFromRight 位置以后的数做倒序处理reverseNum(num, minFromRight);}private static void reverseNum(int[] num, int index) {// TODO Auto-generated method stubint end = num.length-1;while(index<end){int tmp = num[index];num[index] = num[end];num[end] = tmp;index++;end--;}}
public String getPermutation(int n, int k) {StringBuilder sb = new StringBuilder();int[] fact = new int[n]; // saving the factorial// prepare the factorialsfor (int i=1; i<=n; ++i) {sb.append(i);if (i == 1)fact[i-1] = 1;elsefact[i-1] = fact[i-2] * i;}// make sure k is inside the sequencek = k%fact[n-1];k--;for(int i=0;i<n;i++){int id = k/fact[(n-1)-1-i]+i; // +i!!??// get target numberchar num = sb.charAt(id);// shift numberfor(int j=id;j>i;j--)sb.setCharAt(j, sb.charAt(j--));sb.setCharAt(i, num);k=k%fact[(n-1)-1-i];}return sb.toString();}
Subsets
Subsets II
Combinations
Combination Sum
Combination Sum II
// METHOD ONE:public static void subset(int[] num,int index) {if(index == num.length-1){for(int i=0;i<num.length;i++){if(table[i])System.out.print(num[i]+" ");}System.out.println("");return;}table[index] = true;subset(num, index+1);table[index] = false;subset(num, index+1);}// METHOD TWO:private static void subset2(int[] num,int index, int size) {// TODO Auto-generated method stubif(index == num.length-1){for(int i=0;i<size;i++){System.out.print(num[i]+" ");}System.out.println("");return;}num[size] = index;subset2(num,index+1,size+1);subset2(num,index+1,size);}另有二进制转化法, 请查阅 [Bit Operation][https://www.zybuluo.com/xmruibi/note/71723] 章节即将每个组合与一个二进制数对应起来,枚举二进制的同时,枚举每个组合。这种方法由于有一一对应的特质,所以要求原数组中的各元素应该各不相同,否则求出来的子集和会有重复元素。
public static List<List<Integer>> subsetsWithDup(int[] num) {List<List<Integer>> result = new ArrayList<List<Integer>>();List<Integer> subset = new ArrayList<Integer>();Arrays.sort(num);subsetwDuphelper(num, 0, subset, result);return result;}private static void subsetwDuphelper(int[] num, int start,List<Integer> subset, List<List<Integer>> result) {result.add(new ArrayList<>(subset));for (int i = start; i < num.length; i++) {// 注意此处 i!=startif (i !=start && num[i] == num[i - 1])continue;subset.add(num[i]);subsetwDuphelper(num, i + 1, subset, result);subset.remove(subset.size() - 1);}}
//METHOD ONE:public List<List<Integer>> combine(int n, int k){List<List<Integer>> result = new ArrayList<List<Integer>>();combine(n, k, 1, result, new ArrayList<Integer>());return result;}public void combine(int n, int k , int start, List<List<Integer>> result, ArrayList<Integer> list){// stop pointif(k==0){result.add(list);return;}for(int i= start;i<=n;i++){ArrayList<Integer> curlist = (ArrayList<Integer>)list.clone();curList.add(i);combine(n,k-1,i+1,result,curlist);}}
Given a set of candidate numbers (C) (Allow duolicated element) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
public List<List<Integer>> combinationSum2(int[] num, int target) {List<List<Integer>> res = new ArrayList<List<Integer>>();int m = num.length;if(m == 0) return res;Arrays.sort(num);List<Integer> item = new ArrayList<Integer>();helper(num, 0, target, item, res);return res;}private void helper(int[] num, int start, int target, ArrayList<Integer> list, ArrayList<ArrayList<Integer>> result){if(target == 0){result.add(list);return;}for(int i=start;i<num.length;i++){if(i>0&&num[i]==num[i-1])continue;list.add(num[i]);helper(num,i,target-num[i],list, result);list.remove(i);}}
Given a set of candidate numbers (C) (No duplicated element) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
public List<List<Integer>> combinationSum2(int[] num, int target) {//1240List<List<Integer>> res = new ArrayList<List<Integer>>();int m = num.length;if(m == 0) return res;Arrays.sort(num);List<Integer> item = new ArrayList<Integer>();dfs(num, target, 0, item, res);return res;}public void dfs(int[] num, int target, int start, List<Integer> item, List<List<Integer>> res){if(target == 0){res.add(new ArrayList<Integer>(item));return;}if(target < 0) return;for(int i = start; i < num.length; i++){if(i > start && num[i] == num[i-1]) continue;// avoid duplicate solutionsitem.add(num[i]);dfs(num, target - num[i], i + 1, item, res);item.remove(item.size() - 1);}}
public List<String> letterCombinations(String digits) {List<String> combList = new ArrayList<String>();combList.add("");for (int i = 0; i < digits.length(); i++) {List<String> subList = new ArrayList<String>();String letter = getLetters(digits.charAt(i));for(int j=0;j<combList.size();j++){ // get previous list element then add letter on themfor(int k = 0;k<letter.length();k++){subList.add(combList.get(j)+letter.charAt(k));}}combList = subList;}return combList;}private String getLetters(char digit) {switch (digit) {case '2':return "abc";case '3':return "def";case '4':return "ghi";case '5':return "jkl";case '6':return "mno";case '7':return "pqrs";case '8':return "tuv";case '9':return "wxyz";case '0':return " ";default:return "";}}
public List<String> restoreIpAddresses(String s) {List<String> res = new ArrayList<String>();if(s==null || s.length()==0||s.length()>12)return res;helper(0, 1, "", s, res);return res;}// separated into 4 segmentsprivate void helper(int index, int segment, String cur, String addr,List<String> res) {if(index>=addr.length())return;if(segment == 4){String str = addr.substring(index,addr.length());if(isValid(str)){res.add(cur+"."+str);return;}}for (int i = 1; i < 4 && (i + index <= addr.length()); i++) {String str = addr.substring(index, index + i);if (isValid(str)) {if (segment == 1) {helper(index + i, segment + 1, str, addr, res);} else {helper(index + i, segment + 1, cur +"."+ str, addr, res);}}}}private boolean isValid(String str) {if (str == null || str.length() > 3)return false;if (str.charAt(0) == '0' && str.length() > 1)return false;if (Integer.parseInt(str) <= 255 && Integer.parseInt(str) >= 0)return true;return false;}
public static List<String> generateParenthesis(int n) {List<String> list = new ArrayList<String>();generate(n, 0, "", list);return list;}/**** @param fpNum the number of "("* @param bpNum the number of ")"* @param product current product* @param list*/private static void generate(int fpNum, int bpNum, String product,List<String> list) {// TODO Auto-generated method stubif (fpNum == 0 && bpNum == 0) {list.add(product);return;}if (fpNum > 0) {generate(fpNum - 1, bpNum + 1, product + "(", list);}if (bpNum > 0) {generate(fpNum, bpNum - 1, product + ")", list);}}
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
public static List<List<String>> partition(String s) {List<List<String>> res = new ArrayList<List<String>>();backTrack(s, 0, new ArrayList<String>(), res);return res;}private static void backTrack(String s, int start,ArrayList<String> curlist, List<List<String>> res) {// TODO Auto-generated method stubif (curlist.size() > 0 && start == s.length()) {res.add(new ArrayList<String>(curlist));return;}for (int i = start + 1; i <= s.length(); i++) {String str = s.substring(start, i);if (isPalindrome(str)) {curlist.add(str);backTrack(s, i, curlist, res);curlist.remove(curlist.size() - 1);}}}private static boolean isPalindrome(String str) {int l = 0;int r = str.length() - 1;while (l < r) {if (str.charAt(l) != str.charAt(r))return false;l++;r--;}return true;}