1.单链表的基本结构示意图
2.在写逆置之前需要写的其他基本函数
(1)首先在建立一个"list.h"的头文件,用来放声明
#pragma once
//单链表的结构定义
typedef struct Node
{
int data;//数据域
struct Node* next;
}Node, * List;
//初始化
void InitList(List plist);
//尾插
bool Insert_tail(List plist, int val);
//输出
void Show(List plist);
//链表逆置(向后转)
void Reverse1(List plist);
//链表逆置(头插法)
void Reverse(List plist);
(2)接着建立一个"list.cpp"的源文件来实现上面的函数
//初始化
void InitList(List plist)
{
assert(plist != NULL);
if (plist == NULL)
return;
plist->next = NULL;
//头节点的数据域不用
}
//尾插
bool Insert_tail(List plist, int val)
{
assert(plist != NULL);
if (plist == NULL)
return false;
Node* p = (Node*)malloc(sizeof(Node));//p是申请的新节点
assert(p != NULL);
p->data = val;
Node* q;//用来查找尾巴
for (q = plist; q->next != NULL; q = q->next)
{
;
}
p->next = q->next;
q->next = p;
return true;
}
//输出
void Show(List plist)
{
assert(plist != NULL);
if (plist == NULL)
return;
for (Node* p = plist->next; p != NULL; p = p->next)
{
printf("%d ", p->data);
}
printf("\n");
}
//链表逆置(方法一:向后转)
void Reverse1(List plist)
{
assert(plist != NULL);
if (plist == NULL || plist->next == NULL || plist->next->next == NULL)
return;
Node* p = plist->next;
Node* q = p->next;
Node* r;
p->next = NULL;
while (q != NULL)
{
r = q->next;
q->next = p;
p = q;
q = r;
}
plist->next = p;
}
//方法二:头插法
void Reverse(List plist)
{
assert(plist != NULL);
if (plist == NULL || plist->next == NULL || plist->next->next == NULL)
return;
Node* p = plist->next;
Node* q = p->next;
plist->next = NULL;
while (p != NULL)
{
q = p->next;
p->next = plist->next;
plist->next = p;
p = q;
}
}
(3)最后建立一个"test.cpp"的源文件用来测试两个逆置函数
#include<stdio.h>
#include"list.h"
//单链表逆置测试
int main()
{
Node head;//定义一个链表
InitList(&head);//初始化
for (int i = 0; i < 20; i++)
{
Insert_tail(&head, i);//尾插
}
printf("原本的链表为:");
Show(&head);
Reverse(&head);
printf("逆置后的链表为:");
Show(&head);
return 0;
}
两次运行结果如图所示
3.对“向后转”和“头插法”的详解
(1)向后转(和头插法比较处理比较麻烦)
首先“if (plist == NULL || plist->next == NULL || plist->next->next == NULL)”这个语句是为了 防止给的是空表或者只有一个节点,直接return出去,不会崩溃掉
在这个方法中需要定义三个指针,分别为p,q,r。其中p是前面的点,q是后面的点,r是未处理的点。
“q->next=p”这句话就会让链表的指向方向发生改变;在写这句话之前需要先标记r,因为如果不标记后面的内容就找不到了,如下图所示
接下来r不能直接往后走,三个点应该依次有顺序往后走。顺序为"p=q;q=r;r=q->next;",写完这三句之后再写"q->next=p;"又会出现下一个反转箭头
接着一直往后走,当r走到空的时候仍然没有处理完,需要再写一遍"q->next=p;",写完之后变成
再接着往后走,q就会也等于空,所以循环终止条件是“q==NULL;”,这个时候表示处理完了。但是结束之后头节点的指针还是指向后面,所以需要特殊处理,即“plist->next=p”即可,因为此时p就是最后一个节点
在这张图中你会看到第一个节点的next是100,不符合逆置后的链表,所以在循环进入之前就要对该节点的next进行处理,因为第一个节点逆置之后就是最后一个节点,所以要把第一个节点置空,在p,q,r定义完成之后才能确定位置,否则后面的数据都会消失,语句为“p->next=NULL”。
完成之后就把链表逆置完毕了
(2)头插法
首先“if (plist == NULL || plist->next == NULL || plist->next->next == NULL)”这个语句是为了 防止给的是空表或者只有一个节点,直接return出去,不会崩溃掉
需要定义两个指针,分别为p,q
定义好之后让链表断开,变成两部分,“plist->next=NULL”,注意不能在没有定义p,q之前就置空,那样后面的数据就会丢失
接着利用头插把p这个节点整体插到plist上,在处理q之前先“p = q;q = p->next;”,按照这样的顺序挪整体
发现这个循环结束条件不能是"q==NULL",所以继续处理,p应该作为循环的结束条件,在下一次挪动的时候p为空就结束了
最后就完成了对单链表的逆置