单链表逆转的方法有很多,我也从很多博主那学习看到了各种各样的方法。今天就给大家总结一下个人认为较简便的四种方法。这四种方法不用开辟新的空间,所以不会造成空间浪费。
采用迭代的基本思路如下:我假设链表为1->2->3->4,那么但我完成链表的逆转后就应该是4->3->2->1。我将链表的头结点设为L,同时开辟两个新的节点q和p,我将利用L,q,p三个节点来进行迭代。
List Reverse(List L) {
List p, q;
p = L;
L = NULL;
while (p)
{
q = p;
p = p->Next;
q->Next = L;
L = q;
}
return L;
}
代码在上面,具体的思路讲解如下:
L为头节点,把L赋值给p,而后让L为null。如下
进入while循环体进行第一次的循环,如下
当p不为空,进行第二次循环。如下
当p不为空,进行第三次循环,第四次.........到p为空为止。当while循环体执行到p为空的时候,这时最后一个节点为L,返回L,逆转结束。
(本质是有三个节点分别为L,q,p。进行while循环后,每次改变一个节点的方向,让原来为L->q的方向,变成q->L。然后通过迭代让q变成新的L,p变成新的q,p=p->next变成新的p,在下一个循环继续改变方向。)
第二种方法也是迭代,但和第一种方法相比会有所不同。核心思想一样,通过不断移动三个点来实现方向的转变。
头节点为L,新开辟三个新的节点空间为p,q,r。
List Reverse(List L) {
List p, q, r;
if (L == NULL || L->Next == NULL) //当头节点为空或者只有一个节点时直接返回头节点。
return L;
p = L;
q = L->Next;
L->Next = NULL;
while (q) {
r = q->Next;
q->Next = p;
p = q;
q = r;
}
L = p;
return L;
}
代码在上面,具体的思路讲解如下:
头节点赋给p,头节点下一个为q,然后让头节点指向null。如下
当q节点不为空,进行第二次循环,如下
继续进行循环,直到当q节点为空时,这最后一个节点为p,退出循环。将p设为新的头节点L,返回L。
从第2个节点到第N个节点,依次逐节点插入到第1个节点(L节点)之后,最后将第一个节点挪到新表的表尾。
List Reverse(List L) {
if (L == NULL || L->Next == NULL)return L;//当为空链表或者只有一个节点时返回L
List p, q;
p = L->Next;
while (p->Next) {
q = p->Next;
p->Next = q->Next;
q->Next = L->Next;
L->Next = q;
}
p->Next = L;
L = p->Next->Next;
p->Next->Next = NULL;
return L;
}
代码在上面,具体的思路讲解如下:
假设链表为1->2->3->4->5->6,我们的步骤如下:
具体的步骤如下:
当开始执行时(写得有些潦草,诸位多多包涵,耐心看一定有所收获)
当继续循环时
当循环结束时,我们会得到1->6->5->4->3->2。这明显不是我们想要的结果,但离最终结果已经很近了,我们如何将原始的头节点移到最后呢?
因为循环条件是p->next不为空,那么我们已经知道了当循环退出时最后一个节点为p。于是我们可以(1)让p->Next = L,形成一个闭环。(2) L = p->Next->Next形成新的头节点。(3)最后用p->Next->Next = NULL来切断闭环,形成一个链表。还是有点懵?不要紧,我把图画好了!诸君请细看。
(1)让p->Next = L,形成一个闭环
(2) L = p->Next->Next形成新的头节点
最后用p->Next->Next = NULL来切断闭环,形成一个链表
最后三步的总图在这
用递归来实现代码上会相对的简单。关于递归这,我看了几篇博主的文章,有几个版本,大家看看哪个才是对的。
(1)版本一:有的博主是这样认为的,比如链表是1->2->3->4->5->6,他认为递归函数Reverse()执行完后,链表就变成了6->5->4->3->2 <-1,然后只需要改动原来的头节点就可以完成逆转。通俗的说就是不断继续递归找到新节点,然后执行递归后面的语句,只执行一次改变头节点就完成了。
(2)版本二:通过不断的递归,每次递归都会执行递归后面的语句,每次改变两个节点之间的方向,从前往后改,基本是这样的1<-2->3->4->5->6,然后1<-2-<3->4->5->6,然后1<-2<-3<-4->5->6以此类推。
(3)这既然是一个有争议的地方,我个人是这样认为的,递归后的语句应该是在完成递归后再不断执行,递归多少次就执行多少次,和版本二一样,但执行的顺序我认为应该是从后往前,因为它是堆栈存储,应该遵循后进先出,所以我认为应该是1->2->3->4->5<-6,然后1->2->3->4<-5<-6,然后1->2<-3<-4<-5<-6,以此类推。
但无论是哪个版本,下面这些递归代码答案都是正确的,而且极其简短。
List Reverse(List L) {
if (L == NULL || L->Next == NULL)return L;//当L为空或者只有L一个节点时
List newL, q;
newL = Reverse(L->Next);
q = L->Next;
q->Next = L;
L->Next = NULL;
return newL;
头节点为L,当逆转链表后,新的头节点为newL;