NOTICE: 本篇代码是按照源码的书写顺序所写,复制之后可直接运行。
环境: vscode
题目:
试写一算法,对单链表进行逆置
分析:
单链表的逆置需要设置两个指针,第一个进行遍历单链表;第二个进行节点的反向连接。
单链表的逆置其实就是反复利用删除和插入操作。
主意:本题所说的逆置不能开辟新的内存空间,这样一来就不能创建一个新的表然后利用尾插法进行“逆置”了。
代码:
初始化单链表:
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
typedef struct LNode
{
ElemType data;
LNode *next;
}LNode, *LinkList;
Status InitList(LinkList &L)
{ // 初始化单链表 L
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
return OK;
}//InitList
生成单链表:
Status CreateList(LinkList &L, int e)
{ // 创建单链表 L
LinkList p = L;
while(p->next)
p = p->next;
LinkList temp = (LinkList)malloc(sizeof(LNode));
temp->data = e;
temp->next = NULL;
p->next = temp;
return OK;
}//CreateList
打印单链表:
Status DispList(LinkList &L)
{ // 打印单链表 L
LinkList p = L->next;
while(p)
{
printf("%d\t", p->data);
p = p->next;
}
return OK;
}//DispList
逆置:
Status ListOppose(LinkList &L)
{ // 将带表头节点的单链表逆置
LinkList q, p, s;
p = L;
p = p->next;
L->next = NULL;
while(p) // 因为 while 中有 p = p->next 所以终止条件不用写成 p->next
{
q = p;
p = p->next;
q->next = L->next; // q 节点指向现在的 L 的首元结点。这一行加上下一行相当于一个插入操作
L->next = q;
}
return OK;
}//ListOppose
方法二:
Status ListOppose(LinkList &L)
{ // 将带表头节点的单链表逆置
LinkList q, p, s;
p = L->next;
q = p->next;
s = q->next;
p->next = NULL;
while(s->next)
{
q->next = p;
p = q;
q = s;
s = s->next;
}
// 将尾结点连接到原表的首元结点
q->next = p;
s->next = q;
L->next = s;
return OK;
}//ListOppose
主函数:
int main()
{
int i;
LinkList L;
InitList(L);
for(i=1; i<=10; i++)
CreateList(L, i);
printf("\n创建的单链表为:\n");
DispList(L);
printf("\n逆置后的单链表为:\n");
ListOppose(L);
DispList(L);
return OK;
}
创建好的单链表为:
(PS: 画图太难了!!!!!我透!)
其中,ListOppose() 函数中(第一种方法):
- p = L, 即:使 p 指向 L 的头结点
- p = p->next, 即:p 向后移动一个位置。这是为了给另一个用来做反向连接的变量 q 挪地方
- L->next = NULL, 将表 L 掐断,变成如下图所示:
4. 第一次循环:
q = p; p = p->next; 这两行就是把 q 指向 p,然后将 p 向后移动到下一个节点:
q->next = L->next; L->next = q; 这两行就是进行插入,即:将 q 所指的节点插入到现在的表 L 的头结点和首元结点中间:
如果搞不清楚 q 和 p 所指向的节点的话,可以在程序里打印 q->data 或者 p->data,这样就能很清楚的看到每次循环 q 和 p 所指节点。我复盘的时候就蒙圈了,不晓得 q 先指向哪个节点,所以我打印了 q->data:
然后看到运行结果如下图:
可以很清楚的看到,q 先指向首元结点。
第二次循环:
q = p; p = p->next; q 向后移,p 也向后移;
q->next = L->next; L->next = q; 将 data 域为 2 的节点插入到首元结点之前:
第三次循环:
q = p; p = p->next; q->next = L->next; L->next = q; 将 data 域为 3 的加点插入到首元结点的前边:
以此类推,到最后一次循环结束的时候:
没错,p 指向空,此时跳出循环,但是整个单链表的逆置也已经完成了。
运行结果示意图:
THE END!