一、链表简介
1.什么是链表?
链表是一种线性数据结构,它由一系列节点组成,每个节点包含两部分:存储的数据和指向下一个节点的指针。与数组不同,链表中的元素不是连续存储在内存中,而是通过指针链接在一起。
2.

链表结构介绍:
(1)数据域:存储数据元素信息的域。
(2)指针域:存储直接后继位置的域。
(3)结点:数据域和指针域组成的存储映像。
(4)头指针:链表中的第一个结点的存储位置。
之后的每一个结点,都位于上一个结点其后继指针所指向的位置, 最后一个结点的指针为“空”,用NULL符号表示。
(5)头结点:单链表的第一个结点。
①头结点的数据域可以不存储任何信息,也可以存储线性表长度等附加信息,头结点的指针域存储指向第一个结点的指针。
②头结点不是必须的,它的设立是为了操作的统一与方便,有了头结点,对在第一元素结点的前插与删除操作,与其他结点的操作是统一的。
(6)单链表:每个结点中只包含一个指针域的链表。
3.链表的特点:
(1)动态大小:链表可以在运行时根据需要添加或删除节点。
(2)高效的插入和删除:相比于数组,在链表中插入和删除元素通常更高效。
(3)随机访问效率低:访问链表中的某个元素,必须从头开始遍历直到目标位置。
二、单向链表的基本操作
1.EasyX初始化函数
(1)定义图形窗口相关参数,并对需要外部调用的函数进行声明。
//displayList.h
#pragma once
//EasyX 窗口尺寸
#define EASYX_WIDTH 1280
#define EASYX_HEIGHT 720
//EasyX汉字字体高度
#define TEXT_HEIGHT 20
#define MAX_DEBUG_CHAR 512
//函数声明
void init_display_window(void);
void end_display(void);
(2)实现初始化显示窗口函数,显示个测试字符,然后结束显示。
//displayList.cpp
//初始化EasyX显示窗口
void init_display_window(void)
{
initgraph(EASYX_WIDTH,EASYX_HEIGHT); //创建图形窗口
setbkcolor(WHITE);
cleardevice(); //使用背景色擦除整个屏幕
settextcolor(BLACK); //设置字体颜色
settextstyle(TEXT_HEIGHT,0,_T("微软雅黑")); //设置字体样式
setbkmode(TRANSPARENT); //设置字体为透明背景
outtextxy(10 , 20 , _T("ABCDEF"));
}
//结束显示
void end_display(void)
{
_getch(); //程序稍做暂停
closegraph();
}
- 在main函数中调用初始化与结束显示的函数。
//ListDemo.cpp
int main()
{
init_display_window();
end_display();
return 0;
}
测试结果:

3.创建链表
创建链表,需要多个节点并将它们连接起来。
// 在链表中插入一个新的卡片结点
void insert_card(CardList *list, NodeCard* newCard) {NodeCard *current = (NodeCard*)*list; // 将当前指针设置为链表的头结点
while(current->next != NULL)
{
// 遍历链表直到最后一个结点
current = current->next;
// 移动到下一个结点
}
current->next = newCard; // 将最后一个结点的next指针指向新插入的卡片结点
}
// 显示链表中结点间的箭头,表示指针的指向关系
void display_arrows(CardList list)
{
CardList p = list; // 从头结点开始遍历
NodeCard *tmp;
while(p)
{ // 遍历链表直到NULL
if(p->next != NULL && p->pointer == p->next->address)
{ // 如果当前结点的next不为空,并且当前结点的指针域等于下一个结点的地址域
int x1 = p->right; // 起点的x坐标为当前结点的右边界
int y1 = (p->bottom + p->top)/2; // 起点的y坐标为当前结点的垂直中心
int x2 = p->next->left; // 终点的x坐标为下一个结点的左边界
int y2 = (p->next->bottom + p->next->top)/2; // 终点的y坐标为下一个结点的垂直中心
draw_arrow(x1, y1, x2, y2); // 绘制箭头
}
p = p->next; // 移动到下一个结点
}
}
// 显示链表的图形化表示,并绘制结点间的箭头
void display_list(LinkList L)
{
LinkList p = L; // 从头结点开始遍历
int tmpx = 10; // 初始化结点卡片的横坐标
int tmpy = 10; // 初始化结点卡片的纵坐标
NodeCard *tmp; // 临时变量,用于存储结点卡片的指针
while(p)
{
// 遍历链表直到NULL
tmp = display_card(tmpx, tmpy, 0, p); // 显示当前结点的卡片,并获取卡片的指针
insert_card(&CL, tmp); // 将当前结点的卡片插入到卡片链表中
tmpx = tmp->right + 50; // 增加横坐标,为下一个结点卡片预留空间
if(tmpx > EASYX_WIDTH - (tmp->right - tmp->left))
{
tmpx = 10; // 重置横坐标到初始值
tmpy += tmp->bottom + 20; // 增加纵坐标,换行显示
}
p = p->next; // 移动到下一个结点
}
display_arrows(CL); // 绘制卡片链表中结点间的箭头
clear_card_list(&CL); // 清空卡片链表,释放内存
}
这段代码主要实现了三个功能:
- insert_card:在卡片链表的末尾插入一个新的卡片结点。
- display_arrows:遍历卡片链表,并在相邻结点间绘制箭头,表示链表中结点的连接关系。
3.display_list:遍历链表,显示每个结点的卡片,并在卡片链表中插入对应的卡片结点,最后绘制所有结点间的连接箭头,并清空卡片链表。
运行结果:

