前言
所谓反转链表,就是将链表整体“反过来”,将头变成尾、尾变成头。那么,如何实现链表的反转呢?
常用的实现方案有 4 种,这里分别将它们称为迭代反转法、递归反转法、就地逆置法和头插法。
值得一提的是,递归反转法更适用于反转不带头节点的链表;其它 3 种方法既能反转不带头节点的链表,也能反转带头节点的链表。
注意:以下均以不带头节点的链表为例讲解。
一、迭代反转链表

步骤:
- 需要设置三个指针:beg, mid, end,分别指向如图所示位置;
- 改变mid的指向,将mid指向beg;
- 三个指针均向前一步;
- 重复2.3步骤,直到end指向NULL;
- 改变mid的指向,将mid指向beg;
- 最后只需改变 head 头指针的指向,另其和 mid 同向,就实现了链表的反转。
c语言实现代码如下:
//迭代反转法,head 为无头节点链表的头指针
link * iteration_reverse(link* head){
if (head == NULL || head->next == NULL) {
return head;
}
else{
link * beg = NULL;
link * mid = head;
link * end = head->next;
//遍历
while(1){
//修改 mid 所指节点的指向
mid->next = beg;
//此时判断 end 是否为 NULL,如果成立则退出循环
if (end == NULL) {
break;
}
beg = mid;
mid = end;
end = end->next;
}
//最后修改head指向
head = mid;
return head;
}
}
二、递归反转链表
和迭代反转法的思想恰好相反,递归反转法的实现思想是从链表的尾节点开始,依次向前遍历,遍历过程依次改变各节点的指向,即另其指向前一个节点。

