全面分析再动手的习惯:链表的反转问题(递归和非递归方式)
定义一个方法(函数),实现输入一个链表的头结点,然后可以反转这个链表的方向,并输出反转之后的链表的头结点。
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电子书,资料,帮忙内推,欢迎拍砖!