递归相关的题目 找一个字符串的不重复的子序列 递归的思路:每次按要这个位置的字符和不要这个位置的字符两种情况去递归,当i = s.length()时,保存当前的子序列。用hashSet去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static String[] generatePermutation(String str) { char [] s = str.toCharArray(); HashSet<String> set = new HashSet <>(); f2(s, 0 , new char [s.length], 0 , set); int m = set.size(); String[] ans = new String [m]; int i = 0 ; for (String cur : set) { ans[i++] = cur; } return ans; } public static void f2 (char [] s, int i, char [] path, int size, HashSet<String> set) { if (i == s.length) { set.add(String.valueOf(path, 0 , size)); } else { path[size] = s[i]; f2(s, i + 1 , path, size + 1 , set); f2(s, i + 1 , path, size, set); } }
找一个数组中的所有子数组,顺序可以任意 思路:先将数组排序,然后每次对同一个数讨论,要0个,要1个… 还需要知道对于这个数来说下一个跟自己不同的数的位置,然后调用递归从下个不同的数开始。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public List<List<Integer>> subsetsWithDup (int [] nums) { List<List<Integer>> ans = new ArrayList <>(); int [] path = new int [11 ]; Arrays.sort(nums); findSubsetsWithDup(0 ,nums,0 ,path,ans); return ans; } public void findSubsetsWithDup (int i,int [] nums,int size,int [] path,List<List<Integer>> ans) { if (i==nums.length){ List<Integer> temp =new ArrayList <>(); for (int j=0 ;j<size;j++){ temp.add(path[j]); } ans.add(temp); }else { int j = i+1 ; while (j < nums.length && nums[j] == nums[i]){ j++; } findSubsetsWithDup(j,nums,size,path,ans); for (;i<j;i++){ path[size++] = nums[i]; findSubsetsWithDup(j,nums,size,path,ans); } } }
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 思路: 每次拿一个数跟当前下标的第一个数交换,然后递归调用将下标+1,递归完成后再交换回来。终止条件就算下标==nums.length
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public List<List<Integer>> permute (int [] nums) { List<List<Integer>> ans =new ArrayList <>(); findPermute(nums,0 ,ans); return ans; } public void findPermute (int [] nums,int i,List<List<Integer>> ans) { if (i == nums.length){ List<Integer> temp = new ArrayList <>(); for (int j=0 ;j<nums.length;j++){ temp.add(nums[j]); } ans.add(temp); }else { for (int j = i;j<nums.length;j++){ swap(nums,i,j); findPermute(nums,i+1 ,ans); swap(nums,i,j); } } }
拓展:如果有重复数字,则需要去重
思路: 每次递归的时候维护一个set这个set记录哪些数来到过我这个下标,如果发现一个数已经来到过,则不进行交换,也不递归跳过即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void f (int [] nums, int i, List<List<Integer>> ans) { if (i == nums.length) { List<Integer> cur = new ArrayList <>(); for (int num : nums) { cur.add(num); } ans.add(cur); } else { HashSet<Integer> set = new HashSet <>(); for (int j = i; j < nums.length; j++) { if (!set.contains(nums[j])) { set.add(nums[j]); swap(nums, i, j); f(nums, i + 1 , ans); swap(nums, i, j); } } } }
N皇后问题 思路:维护一个路径数组path[],path的第几个数的值表示第几行的皇后放在第几列,每次循环每一行的列去判断是否合法,如果合法则递归调用下一行,并且size++.终止条件是size == n 即找到合法的一个解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public static List<List<String>> solveNQueens (int n) { List<List<String>> ans = new ArrayList <>(); int [] path = new int [n]; findSolveNQueens(0 ,n,path,ans); return ans; } public static void findSolveNQueens (int size,int n,int [] path,List<List<String>> ans) { if (size == n){ List<String> temp = new ArrayList <>(); for (int i = 0 ;i<size;i++){ StringBuilder sb = new StringBuilder (); int pos = path[i]; for (int j = 0 ;j<pos;j++){ sb.append('.' ); } sb.append('Q' ); for (int j = pos+1 ;j<n;j++){ sb.append('.' ); } temp.add(sb.toString()); } ans.add(temp); }else { for (int i = 0 ;i<n;i++){ if (isvalid(i,path,size)){ path[size] = i; findSolveNQueens(size+1 ,n,path,ans); } } } } public static boolean isvalid (int i ,int [] path,int size) { for (int k = 0 ; k< size; k++){ if (i==path[k]){ return false ; } if ( Math.abs(size-k) == Math.abs(i-path[k])){ return false ; } } return true ; }
拓展: 利用位运算去判断当前位置是否合法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public static int totalNQueens2 (int n) { if (n < 1 ) { return 0 ; } int limit = (1 << n) - 1 ; return f2(limit, 0 , 0 , 0 ); } public static int f2 (int limit, int col, int left, int right) { if (col == limit) { return 1 ; } int ban = col | left | right; int candidate = limit & (~ban); int place = 0 ; int ans = 0 ; while (candidate != 0 ) { place = candidate & (-candidate); candidate ^= place; ans += f2(limit, col | place, (left | place) >> 1 , (right | place) << 1 ); } return ans; }