线性表--顺序表--单向链表(四)
开头小故事
在讲链表之前,先讲一个小故事: 在很久很久以前,我从集市上买了一条哈士奇,众所周知,哈士奇,俗称撒手没,为了不让他跑掉,我需要一条链子来拴住它,但是我已经回家了,怎么办呢? 我需要自己动手来做一条,一条链子由什么组成?当然是一节节小链子,链子的样子你一定见过,每一节都有一个小口用来连接下一节,于是我做好了一节节的小链子,并一节一节连起来,然后,我们还需要确定哪一端是人拿的,哪一端是给狗套的,于是我又做了一根绳子接在了链子其中的一段。后来,二哈给我准备了一个20万的装修计划,,,,,,好了,小故事讲完了。配张图!!
正题之前,请先记住链子,小链子,绳子这三个词和对应的链表中的名词。
词 |
链表对应名词 |
---|---|
链子 |
链表 |
小链子 |
节点 |
绳子 |
头节点 |
现在来讲讲单向链表,首先,链表和数组不同,数组中元素的内存地址是连续的,而链表中的元素内存地址是随机的。由于链表中的节点的数据类型是相同的,并且是随机存储,所以我们不能像数组那样只需要知道头节点就可以访问整个链表,我们必须在每个节点中存储下一个节点的地址,就好比狗链子中每一节的口子,用来连接下一节链子,只有这样,我们才能一节一节访问整个链表。 数组是这样使用内存,内存地址是连续的
而链表是这样使用内存的,所有他们必须有指向下一节点的指针
看到到这里,你对链表应该有一个小的认识了吧,那么下面我们就来定义一个链表吧。
1.定义链表
typedef struct list
{
int x;
struct list * next;
}list;
想一想,定义了链表就相当于我们有了每节小链子的模具,接下来,我们就该造小链子以及绳子了。
2.头节点初始化
list * InitListHead() //初始化
{
list *Phead = (list *)malloc(sizeof(list));//创建头结点
Phead->x = 0; //该变量可存放该链表长度
Phead->next = NULL;
return Phead; //创建后返回该头指针
}
头节点的数据可以存放比如链表的长度等等,接下来,我们只需要重复创建节点,这便是链表,这里要注意一下的是,单向链表包括头插入和尾插入,下面将分开讲解。
3.头插法
void HeadList(list * L,int e)//头插法
{
list * Pbady = (list *)malloc(sizeof(list));//创建一个节点
Pbady->x = e;
Pbady->next = NULL;
if (L->next == NULL)
{
L->next=Pbady;
}
else
{
Pbady->next = L->next;
L->next = Pbady;
}
L->x++;
}
}
避免生硬的代码,我们用图来说明。
不难看出,使用头插法的数据是逆序排列的,后来的反而排在前面,这也是头插和尾插的区别之一。
4.尾插法
void TrailList(list * LA,list ** LB,int e)//尾插法
{
list *Pbady = (list*)malloc(sizeof(list));
Pbady->x = e;
if (LA->next == NULL)
{
LA->next = Pbady;
}
else
{
(*LB)->next = Pbady;
}
Pbady->next = NULL;
*LB = Pbady;
LA->x++;
}
尾插法相对于头插法多了一个指针,如果代码看着懵,那我们依旧上图来说。
尾插法不像头插法,只需要在开头插入就可以,而尾插入需要遍历整个链表并找到最后,如果链表足够长,每次都遍历一遍,显然不科学,所以我们想到用一个指针来随时记录最后一个节点的位置,并指向最后一个节点,这样,每当我们有新节点的时候,只需要在该指针后面插入即可。并更新该指针,至于为什么使用二级指针可查看该文。
接下来来看一下链表的基本运算。
5.按序号查找内容
int NumFindList(list *L,int e)//按序号查找
{
if (e <= 0)
{
return -1;
}
int i = 0;
int j = L->x;
list * P = L;
while (P != NULL&&i <= j)
{
if (i == e)
{
printf("该内容为%dn", P->x);
break;
}
P = P->next;
i++;
}
}
6.按值查找
void ValueFindList(list * L,int e)//按值查找
{
int i = 0;
int j = L->x;
list * P = L;
while (P != NULL&&i <= j)
{
if (P->x == e)
{
printf("已找到n");
}
P = P->next;
i++;
}
}
7.求链表长度
int LengthList(list * L)//链表长度
{
return L->x;//这里我们只需要访问头节点中的数据即可
}
8.插入操作
void InsertList(list * L,int i,int e)//插入操作
{
if (i > L->x)
{
printf("插入位置不合法n");
return ;
}
list * Pbady = (list *)malloc(sizeof(list));
list *P = L->next;
int a = 1;
Pbady->x = e;
while (P != NULL)
{
if (a == i-1)
{
Pbady->next = P->next;
P->next = Pbady;
L->x++;
break;
}
P = P->next;
a++;
}
}
9.删除操作
void DelList(list * L,int i)//删除操作
{
if (i > L->x)
{
printf("插入位置不合法n");
return;
}
list * P = L->next;
list * P_2 = NULL;
int a = 1;
while (P != NULL)
{
if (a == i-1)
{
P_2 = P->next;
P->next = P->next->next;
P_2->next = NULL;
free(P_2);
L->x--;
break;
}
P = P->next;
a++;
}
}
10.显示链表现有内容
void ShowList(list * L)//显示链表
{
list * P = L;
printf("该链表共有%d个元素n", P->x);
P = L->next;
while (P!= NULL)
{
printf("%dn", P->x);
P = P->next;
}
}
11.释放内存
void FreeList(list * L)
{
list * P;
if (L == NULL)
{
return ;
}
while (L)
{
P = L->next;
free(L);
L = P;
}
}
好了,单向链表的讲解就到这里。
如果有什么错误,欢迎指出,欢迎讨论。 每文一句:不要总是用眼泪去打动去挽留,有时侯你的眼泪适得其反,微笑,一定要自信的微笑着面对一切!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【tcl学习】vivado write_edif
- K8s中优雅停机和零宕机部署
- Python读取.edf格式脑电数据文件
- Kotlin修炼指南(三)——奇技淫巧
- 前端|CSS盒阴影和文字阴影
- 一文读懂Spring Boot各模块组件依赖关系
- 谈一谈|MkDocs介绍及应用
- 讲得最明白的Elasticsearch源码调试环境搭建教程
- 史上最全ThreadPoolExecutor梳理(下篇)
- JAVA|多Realm管理基础实现
- 史上最全ThreadPoolExecutor梳理(上篇)
- 使用缓存必须注意的事项
- ReentrantLock知识点梳理
- ZooKeeper常用API命令
- Python|数据可视化之公众号得分的柱状图