递归综合练习
全排列

题目解析 :给一个不含重复元素的数组,求出其数的所有排列可能
将这个所有结果可以化成一个决策树


java
class Solution {
List<List<Integer>> ret;
List<Integer> path;
boolean[] check;
public List<List<Integer>> permute(int[] nums) {
ret = new ArrayList<>();
path = new ArrayList<>();
check = new boolean[nums.length];
dfs(nums);
return ret;
}
public void dfs(int[] nums) {
//path长度等于nums长度记录结果
if (path.size() == nums.length) {
ret.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
//只能放path中没有的数
if (check[i] == false) {
path.add(nums[i]);
check[i] = true;
dfs(nums);
//回溯
path.remove(path.size() - 1);
check[i] = false;
}
}
}
}
全排列||

题目解析 :给了一个包含重复元素的序列,返回所有不同的全排列
上一题是给的不同元素的全排列,但是全排列中不允许出现重复的,这里和上题目思路整体一样,只不过这里剪枝需要特殊考虑重复问题
剪枝
1.同一个数不可以重复选 2. 一个节点分支下,相同元素只可以选择一次因此这里判断何时递归插入要将两个剪枝都要判断
这里通过check标记已经选过的数,并将数组排序 方便考虑剪枝2
不合法情况 :选择数被选过 || 同一层下选择的数这层已经选过
合法情况 :选择数没备选 && 同一层下选择的数这层未被选过合法不合法无非就是符合和判断条件修改一下


java
class Solution {
List<List<Integer>> ret;
List<Integer> path;
boolean[] check;
public List<List<Integer>> permuteUnique(int[] nums) {
ret = new ArrayList<>();
path = new ArrayList<>();
check = new boolean[nums.length];
Arrays.sort(nums);
dfs(nums);
return ret;
}
public void dfs(int[] nums) {
//path长度等于nums长度记录结果
if (path.size() == nums.length) {
ret.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
//判断合法
if (check[i] == false && (i == 0 || (nums[i] != nums[i - 1]) || check[i - 1] == true)) {
path.add(nums[i]);
check[i] = true;
dfs(nums);
path.remove(path.size() - 1);
check[i] = false;
}
}
}
}
java
//不合法直接continue跳过即可
//判断是否合法
//1.未被使用过
//2.这个数存在重复,并且这一层没有用这个相同的数
if (check[i] || (i > 0 && nums[i] == nums[i - 1] && !check[i - 1])) {
continue;
}
path.add(nums[i]);
check[i] = true;
dfs(nums);
//回溯
path.remove(path.size() - 1);
check[i] = false;
}
子集

题目解析 :就是求出所有子集
解法一:每个数字只有两种情况选或者不选
解法二:不同的数进行选选择,选一个数、选两个、选三个............


java
//解法一
class Solution {
List<List<Integer>> ret;
List<Integer> path;
public List<List<Integer>> subsets(int[] nums) {
ret = new ArrayList<>();
path = new ArrayList<>();
dfs(nums,0);
return ret;
}
public void dfs(int[] nums,int pos){
//出口
if(pos == nums.length){
ret.add(new ArrayList<>(path));
return;
}
//选
path.add(nums[pos]);
dfs(nums,pos+1);
path.remove(path.size()-1);//恢复到选之前位置
//不选
dfs(nums,pos+1);
}
}
java
//解法二
class Solution {
List<List<Integer>> ret;
List<Integer> path;
public List<List<Integer>> subsets(int[] nums) {
ret = new ArrayList<>();
path = new ArrayList<>();
dfs(nums, 0);
return ret;
}
public void dfs(int[] nums, int pos) {
//进入每一层都是一个结果
ret.add(new ArrayList<>(path));
for(int i = pos;i < nums.length;i++){
path.add(nums[i]);
dfs(nums,i+1);
//回溯1
path.remove(path.size()-1);
}
}
}
找出所有子集的异或总和再求和

题目解析 :一个数组中可以有多个子集,求出每个子集异或结果进行和
可以发现这个题目和上一题找子集一样,这里不是返回子集,而是将子集中元素异或得出的结果
解法一 :每个数字只有两种情况选或者不选
解法二 :不同的数进行选选择,子集选一个数、选两个、选三个............此时这里进行回溯方式不同,上面是去掉这个数,这里因为是异或
回溯:再异或一次当前数(相同两个数异或为0)
java
class Solution {
int ret;
int path;//记录当前值
public int subsetXORSum(int[] nums) {
ret = 0;
path = 0;
dfs(nums,0);
return ret;
}
public void dfs(int[] nums,int pos){
ret += path;
//从pos位置开始防止有重复结果
for(int i = pos; i < nums.length;i++){
path ^= nums[i];
dfs(nums,i+1);
//回溯
path ^= nums[i];//相同两个数^为0,所以就是结果
}
}
}
java
public void dfs(int[] nums,int pos){
if(pos == nums.length){
ret += path;
return;
}
//选
path ^= nums[pos];
dfs(nums,pos+1);
path ^= nums[pos];//回溯
//不选
dfs(nums,pos+1);
}
电话号码的字母组合

