全面分析再动手的习惯:链表的反转问题(递归和非递归方式)

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

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,也就是反转之后的链表,此时递归工作栈的函数全部执行,返回的结点就是反转之后的链表的头结点(之前的尾结点)

 

你可能感兴趣的:(非递归)