链表的各种操作逻辑性较强,初学者如果不能很好的理解链表的概念和结构,往往会“蒙圈”!而对于初学者,链表逆置更是比较难以理解!
--------------------------------------------------------------------
首先说明一下链表逆置的概念:链表逆置不同于数组逆置,其首节点需要保持不变!这是因为链表的输出是以头节点指向首节点,然后根据各节点的next指针域一个一个遍历输出!假设要把所有的元素逆置,那么链表的首节点就会指向NULL,则链表结构将变成这种:
那么这个时候输出的就只有数字10!所以正确的逆置后的结构应该是:
这个时候遍历输出的才是:10,30,20!
--------------------------------------------------------------------
明白了逆置的概念后,我们再来说说具体实现的算法:
1.判断首节点的next是否为NULL,如果为空则不需要逆置!
判断首节点next的next是否为空,如果为空证明链表除首节点之外只有一个节点,所以也不需要逆置;
if (ls->next == NULL)
return;//只有一个首节点,不需要逆置
if (ls->next->next == NULL)
return;//也不需要逆置
2.定义一个指针last,指向首节点的next域(即上图“20”所在的指针),因为逆置之后,该域为链表尾节点;
定义三个指针,分别代表前一个节点,当前节点,下一个节点;
前节点指向链表首节点;
当前指针指向链表首节点的next域;
下一个节点为NULL;
struct list *last = ls->next;//逆置后ls->next就成了最后一个节点了
struct list *pre = ls;//上一个节点的指针
struct list *cur = ls->next;//当前节点的指针
struct list *next = NULL;//下一个节点的指针
3.循环条件判断当前节点是否为NULL,如果为NULL退出循环;
- 下一个节点指向当前节点的下一个节点;
- 当前节点的下一个节点指向前一个节点;
- 前一个节点指向当前节点;
- 当前节点指向下一个节点;
while(cur)
{
next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
4.循环完成;
设置last节点的next为NULL;
设置链表首节点的next为前一个节点pre。
--------------------------------------------------------------------相信初学者看完上述算法早已经绕晕了,下面我已图示法一步步来解析。在解析前需要牢记一个概念是:节点的互相关联是靠指针的不停移动和“指向”串起来的。也就是说,链表在逆置时,节点并没有动,动的是指针!而节点所做的改变就是其next的指针域指向发生了改变而已!图解:
起初:
进入循环:
第一次
:
第二次:
此时判断cur=NULL,所以跳出循环!
--------------------------------------------------------------------
OK,就是这样,不过还是希望大家能够自己推演一遍,这样才能真正理解,况且我也是初学者,有什么理解不对的地方还请指正!
另外,附上完整代码:
(注:此为一个函数,大家创建好链表后,只需要调用即可!参考例程为“传智播客” 的C语言基础教程!)
voidreverse(struct list *ls)//链表逆置
{
if (ls->next == NULL)
return;//只有一个首节点,不需要逆置
if (ls->next->next == NULL)
return;//也不需要逆置
struct list *last =ls->next;//逆置后ls->next就成了最后一个节点了
struct list *pre = ls;//上一个节点的指针
struct list *cur = ls->next;//当前节点的指针
struct list *next = NULL;//下一个节点的指针
while(cur)
{
next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
ls->next = pre;
last->next = NULL;
}