数据结构 --- [关于递归的理解,使用递归的方式删除链表的元素]

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 --- [关于递归的理解,使用递归的方式删除链表的元素]相关的知识,希望对你有一定的参考价值。

递归

使用的方法在自己的方法体内调用自己,
从本质上,就是把原来的问题转化为更小的同一问题。

比如说,要执行方法A,但是方法A中又调用了方法B,方法B中调用了方法C,

在递归的思想中,是方法直接或者间接调用自身.

例如说计算1+2+3+4+5…+100的和;

sum(1)  =  1
sum(2)  =  2 + sum(1) = 3
sum(3)  =  3 + sum(2) = 6
sum(4)  =  4 + sum(3) = 10
........
sum(100)= 100 +sum(99)=?

即
sum (n)  =  n + sum(n-1)

对于递归,要先确定一个递归到底的情况;(一般用if语句先去判断).

比如说,这个整数相加时,递归结束点就是 加到 1 时;
确定了结束点手,再去写递归的方法(表达式),

//整数相加;
    public static int  sum(int n) {
        //递归结束点;;
        if(n == 1){
          return  1;
        }
        //递归函数  sum(n-1) + n
        return  sum(n-1) + n;
    }

猴子吃桃问题递归求解

一猴子在吃桃子, 每天吃掉一半 桃子后,又偷偷地 多吃 一个;
结果第6天的时候,就剩下1个桃子了;

那么;
第 4 天桃子总数 ==>  1 个桃子;
第 3 天桃子总数  ==>  (第四天的桃子 +  1)  X  2  ==> 4
第 2 天桃子总数  ==>  (第三天的桃子 +  1)  X  2  ==> 10
第 1 天桃子总数  ==>  (第二天的桃子 +  1)  X  2  ==> 22
......
第 n天 ==> (第 (n-1) 天 + 1 ) ) X2
//吃桃问题;求出第一天的桃子数量;
public static  int peach(int day){
    //递归到底结束点为第四天;
    if(day == 4){
        return 1 ;
    }
    //递归表达式;
    return (peach(day+1)+1)*2;
}

这样的方式,如果说要改变吃桃子的天数,还需在递归函数内部去更改递归结束点的判断条件.

若采用反向的方式,将递归结束条件变为倒数第一天时,就剩下一个桃子;

public static int peach2(int day){
     //终结点;
     if(day==1){
       return 1;
     }
     return (peach2(day-1)+1)*2;
 }


斐波那契数递归求解

斐波那契数列

该数列从第3项开始,每一项都等于前两项之和。

public  static int test3(int i){
     //终结点;
     if(i==0){
         return 0;
     }
     if(i==1||i==2){
         return 1;
     }
     return test3(i-1)+test3(i-2);
 }


而对于链表的删除指定内容,也能使用递归的方式实现;

之前创建的自定义链表==>MyLink
现在给它添加一个使用递归,删除指定的数据元素的方法removeValDg

public class MyLink<T> {
    /**
     * 创建节点内部类
     */
    class Node {
        //val 数据域;
        private T val;
        //next 指向下个结点的引用;默认指向空;
        private Node next;

        //初始化结点;
        public Node(T val) {
            this.val = val;
            this.next = null;
        }

        //结点数据,输出方法;
        @Override
        public String toString() {
            return this.val.toString();
        }
    }

    //定义链表头;
    private Node head;
    //链表的实际结点个数;
    private int size;

    //初始化链表;
    public MyLink() {
        this.head = null;
        this.size = 0;
    }

    /**
     * 判断链表是否为空;
     */
    public boolean isEmpty() {
        return this.size == 0;
    }
    /**
     * 获取链表的结点个数;
     */
    public int getSize() {
        return this.size;
    }
    /**
     * 输出链表;
     */
    @Override
    public String toString() {

        StringBuilder sbl = new StringBuilder();
        Node cur = head;
        //排除头结点不为空的情况下,遍历得到所有结点;
        while (cur != null) {
            sbl.append(cur + "==>");
            cur = cur.next;
        }
        sbl.append("null");
        return sbl.toString();
    }
    //==========================================================>
    //提供给外部访问的递归删除方法;
    //(注意,仅可删除首次出现的指定元素)
    public Node removeValDg(T val) {
        //先判断链表为空的情况;
        if (this.isEmpty()) {
            return null;
        }
        //调用内部定义的私有方法处理;
       return head = removeValDg(head,val);
    }