4.插入节点
插入节点:(1)头插法(2)尾插法(3)中间插入
- 头插法
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
void CreateListHead(LinkList *L, int n)
{
LinkList p;
int i;
srand(time(0)); /* 初始化随机数种子 */
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL; /* 先建立一个带头结点的单链表 */
for (i=0; i<n; i++)
{
p = (LinkList)malloc(sizeof(Node)); /* 生成新结点 */
p->data = rand()%100+1; /* 随机生成100以内的数字 */
p->next = (*L)->next;
(*L)->next = p; /* 插入到表头 */
}
}
- 尾插法
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0)); /* 初始化随机数种子 */
*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
r=*L; /* r为指向尾部的结点 */
for (i=0; i<n; i++)
{
p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */
p->data = rand()%100+1; /* 随机生成100以内的数字 */
r->next=p; /* 将表尾终端结点的指针指向新结点 */
r = p; /* 将当前的新结点定义为表尾终端结点 */
}
r->next = NULL; /* 表示当前链表结束 */
}
5.插入和删除
- 插入
//动画演示新结点插入的过程,参数为链表,位置,新结点数据
Status display_list_insert(LinkList *L,int i,ElemType e)
{
int cnt;
LinkList p,s;
p = *L;
cnt = 1;
while (p && cnt < i) /* 寻找第i个结点 */
{
p = p->next;
++cnt;
}
if (!p || cnt > i)
return ERROR; /* 第i个元素不存在 */
//擦除屏幕靠下区域
setfillcolor(WHITE);
solidrectangle(0,200,EASYX_WIDTH,EASYX_HEIGHT);
debug_info(_T("执行插入命令,在%d位置,插入节点%d。。。"),i,e);
int sleep_time=1000;
s = (LinkList)malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
display_text(800,200,_T("结点申请内存:s=(LinkList)malloc(sizeof(Node));"));
display_card(10,200,0,p);//显示前一个结点
display_card(400,200,1,s);//显示新建的结点
//_getch();
Sleep(sleep_time);
s->data = e;
display_text(800,300,_T("结点数据域赋值:s->data = e;"));
display_card(10,300,0,p);//显示前一个结点
display_card(400,300,1,s);//显示新建的结点
_getch();
s->next = p->next; /* 将p的后继结点赋值给s的后继 */
display_text(800,400,_T("将p的后继结点赋值给s的后继:s->data = p->next;"));
display_card(10,400,0,p);//显示前一个结点
display_card(400,400,1,s);//显示新建的结点
_getch();
p->next = s; /* 将s赋值给p的后继 */
display_text(800,500,_T("将s赋值给p的后继:p->next = s"));
display_card(10,500,0,p);//显示前一个结点
display_card(400,500,1,s);//显示新建的结点
_getch();
setfillcolor(WHITE);
solidrectangle(0,0,EASYX_WIDTH,200);
display_list(*L);
debug_info(_T("结点%d已经插入完毕,按任意键结束\n"));
_getch();
return OK;
}
插入动画演示:(双击播放视频)
插入动画演示
- 删除
//列表中删除结点
Status display_List_delete(LinkList *L,int i,ElemType *e)
{
int cnt;
LinkList p,s; /* 声明结点p与s(类型为指向结点的指针) */
p = *L;
cnt = 1;
while (p->next && cnt < i) /* 遍历寻找第i个元素 */
{
p = p->next;
++cnt;
}
if (!(p->next) || cnt > i)
return ERROR; /* 第i个元素不存在 */
setfillcolor(WHITE);
solidrectangle(0,200,EASYX_WIDTH,EASYX_HEIGHT);
debug_info(_T("执行删除位置,删掉第%d个结点\n"),i);
int sleep_time=1000;
_getch();
s = p->next;
display_text(800,200,_T("找到待删除结点s,p是s的前一个结点, s = p->next;"));
display_text(80,170,_T("结点p"));
display_card(10,200,0,p);
display_text(470,170,_T("结点s"));
display_card(400,200,1,s);
Sleep(sleep_time);
p->next = s->next; /* 将s的后继赋值给p的后继 */
display_text(800,300,_T("将s的后继赋值给p的后继,p->next = s->next; "));
display_text(80,270,_T("结点p"));
display_card(10,300,0,p);
display_text(470,270,_T("结点s"));
display_card(400,300,1,s);
Sleep(sleep_time);
*e = s->data; /* 将s结点中的数据给e */
free(s); /* 让系统回收此结点,释放内存 */
if(p->next)
{
display_text(800,400,_T("将s释放,free(s); "));
display_text(80,370,_T("结点p"));
display_card(10,400,0,p);
display_text(470,370,_T("结点p的后继结点"));
display_card(400,400,1,p->next);
Sleep(sleep_time);
}
else
{
display_text(470,370,_T("不存在 结点p的后置结点"));
}
setfillcolor(WHITE);
solidrectangle(0,0,EASYX_WIDTH,170);
display_list(*L);
debug_info(_T("结点%d已经删除完毕,按任意键继续\n"),*e);
_getch();
return OK;
}
删除动画演示:(双击播放视频)
删除动画演示
链表简介、操作及学习关键点
627

被折叠的 条评论
为什么被折叠?



