Java递归综合练习

递归综合练习

全排列

题目解析 :给一个不含重复元素的数组,求出其数的所有排列可能

将这个所有结果可以化成一个决策树


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]);

       
    }
}
相关推荐
前端小白在前进6 小时前
力扣刷题:删除排序链表的重复元素Ⅱ
算法·leetcode·链表
张哈大6 小时前
读懂大模型核心:Transformer 与 AI 发展的底层逻辑
java·神经网络·机器学习
小灰灰搞电子6 小时前
Qt SCXML 模块详解
开发语言·qt
魔镜前的帅比6 小时前
(开源项目)xsun_workflow_jira
java·jira
JAVA+C语言6 小时前
Python+Django 核心介绍
开发语言·python·django
中年程序员一枚6 小时前
不想花钱买会员,自己动手用python制作视频
开发语言·python·音视频
TH_16 小时前
15、IDEA可视化操作代码分支
java
江公望6 小时前
为什么Rust的编译工具依赖C语言的编译工具?
开发语言·rust
smileNicky6 小时前
分组拖动排序功能全流程实现(前端Sortable.js + 后端Java批量更新)
java·前端·javascript