递归-----及一些题的递归思路

本文详细介绍了递归的基本概念、重要元素及其在解决实际问题中的应用,包括跳台阶问题、字符串排列、有重复项数字的排列、二叉搜索树的第k个节点以及括号生成等经典实例。通过递归分析,展示了如何利用递归解决复杂问题,并提供了相应的Java代码实现。
摘要由CSDN通过智能技术生成

目录

一、递归的基本常识     

二、常见题目的递归分析

1.跳台阶

 2.字符串的排列

3.有重复项数字的所有排列

4.二叉搜索树的第k个节点

5.括号生成


一、递归的基本常识     

   递归的思路是将大问题,分解成小问题,直到最后不能再分的情况,求出结果,再将结果汇总成最终问题的解。是一种典型的分治思想。

        递归,有2个比较重要的元素。一个是递归式,一个是终止条件

        递归式,也就是问题拆分过程中,如何自己调用自己,或者简介调用自己

        终止条件,也就是程序的退出条件。如果没有终止条件,就是死循环,java的话,会造成栈溢出,报stackoverflow的异常。

使用递归解决的问题,一般是获取最终的结果,或者是获取递归的过程

二、常见题目的递归分析

1.跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)

分析:

青蛙只有2种行为:一种跳1层,一种跳2层。如果跳到8级台阶,只有2种方案,一种从6条到8,另一种从7跳到8。也就是说跳到n级台阶,也只能有2种方案,n-2=>n  , n-1=>n

 

 对于分解图,可见,当n<=2的时候,青蛙的跳法确定,不需要拆解

代码如下:

public int jumpFloor(int target) {
        if(target<=2){
            return target;
        }
        return jumpFloor(target-1) + jumpFloor(target-2);
    }

 2.字符串的排列

输入一个长度为 n 字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。

例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。

分析:

        不考虑结果的正确,只产生ABC三个字母的全排列,可以得到如下代码

    public ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<>();
        if(str==null||str.length()==0) return list;
         char[] chars = str.toCharArray();
        createStr(chars,list,"");
        return list;
    }
    public void createStr(char[] chars,ArrayList<String> list,String s){
        if(s.length()==chars.length){
            list.add(s);
            return;
        }
        for(int i = 0;i<chars.length;i++){
            createStr(chars,list,s+chars[i]);
        }
    }

结果:

["AAA","AAB","AAC","ABA","ABB","ABC","ACA","ACB","ACC","BAA","BAB","BAC","BBA","BBB","BBC","BCA","BCB","BCC","CAA","CAB","CAC","CBA","CBB","CBC","CCA","CCB","CCC"]

现在考虑每个字母使用一次之后不再使用,因此,递归时生成不包含已经使用的新char[]

    public ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<>();
        if(str==null||str.length()==0) return list;
         char[] chars = str.toCharArray();
        createStr(chars,list,"",chars.length);
        return list;
    }
    public void createStr(char[] chars,ArrayList<String> list,String s,int sum){
        // chars通过newChar每次都在变化,因此使用了一个新的变量存储总数量
        if(s.length()==sum){
            list.add(s);
            return;
        }
        for(int i = 0;i<chars.length;i++){
            createStr(newChar(chars,i),list,s+chars[i],sum);
        }
    }
    public char[] newChar(char[] chars,int i){
        String s = "";
        for(int j = 0;j<chars.length;j++){
            if(i!=j){
                s+=chars[j];
            }
        }
        return s.toCharArray();
    }

字符串不包含重复的情况,已经求出答案,如果包含重复,添加时判断是否包含if(list.contains(s))return;

3.有重复项数字的所有排列

给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。

输入:[1,1,2]

返回值:[[1,1,2],[1,2,1],[2,1,1]]

按递归路径,需要准备一些变量:

  1. 已经选了哪些数path
  2. boolean类型数组used

按所有数字全排列,得到 [[1,1,2],[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]

代码如下:

    public ArrayList<ArrayList<Integer>> permuteUnique(int[] num) {
        ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
        Deque<Integer> stack = new ArrayDeque<Integer>();
        int length = num.length;
        boolean used[] = new boolean[length];
        Arrays.sort(num);
        createStr(num,num.length,stack,used,ans);
        return ans;
    }
    public void createStr(int[] num,int sum,Deque<Integer> path,boolean[] used,ArrayList<ArrayList<Integer>> ans){
        if(path.size()==sum){
            ans.add(new ArrayList<>(path));
            return;
        }
        for(int i = 0;i<num.length;i++){
            if(used[i])continue;
            path.addLast(num[i]);
            used[i]=true;
            createStr(num,sum,path,used,ans);
            path.removeLast();
            used[i]=false;
        }
    }

在此基础上过滤掉每一层已经使用过的数字 (i!=0&&num[i] == num[i-1] )&&!used[i-1]

其中i!=0,是图上的第一层为空,不需要过滤,也为了防止数组下标越界

num[i]==num[i-1],是为了找到重复数字,在重复数字还没使用的情况used[i-1]=false下跳过

最终将重复项过滤掉

4.二叉搜索树的第k个节点

给定一棵结点数为n 二叉搜索树,请找出其中的第 k 小的TreeNode结点值。

1.返回第k小的节点值即可

2.不能查找的情况,如二叉树为空,则返回-1,或者k大于n等等,也返回-1

3.保证n个节点的值不一样

如输入{5,3,7,2,4,6,8},3时,二叉树{5,3,7,2,4,6,8}如下图所示:

该二叉树所有节点按结点值升序排列后可得[2,3,4,5,6,7,8],所以第3个结点的结点值为4,故返回对应结点值为4的结点即可。

分析:

二叉搜索树,可以直接使用中序遍历,得到有序的数组,然后获取目标位置的值,就是第k个小的数

    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param proot TreeNode类 
     * @param k int整型 
     * @return int整型
     */
    public int KthNode (TreeNode proot, int k) {
        // write code here、
        if(proot==null||k==0)return-1;
        ArrayList<Integer> list = new ArrayList<>();
        
        midOrder(proot,list,k);
        if(k>list.size())return-1;
        return list.get(k-1);
    }
        public void midOrder(TreeNode root,List<Integer> list,int k){
            if(list.size()==k)return;
            if(root!=null){
                midOrder(root.left,list,k);
                list.add(root.val);
                midOrder(root.right,list,k);
            }
        
    }

5.括号生成

给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。

例如,给出n=3,解集为:

"((()))", "(()())", "(())()", "()()()", "()(())"

分析:

对于括号,都是成对出现,可以想到最终括号的数量一定是2n,从而可以确定终止条件,生成括号数量2n,且左括号数量==右括号数量。

获得过程解需要记录生成过程的list,左右括号的数量left ,right

left<n,添加左括号,right<left ,添加有括号

不是left<n,right<n的原因是,括号成对出现,一定要先有左括号,再添加由括号,否则可能不成对,例如["(())","()()","())(",")(()",")()(","))(("]

    /**
     * 
     * @param n int整型 
     * @return string字符串ArrayList
     */
    public ArrayList<String> generateParenthesis (int n) {
        // write code here
        ArrayList<String> result = new ArrayList<>();
        
        create(n,0,0,"",result);
        return result;
    }
    
    public void create(int n,int left,int right,String s,ArrayList<String> result){
        if(s.length()==n<<1){
            result.add(s);
            return;
        }
        if(left<n){
            create(n,left+1,right,s+"(",result);
        }
        
        if(right<left){
            create(n,left,right+1,s+")",result);
        }
        return;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值