zoukankan      html  css  js  c++  java
  • 全面分析再动手的习惯:链表的反转问题(递归和非递归方式)

    定义一个方法(函数),实现输入一个链表的头结点,然后可以反转这个链表的方向,并输出反转之后的链表的头结点。

    typedef struct Node{
        int data;
        Node *next;
    } Node, *List;

    链表类的问题,涉及到了很多指针的操作,需要严谨的分析,全面的分析问题之后,在开始写代码,磨刀不误砍柴工!反转链表,直接的想法,就是把链表中指针的方向反转就可以了,如图所示:

    假设 i 结点之前,我们把所有的结点的指针都已经反转了,那么自然 i 和以后的结点链接发生了断裂!如下图;

    这样的话,无法继续遍历 i 以后的结点了,那么自然想到,在断链之前,提前保存之前的状态。那么自然想到定义三个指针,分别指向当前结点 i,i 的后继 j,i 的前驱 h 结点。保存断链之前的三个结点的连接状态。然后,假设没问题了,那么继续反转完毕,最后链表的尾结点就是反正链表的头结点了,也就是 next 为 null 的结点,是原始链表的尾结点。

    #include <iostream>
    using namespace std;
    
    typedef struct Node{
        int data;
        Node *next;
    } Node, *List;
    
    Node * reverseList(List head){
        //定义三个指针,保存原来的连接的状态
        //当前结点指针
        Node *pnow = head;
        //当前结点的前驱指针,初始化是 NULL
        Node *pre = NULL;
        //当前结点的后继指针,初始化也是 null
        Node *pnext = NULL;
        //定义尾指针
        Node *tail = NULL;
        //开始遍历链表
        while(pnow != NULL){
            //如果当前结点不是 null,那么初始化 pnext 指针指向当前结点的下一个结点
            pnext = pnow->next;
            //如果找到了尾结点,初始化 tail 指针
            if(NULL == pnext){
                tail = pnow;
            }
            //进行链表的反转,当前结点的 next 指针指向前一个结点,实现链表方向的反转,此时发生了断链
            pnow->next = pre;
            //勿忘断链的情形,需要使用 pre 指针保存状态,pre 等价于是后移一个结点
            pre = pnow;
            //pnow 后移一个结点
            pnow = pnext;
        }
        
        return tail;
    }

    定义的这个三个指针,目的就是防止断链之后无法继续遍历链表以后的结点,实现全部的反转。当 pnow 的 next 指向 pnow 的前驱pre(初始化是 null)的时候,已经实现了 pnow 和前驱pre的方向反转,但是 pnow 此时就和后继pnext断链了,那么使用 pre 后移的方式,指向 pnow,同时 pnow 也后移,指向 pnext,而 pnext 继续指向更新之后的 pnow 的 next 结点即可。从而实现了状态的保存,继续遍历全部结点,实现链表反转。

    注意关于链表问题的常见注意点的思考:

    1、如果输入的头结点是 NULL,或者整个链表只有一个结点的时候

    2、链表断裂的考虑

    下面看看递归的实现方式

    递归的方法其实是非常巧的,它利用递归走到链表的末端,然后再更新每一个node的next 值 ,实现链表的反转。而newhead 的值没有发生改变,为该链表的最后一个结点,所以,反转后,我们可以得到新链表的head。

    //递归方式
    Node * reverseList(List head)
    {
        //如果链表为空或者链表中只有一个元素
        if(head == NULL || head->next == NULL)
        {
            return head;
        }
        else
        {
            //先反转后面的链表,走到链表的末端结点
            Node *newhead = reverseList(head->next);
            //再将当前节点设置为后面节点的后续节点
            head->next->next = head;
            head->next = NULL;
            
            return newhead;
        }
    }

    程序刚开始执行,if 语句失效,进入 else 语句,然后执行Node *newhead = reverseList(head->next);第二个结点的指针参数传入递归函数,一直到,最后一个结点的指针参数传入递归函数,if 语句有效head->next == NULL,返回当前的head 给 newhead 指针指向,如图:

    其实在递归函数栈内,按照后进先出的顺序,执行一级级的递归函数,返回末位结点给 newhead 之后,执行递归栈里的第二个递归函数,发生如图

    返回 newhead,也就是新的反转之后的链表(临时的),然后进入到递归工作栈里的第一个递归函数,如图:

    返回 newhead,也就是反转之后的链表,此时递归工作栈的函数全部执行,返回的结点就是反转之后的链表的头结点(之前的尾结点)

    欢迎关注

    dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

  • 相关阅读:
    C#中异步和多线程的区别
    猫 老鼠 人的编程题
    C#中数组、ArrayList和List三者的区别
    经典.net面试题目
    sql有几种删除表数据的方式
    内存池的实现
    A*算法为什么是最优的
    传教士与野人问题
    d3d导致cairo不正常
    c++中的signal机制
  • 原文地址:https://www.cnblogs.com/kubixuesheng/p/4394509.html
Copyright © 2011-2022 走看看