c语言实现代码如下:
//递归反转
link* recursive_reverse(link* head) {
//递归的出口
if (head == NULL || head->next == NULL) // 空链或只有一个结点,直接返回头指针
{
return head;
}
else{
//一直递归,找到链表中最后一个节点
link *new_head = recursive_reverse(head->next);
head->next->next = head;
head->next = NULL;
return new_head;
}
}
步骤:
- 递归的"深入"阶段 - 就像剥洋葱
想象你有一串珍珠项链(链表),要从头到尾把每颗珍珠都翻转方向。递归的做法是:
先找到最后一颗珍珠(链表尾节点)
然后从后往前一颗一颗翻转
这个过程就像:
你问第一个人:"后面的人反转过来了吗?"
第一个人问第二个人:"后面的人反转过来了吗?"
...
最后一个人说:"我是最后一个,不用反转了"(递归终止条件)
- 递归的"回溯"阶段 - 从后往前处理
当递归到达最后一个节点后,开始往回走(回溯),这时候每退回一层就会执行那两行关键代码:
head->next->next = head; // 让下一个节点指向自己
head->next = NULL; // 断开自己原来的指向
这就像:
最后一个人告诉前一个人:"我已经反转好了,现在该你了"
前一个人调整自己的指针方向
然后告诉更前一个人:"我们俩已经反转好了,现在该你了"
...
一直传递到第一个人
- 为什么这两行代码会执行多次?
因为每一层递归在返回时都会执行它们!比如链表有5个节点:
递归到第5个节点时停止(满足终止条件)
返回到第4个节点时执行这两行代码(处理4和5的关系)
返回到第3个节点时执行这两行代码(处理3和4的关系)
返回到第2个节点时执行这两行代码(处理2和3的关系)
返回到第1个节点时执行这两行代码(处理1和2的关系)
每一层递归都有自己的head变量(指向不同节点),所以每次执行这两行代码时,head的值都不同,处理的是不同位置的节点关系。
- 通俗比喻
想象你在一个有很多房间的走廊里,每个房间都有一个开关和一个指向下一个房间的箭头:
你从第一个房间出发,沿着箭头走到最后一个房间(递归深入)
在最后一个房间,你发现没有下一个房间了(递归终止)
你开始往回走,每到一个房间就做两件事:
把下一个房间的箭头指向当前房间(反转方向)
把当前房间的箭头关掉(设为NULL)
这样走完全部房间后,所有箭头方向都反过来了
这个过程中,你没有使用循环语句,但通过"走到尽头再回头处理"的方式,实现了对每个房间(节点)的操作,这就是递归的"循环"效果。
可视化过程:
初始链表:1 → 2 → 3 → NULL
递归调用:
reverseList(1)
reverseList(2)
reverseList(3) → 返回3
reverseList(2)执行:3→2 2→NULL 返回3
reverseList(1)执行:2→1 1→NULL 返回3
最终链表:3 → 2 → 1 → NULL
三、头插法反转链表
头插法是指依次拿下链表头部的节点,采用头部插入的方法构建一个新链表,即为原链表的反转链表。
新反转链表的头指针为 new_head。
//头插法反转
link * head_reverse(link * head) {
link * new_head = NULL;
link * temp = NULL;
if (head == NULL || head->next == NULL) {
return head;
}
while(head != NULL){
temp = head;
head = head->next; // 将temp从head摘除
// 将temp插入new_head新链表
temp->next = new_head;
new_head = temp;
}
return new_head;
}
流程如下:
【初始状态】
原链表:
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
│头 │───→│ 1 │───→│ 2 │───→│ 3 │───→│ 4 │───→│ 5 │───→NULL
└───┘ └───┘ └───┘ └───┘ └───┘ └───┘
新链表:
┌───┐
│new│───→NULL
└───┘
【第1步】摘下节点1
原链表:
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
│头 │───→│ 2 │───→│ 3 │───→│ 4 │───→│ 5 │───→NULL
└───┘ └───┘ └───┘ └───┘ └───┘
新链表:
┌───┐ ┌───┐
│new│───→│ 1 │───→NULL
└───┘ └───┘
【第2步】摘下节点2
原链表:
┌───┐ ┌───┐ ┌───┐ ┌───┐
│头 │───→│ 3 │───→│ 4 │───→│ 5 │───→NULL
└───┘ └───┘ └───┘ └───┘
新链表:
┌───┐ ┌───┐ ┌───┐
│new│───→│ 2 │───→│ 1 │───→NULL
└───┘ └───┘ └───┘
【第3步】摘下节点3
原链表:
┌───┐ ┌───┐ ┌───┐
│头 │───→│ 4 │───→│ 5 │───→NULL
└───┘ └───┘ └───┘
新链表:
┌───┐ ┌───┐ ┌───┐ ┌───┐
│new│───→│ 3 │───→│ 2 │───→│ 1 │───→NULL
└───┘ └───┘ └───┘ └───┘
【第4步】摘下节点4
原链表:
┌───┐ ┌───┐
│头 │───→│ 5 │───→NULL
└───┘ └───┘
新链表:
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
│new│───→│ 4 │───→│ 3 │───→│ 2 │───→│ 1 │───→NULL
└───┘ └───┘ └───┘ └───┘ └───┘
【第5步】摘下节点5
原链表:
┌───┐
│头 │───→NULL
└───┘
新链表(最终结果):
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
│new│───→│ 5 │───→│ 4 │───→│ 3 │───→│ 2 │───→│ 1 │───→NULL
└───┘ └───┘ └───┘ └───┘ └───┘ └───┘
四、就地逆置法反转链表
与头插法反转链表不同的是,就地置法反转链表不需要建立新的链表,在原来的基础上即可完成反转的操作,需要设置两个指针来进行操作。
步骤如下:
-
初始准备:设定两个指针
beg:指向链表的头节点(或首元节点,根据实现决定)
end:指向beg的下一个节点(即待反转的第一个节点) -
移除end节点
将beg的next指针跳过end,直接连接到end的下一个节点 (此时end节点被移出原链表)
-
将end节点插入链表头部
让end的next指针指向当前的头节点 (此时end成为新的头节点)
-
更新头节点指针
将head指针重新指向end(即新的头节点) (确保后续操作能继续从头部插入)
-
调整end指针
将end重新指向beg的下一个节点 (准备处理下一个待反转的节点)
完整的c语言代码:
//就地反转
link * local_reverse(link * head) {
link * beg = NULL;
link * end = NULL;
if (head == NULL || head->next == NULL) {
return head;
}
beg = head;
end = head->next;
while(end != NULL){
beg->next = end->next; // 将end从链表移除
end->next = head; //将end移动到链表头部
head = end;
end = beg->next; //调整 end 的指向,另其指向 beg 后的一个节点,为反转下一个节点做准备
}
return head;
}
总结
以上就是今天要讲的内容,本文介绍了如何实现无头节点的单链表的反转,涉及到4种方法。

1693

被折叠的 条评论
为什么被折叠?



