目录
1.单链表的概念
(1)结点
在链表结构中,构成链表的基本单位就是结点,每个结点结构体一般包括数据域和指针域。在数据域中,包含用户想要存储的每个单位的信息;指针域则是存储下一结点的指针,由此才能将若干结点连接成为链表。结点的结构一般如下所示:
图1. 结点的结构
(2)单链表
单链表,顾名思义,就是将若各干存储信息的结点,通过每个结点指针域的指针依次连接起来。与顺序表不同的是,单链表在内存中并不是连续分布的,而是随机分布,这也就决定了单链表的一个重要特性——在使用前无需一次性分配所有结点所需的空间,而是根据需要随时分配。
打个比方,单链表就像是散落的n个房间,每个房间的右边有走向下一个房间的方向指示,因此,如果想访问任何一个房间,都要从第一个房间开始,依次走过之前的每一个房间,才能到达指定的房间,而不能直接访问指定的房间,即链表不支持随机访问。
图2. 单链表的结构
2.单链表的特点
每一个单链表都有一个头指针,它一般用于指向单链表的第一个结点。如果一个单链表有头结点,则头指针指向头结点;否则直接指向第一个存储信息的结点(尾指针可根据情况自行设置)。
(1)带头结点的单链表
单链表的第一种类型是带头结点的单链表。头结点是一个特殊的结点,它不像其他结点一样存储数据,而是链表的一个标志,存储一些特殊的信息,如表长等。同时,带头结点的单链表的头指针始终不为空,当头结点的指针域为空时,链表为空。
图3. 带头结点的单链表
(2)不带头结点的单链表
第二种类型就是不带头结点的单链表。在这种类型的单链表中,头指针直接指向存储用户信息的第一个链表,同时当头指针为空时,链表为空。
图4. 不带头结点的单链表
3.参考代码
(1)结点和头指针的定义
首先,考虑到我们在进行结点的增删过程中,头指针的值会随时变化,我们选择定义一个全局的结构体指针变量,用来保存我们的头指针;同时,在结点结构体中,我们分别定义一个int类型的变量和一个结构体指针变量,分别作为我们的数据域和指针域(数据域可根据需要自行修改)。
PNode __Head = NULL; //头指针
typedef struct NODE
{
int BufferData; //数据域
struct NODE* Flink; //指针域
}Node,*PNode;
(2)单链表的遍历
单链表的遍历,就是从首个结点开始,逐个输出单链表中每个结点数据域的值。
void TravelList()
{
//定义临时指针,用于遍历链表
PNode v1 = __Head;
//因为最后一个结点的指针域为空,所以我们以此作为循环条件来进行链表的遍历
while (v1 != NULL)
{
printf("%d ", v1->BufferData);
//每次遍历后,临时指针v1后移,指向下一结点,直至最后一个结点停止循环
v1 = v1->Flink;
}
printf("\r\n");
}
(3)求单链表的表长
int GetListLength()
{
//临时变量用于保存表长
int ListLength = 0;
//临时指针用于遍历链表
PNode v1 = __Head;
while (v1 != NULL)
{
//每循环依次,表长+1,直至循环结束
v1 = v1->Flink;
ListLength++;
}
return ListLength;
}
(3)向单链表中插入结点
① 头插法
图5. 单链表的头插法
void FrontInsert(int Data)
{
//由于插入新结点,需要动态申请一块内存
PNode v1 = new Node;
//对新申请的结构体进行初始化
v1->Flink = NULL;
v1->BufferData = Data;
//如果链表为空,直接让头指针指向新结点
if (__Head == NULL)
{
__Head = v1;
}
else
{
//插入到首个结点之前
v1->Flink = __Head;
__Head = v1;
}
}
② 尾插法
图6. 单链表的尾插法
void BackInsert(int Data)
{
//动态申请一块内存并初始化结构体成员
PNode v1 = new Node;
v1->BufferData = Data;
v1->Flink = NULL;
//如果链表为空,直接让头指针指向该结点
if (__Head == NULL)
{
__Head = v1;
}
else
{
//定义临时指针,用于遍历链表找到最后最后一个结点来实现尾插法
PNode v2 = __Head;
while (v2->Flink != NULL)
{
v2 = v2->Flink;
}
//将新结点插到链表中
v2->Flink = v1;
}
}
③ 表中插入
图7. 单链表的表中插入
void MediumInsert(int Position, int Data)
{
//首先获得单链表的表长
int ListLength = GetListLength();
//判断插入位置是否合法,非法则返回错误
if (Position < 1 || Position > ListLength)
{
_tprintf(_T("Position Error!"));
}
else
{
PNode v1 = __Head;
PNode v2 = new Node;
v2->BufferData = Data;
v2->Flink = NULL;
//找到插入位置的前一个结点
for (int i = 1; i < Position - 1; i++)
{
v1 = v1->Flink;
}
//这里如果先连接插入结点和其前驱结点,则前驱结点保存的插入结点的后继节点的地址将丢失
//所以应先连接插入结点和其后继结点,再连接前驱结点
v2->Flink = v1->Flink;
v1->Flink = v2;
}
}
(4)从单链表中查找数据
int FindData(int Data)
{
PNode v1 = __Head;
int Position = 1;
while (v1 != NULL)
{
//遍历单链表,如果结点数据域的值与所查值相等,则返回数据位置
if (v1->BufferData == Data)
{
printf("Find Data %d Success!The Position is %d\r\n", Data, Position);
return Position;
}
else
{
//直到查找到链表最后一个结点
v1 = v1->Flink;
Position++;
}
}
//当v1为空时,表示v1已经指向尾结点的后继结点,即已经查找完所有结点,此时返回查找失败
if (v1 == NULL)
{
printf("Find Data %d Fail!\r\n",Data);
return 0;
}
}
(5)从单链表中删除结点
void DeleteData(int Data)
{
//首先查找待删数据在单链表中的位置
int Position = FindData(Data);
if (Position == 0)
{
//该数据不存在
printf("Delete Data %d Fail!\r\n", Data);
return;
}
else
{
//查找待删结点的前驱节点
PNode v1 = __Head;
for (int i = 1; i < Position - 1; i++)
{
v1 = v1->Flink;
}
//v2为待删结点 直接让v2的前驱结点指向v2的后继结点
PNode v2 = v1->Flink;
v1->Flink = v2->Flink;
//释放内存
delete(v2);
printf("Delete Data %d Success!\r\n", Data);
}
}
(6)main()函数
① 测试代码
void _tmain()
{
BackInsert(5);
BackInsert(128);
BackInsert(66);
FrontInsert(13);
FrontInsert(25);
FrontInsert(132);
TravelList();
MediumInsert(3, 92);
MediumInsert(5, 88);
TravelList();
FindData(88);
FindData(128);
FindData(100);
DeleteData(13);
DeleteData(0);
DeleteData(128);
TravelList();
return;
}
② 测试结果
(7)完整代码
#include<iostream>
using namespace std;
typedef struct NODE
{
int BufferData;
struct NODE* Flink;
}Node,*PNode;
PNode __Head = NULL;
void FrontInsert(int Data);
void BackInsert(int Data);
void MediumInsert(int Position, int Data);
int GetListLength();
void TravelList();
int FindData(int Data);
void DeleteData(int Data);
void _tmain()
{
BackInsert(5);
BackInsert(128);
BackInsert(66);
FrontInsert(13);
FrontInsert(25);
FrontInsert(132);
TravelList();
MediumInsert(3, 92);
MediumInsert(5, 88);
TravelList();
FindData(88);
FindData(128);
FindData(100);
DeleteData(13);
DeleteData(0);
DeleteData(128);
TravelList();
return;
}
void FrontInsert(int Data)
{
PNode v1 = new Node;
v1->Flink = NULL;
v1->BufferData = Data;
if (__Head == NULL)
{
__Head = v1;
}
else
{
v1->Flink = __Head;
__Head = v1;
}
}
void BackInsert(int Data)
{
PNode v1 = new Node;
v1->BufferData = Data;
v1->Flink = NULL;
if (__Head == NULL)
{
__Head = v1;
}
else
{
PNode v2 = __Head;
while (v2->Flink != NULL)
{
v2 = v2->Flink;
}
v2->Flink = v1;
}
}
void MediumInsert(int Position, int Data)
{
int ListLength = GetListLength();
if (Position < 1 || Position > ListLength)
{
_tprintf(_T("Position Error!"));
}
else
{
//找到插入位置的前一个结点
PNode v1 = __Head;
PNode v2 = new Node;
v2->BufferData = Data;
v2->Flink = NULL;
for (int i = 1; i < Position - 1; i++)
{
v1 = v1->Flink;
}
v2->Flink = v1->Flink;
v1->Flink = v2;
}
}
int GetListLength()
{
int ListLength = 0;
PNode v1 = __Head;
while (v1 != NULL)
{
v1 = v1->Flink;
ListLength++;
}
return ListLength;
}
void TravelList()
{
PNode v1 = __Head;
while (v1 != NULL)
{
printf("%d ", v1->BufferData);
v1 = v1->Flink;
}
printf("\r\n");
}
int FindData(int Data)
{
PNode v1 = __Head;
int Position = 1;
while (v1 != NULL)
{
if (v1->BufferData == Data)
{
printf("Find Data %d Success!The Position is %d\r\n", Data, Position);
return Position;
}
else
{
v1 = v1->Flink;
Position++;
}
}
if (v1 == NULL)
{
printf("Find Data %d Fail!\r\n",Data);
return 0;
}
}
void DeleteData(int Data)
{
int Position = FindData(Data);
if (Position == 0)
{
printf("Delete Data %d Fail!\r\n", Data);
return;
}
else
{
PNode v1 = __Head;
for (int i = 1; i < Position - 1; i++)
{
v1 = v1->Flink;
}
PNode v2 = v1->Flink;
v1->Flink = v2->Flink;
delete(v2);
printf("Delete Data %d Success!\r\n", Data);
}
}