    //递归删除的底层;
    private Node removeValDg(Node head, T val) {
        //提供终结条件;
        if(head==null){
            return null;
        }
        //表达式;
        //先把每次要用的头结点脱离链表,存到要操作的结点中;
        Node operNode=head;
        //将当前头结点的下一位存为新的头结点;
        Node newHead=head.next;
        //每次让当前操作结点进行比对,若符合,就删除,返回后面的剩余结点;
        if(operNode.val==val){
            return newHead;
        }else{
            //若当前头结点不符合,则将预备的新头节点,再次传入调用;最终删除后留下的链表 要接到操作结点的后面;一步步回调;
            operNode.next=removeValDg(newHead,val);
            return operNode;
        }
    }

注意终止的条件就是head==null;那么也就是传入的参数为null时触发;

由于 operNode (当前操作结点)11 不符合删除的值,则把newHead ()剩余链表的头结点 12)传参,调用删除方法;
需要注意的是, 以 12 为头结点的链表调用删除方法后 得到的返回链表是连接在之前的操作结点 11 的下一位;也就是说,
即使刚才脱离链表 11 不符合, 那么也得让他连接回去; 所以返回的是 return operNode;

那么,看看 以 12 为操作结点的链表情况, 此时 12作为operNode (当前操作结点) 需要脱离这段剩余的链表;需要把operNode (当前操作结点) 12单独拿出来和 4 比较;那么(剩余链表的头结点) newHead就是13了;由于operNode (当前操作结点) 12 不符合删除的值; 那么,就把newHead ()剩余链表的头结点 13)传参,调用删除方法;同样地,以 13 为头结点的链表调用删除方法后 得到的返回链表是连接在之前的操作结点12后面;而上一次又是连接在11 后面;
以此类推, 以 13 为操作结点的链表情况,… 以 4 为操作结点的链表情况,…;
实际上,这样保证了最终删除元素后,剩余的元素还是保持着连接;

经过测试,这种方式仅仅只能删除首次出现的指定元素;

debug调试发现,该方法在匹配到12作为删除元素后就结束了.

来看看这个删除链表所有指定元素的方式;
直接上方法;

//(提供给外部访问)删除链表的所有指定元素;
    public Node removeAllValDg(T val) {
        //先判断链表为空的情况;
        if (this.isEmpty()) {
            return null;
        }
        //调用内部定义的私有方法处理;
        return head = removeAllValDg(head,val);
    }

    //递归删除所有指定元素方法的底层;
    private Node removeAllValDg(Node head, T val) {
        //终结方式;
        if(head==null){
            return null;
        }
        Node result = removeAllValDg(head.next,val);
        //每次让当前操作结点进行比对,若符合,就删除;
        if(head.val == val){
            return result;
        }else{
            //否则就让当前操作的结点的下一位调用删除方法;处理的结果链接到当前操作结点后;
            head.next=result;
            return head;
        }
    }

在使用debug调试时,注意到由于进入方法后就会执行;

Node result = removeAllValDg(head.next,val);

于是,它就一次一次第进入方法,直到末尾的null处;然后在从14倒着开始和12进行比较;

然后又返回到13;此时13作为链表的头结点;后面连接的是14,14连接的是null;

依次执行;直到最终到达11;


以上是关于数据结构 --- [关于递归的理解,使用递归的方式删除链表的元素]的主要内容,如果未能解决你的问题,请参考以下文章

关于回调和递归思想的理解

关于python递归函数怎样理解

关于递归和动态规划的简单理解

递归思维的算法是啥? (关于具体例子)

有没有关于递归算法方面的书?

关于递归,Java 中的 for 循环。在这种情况下我的理解正确吗?