题目解析 :就是给了一串字符电话号码,每个数都有对应的字符串,求出其字符串中字母组合关系
思想 :和找出所有子集,只不过这里是必须选 ,并且不止一个字符串因此这里dfs方法中,需要先获取当前是那个字符串,再进行和找出所有子集类似的操作

java
class Solution {
List<String> ret;
StringBuffer path;
String[] hash = new String[] { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };
public List<String> letterCombinations(String digits) {
ret = new ArrayList<>();
path = new StringBuffer();
if(digits.length() == 0){
return ret;
}
dfs(digits, 0);
return ret;
}
public void dfs(String digits, int pos) {
if (pos == digits.length()) {
ret.add(path.toString());
return;
}
//当前字符串
String cur = hash[digits.charAt(pos) - '0'];
for(int i = 0;i < cur.length();i++){
path.append(cur.charAt(i));
dfs(digits,pos+1);//下一层
//回溯
path.deleteCharAt(path.length()-1);
}
}
}
括号生成

题目解析 :求出所有表示n个有效括号对的括号组合
有效括号组合:左括号数量 == 右括号数量 ,并且从头开始的任意一个子串 左括号数量 >= 右括号数量


java
class Solution {
List<String> ret;
StringBuffer path;
int left;
int right;
int n;
public List<String> generateParenthesis(int num) {
ret = new ArrayList<>();
path = new StringBuffer();
left = 0;
right = 0;
n = num;
if (n == 0) {
return ret;
}
dfs();
return ret;
}
public void dfs() {
if (right == n) {
ret.add(path.toString());
return;
}
if (left < n) {
path.append("(");
left++;
dfs();
path.deleteCharAt(path.length() - 1);//回溯
left--;
}
if (right < left) {
path.append(")");
right++;
dfs();
path.deleteCharAt(path.length() - 1);//回溯
right--;
}
}
}
组合

题目解析 :就是数学中的组合,在[1,n]中找k个数的组合
思想:和上面找子集思想一样,找子集是把所有数的组合都找出来 ,这里是相当于只找k个数的子集,找子集中每一个层的节点都是结果,这里第k层叶子节点是结果


java
class Solution {
List<Integer> path;
List<List<Integer>> ret;
int n,k;
public List<List<Integer>> combine(int _n, int _k) {
path = new ArrayList<>();
ret = new ArrayList<>();
n = _n;
k = _k;
dfs(1);
return ret;
}
public void dfs(int pos){
if(path.size() == k){
ret.add(new ArrayList<>(path));
return;
}
for(int i = pos;i <= n;i++){
path.add(i);
dfs(i+1);
path.remove(path.size()-1);
}
}
}
目标和

题目解析 :一串数,可以选择加号或者减号,让这些数进行计算最终结果为target目标值所有符号选择的数
思想:和子集中选不选思想一样,只不过这里是选择加号还是减号
可以将path定义成全局变量,也可以path定义成全局变量


java
//path作为全局变量
class Solution {
int ret;
int path;
int target;
public int findTargetSumWays(int[] nums, int _target) {
target = _target;
dfs(nums, 0);
return ret;
}
public void dfs(int[] nums, int pos) {
//到了叶子节点并且这个路径和为target
if (pos == nums.length ) {
if(path == target){
ret++;
return;
}
return;
}
//选择加号
path += nums[pos];
dfs(nums, pos + 1);
path -= nums[pos];//回溯
//选择减号
path -= nums[pos];
dfs(nums, pos + 1);
path += nums[pos];//回溯
}
}
java
class Solution {
int ret;
int target;
public int findTargetSumWays(int[] nums, int _target) {
target = _target;
dfs(nums, 0,0);
return ret;
}
public void dfs(int[] nums, int pos,int path) {
//到了叶子节点并且这个路径和为target
if (pos == nums.length ) {
if(path == target){
ret++;
}
return;
}
//选择加号
dfs(nums, pos + 1,path + nums[pos]);
//选择减号
dfs(nums, pos + 1,path - nums[pos]);
}
}