递归

递归三要素

1. 明确函数的作用

由我们自己定义

2. 寻找递归终止条件

递归就是函数自己调用自己,当参数为什么时,我们能够直接知道函数的结果,这时递归终止,将函数值进行返回

3. 找出函数的等价关系式(等价操作步骤)

我们不断缩小参数的范围,缩小之后要通过辅助的变量或操作使原函数的结果不变

侧重于函数的功能,忽略实现步骤

  1. 辅助的变量(缩小参数范围+变量):适用于数字计算之类的题目f(n)=n*f(n-1)
  2. 操作(缩小参数范围+操作):适用于有节点的数据结构(链表,树)reverseList(head)等价于reverseList(head.next)+改变一下1、2两个节点的指向

3.1 经验之谈

当我们在具有节点的数据结构中使用递归时,递归返回值有两类

  1. 我们要返回找到的节点

    直接return 递归函数(缩小范围的参数); 因为我们函数的目的是为了找到某个节点,所以getNode(node.left, key)与getNode(node, key)是等价的,最终结果都是同一个节点

    	//返回以node为根的二分搜索树中,键为key的节点
        //返回的是当前节点
        private Node getNode(Node node,K key){
            //如果根为空,返回null
            if (node==null)
                return null;
            //key<根节点的key,去左子树中寻找,缩小参数范围
            if (key.compareTo(node.key)<0)
                return getNode(node.left, key);
            //key>根节点的key
            if (node.key.compareTo(key)>0)
                return getNode(node.right, key);
            //key==node.kay
            else return node;
        }
    
  2. 我们要返回增删之后新的二分搜索树的根

    remove(node.left)显然不与remove(node)等价,因此我们要进行一些操作,使其等价

    node.left=remove(node.left);

    return node;

    //移除以node为根的二分搜索树中最小的节点
    //返回移除后新的二分搜索树的根
    private Node removeMin(Node node){
            //递归终止条件
            //当前节点的左子树为空,那么就为最小节点。
            //可能其还有右子树,我们提取出来他的右子树,其右节点理所当然成为新的二分搜索树的根
            if (node.left==null){
                //保存被删节点的右子树
                Node rightNode=node.right;
                //将被删除的节点从当前二叉树中脱离关系
                node.right=null;
                size--;
                return rightNode;
            }
            //removeMin(node)等价于removeList(node.left)+改变node.left指向新的右子树的根
            node.left= removeMin(node.left);
            return node;
        }
    

递归的优化

1. 考虑是否重复计算

使用递归可能会有很多的子问题被重复计算

我们可以把计算的结果保存起来,例如保存f(4),当再次要计算f(4)时,先判断之前是否计算过,如果计算过,直接把结果取出即可,不用重复递归计算了

用什么保存呢?可以用数组或者 HashMap 保存,我们用数组来保存把,把 n 作为我们的数组下标,f(n) 作为值,例如 arr[n] = f(n)。f(n) 还没有计算过的时候,我们让 arr[n] 等于一个特殊值,例如 arr[n] = -1。

当我们要判断的时候,如果 arr[n] = -1,则证明 f(n) 没有计算过,否则, f(n) 就已经计算过了,且 f(n) = arr[n]。直接把值取出来就行了。
// 我们实现假定 arr 数组已经初始化好的了。
int f(int n){
    if(n <= 1){
        return n;
    }
    //先判断有没计算过
    if(arr[n] != -1){
        //计算过,直接返回
        return arr[n];
    }else{
        // 没有计算过,递归计算,并且把结果保存到 arr数组里
        arr[n] = f(n-1) + f(n-2);
        reutrn arr[n];
    }
}

2. 考虑是否可以自底向上

对于递归,我们一般是从上往下递归,直到终止条件,再一层一层把值返回。而当层数太多,栈空间可能不够用

public int f(int n) {
       if(n <= 2)
           return n;
       int f1 = 1;
       int f2 = 2;
       int sum = 0;

       for (int i = 3; i <= n; i++) {
           sum = f1 + f2;
           f1 = f2;
           f2 = sum;
       }
       return sum;
   }

这种方法,其实也被称之为递推

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值