单链表反转的四种经典算法
迭代反转链表
递归反转链表
头插法反转链表
就地逆置法反转链表
主要示例的是:不带头节点的链表反转
链表反转图示图解:
起始链表:
反转后链表:
迭代反转链表
具体实现:
-
设置三个指针,分别指向首元节点、首元节点直接前驱、首元节点后继--->
beg、mid、end
-
循环开始
-
修改
mid
的指针域令其指向beg
-
将三个指针整体向后移动一个节点
-
循环重复。当
end
为空,更改mid
指针域,跳出循环 -
更改头指针位置
一个流程实现图示:
设置三个指针:
更改mid
指针指向,整体向后移动一个节点:
最后一个节点:
迭代反转算法C语言的实现:
/*
1、head为无头节点链表的头指针
*/
Link * iterator_reverse(Link * head){
//判断头指针是否有指向首元节点
if (head==NULL||head->next==NULL)
{
//返回头指针
return head;
}
else {
//设置三个指针--->beg、mid、end。分别指向首元节点前面的空节点、首元节点、首元节点的下一位
Link * beg = NULL;
Link * mid = head;
Link * end = head->next;
//循环执行
while (true)
{
//修改mid指针域指向前一个指针
mid->next = beg;
//判断end是否为空--->mid是否是在最后一个节点了
if (end == NULL)
{
//结束循环
break;
}
//三个数整体向后移动一个节点
beg = mid;
mid = end;
end = end->next;
}
//修改head头指针的指向,指向mid
head = mid;
//返回头指针--->用于找到链表
return head;
}
}
递归反转链表
本质:
-
如果把
NULL
看成一个变量相当于把NULL
这个变量从最后移动到首元节点之前
具体实现:
-
通过判断头节点和头节点的指针域是否为空决定是否返回头节点
-
通过递归的方式找到最后一个节点,此时会返回头节点并且创建新的数据域节点
-
第一次开始进行
NULL
反转的时候起始位置是在倒数第二个节点,创建的临时节点是指针域 -
让指针域的下一位指向自身,自身的指针域指向
NULL
--->这样结束的时候就会递归执行
递归反转实现流程:
指针域∧
表示为 NULL
递归找到最后一个节点:
从倒数第二个节点开始创建新的预留节点:
一直递归重复到首元节点:
递归反转算法C语言实现:
Link * recusive_reverse(Link * head){
//判断递归返回的节点是否是最后一个节点
if (head == NULL || head->next == NULL)
{
//返回头节点
return head;
}
else
{
//一直创建新节点
Link * new_head = (Link*)malloc(sizeof(Link));
//当逐层退出时,new_head 的指向都不变,一直指向原链表中最后一个节点;
//递归每退出一层,函数中 head 指针的指向都会发生改变,都指向上一个节点。
//每退出一层改变head指针域的下一位的指向和head指针的指针域置空
head->next->next = head;
head->next = NULL;
//返回新节点
return new_head;
}
}
/*
head 由节点 1 进入递归,此时 head 的指向又返回到节点 1,整个递归过程结束。
新反转链表的头指针为 new_head。
*/
头插法反转链表
具体实现:
-
声明一个新的节点,形参传入原链表
-
当原链表为空时候结束方法
-
开始循环
-
摘取原链表的首元节点,接到新链表上
-
重复循环
头插法实现流程:
新建一个链表:
取原链表第一个节点放入新链表中:
直到最后:
头插法反转链表算法C语言实现:
/*
1、新建一个链表
2、取出原链表的首元节点放入新链表
3、循环执行步骤
*/
Link * head_reverse(Link * head){
/*新建链表头指针*/
Link * new_head = NULL;
/*定义存储原链表的临时节点*/
Link * temp = NULL;
//判断结束方法条件
if (head == NULL || head->next == NULL)
{
//结束方法,返回head链表
return head;
}
//如果head不为空,进入循环(为什么不是head->next)
while (head!=NULL)
{
//临时节点存储首元节点
temp = head;
//将head变成head的数据域指针--->需要将head的指针域复制给head所以不能是head->next为空
head = head->next;
//temp插入到新建链表的头部--->因为是插入到头部所以temp->next=new_head
temp->next = new_head;
new_head = temp; //起始这个是new_head->elem = temp
}
//最后返回new_head
return new_head;
}
就地逆置法反转链表
具体实现:
-
设置两个指针,分别指向首元节点和第二个节点
-
将第二个节点从链表上删除并且添加到链表头部
-
先删除节点--->
->next->next
-
再更改摘下来的节点的指针域指向beg
-
-
交换
end
和head
的位置 -
调整
end
位置,让他指向当前的beg节点的下一个节点
就地逆置法反转链表C语言实现:
/*
1、创建两个指针
2、删除第二个节点
3、将第二个节点的指针域指向第一个节点
4、将第一个节点的指针域名指向第三个节点
5、更改数据域
6、循环操作
*/
Link * local_reverse(Link * head)
{
//创建两个空指针
Link * beg;
Link * end;
//判断返回情况
if (head == NULL || head->next == NULL)
{
//返回链表自身
return head;
}
//令空指针指向首元节点和第二个节点
beg = head;
end = head->next;
//当end不为空--->指针域不为空(没到最后一位的时候)
while (end!=NULL)
{
//摘除end
beg->next = end->next; //相当于是head->next->next
//更改end的指针域指向链表头
end->next = head; //不能写beg因为如果写了beg那么beg将会是常量
//交换head和end的位置
head = end;
//调整end位置,让他指向当前的beg节点的下一个节点
end = beg->next;
}
//返回head节点
return head;
}