数据结构:单向链表
文章目录
1.线性表的链式存储结构
- 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
- 线性表的链式表示又称为非顺序映像或链式映像。
- 用一组物理位置任意的存储单元来存放线性表的数据元素。
- 这组存储单元既可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。
- 链表中元素的逻辑次序和物理次序不一定相同。
1.1与链式存储有关的术语
- 结点:数据元素的存储映像。由数据域和指针域两部分组成
数据域 指针域 - 链表:
- n个结点由指针链组成一个链表。
- 它是线性表的链式存储映像,称为线性表的链式存储结构
1.2单链表、双链表、循环链表
- 结点只有一个指针域的链表,称为单链表或线性链表
- 结点有两个指针域的链表,称为双链表
- 首尾相接的链表称为循环链表
1.3头指针、头结点和首元结点
- 头指针:是指向链表中第一个结点的指针
- 首元结点:是指链表中存储第一个数据元素ā的结点
- 头结点:是在链表的首元结点之前附设的一个结点:
1.4链表的特点
(1)结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
(2)访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。
2.链表应该如何表示?
typedef int ElemType;
typedef struct LNode { //声明结点的类型和指向结点的指针类型
ElemType data; //结点的数据域
struct LNode* next; //结点的指针域
}Lnode,*LinkList;
- ElemType 的定义:
ElemType
被定义为int
类型,这意味着链表节点中的数据域data
将存储整数类型的数据。
- LNode 结构体的定义:
LNode
结构体包含两个成员:ElemType data
:用于存储节点的数据。struct LNode* next
:用于指向下一个节点的指针。
- LinkList 的定义:
LinkList
是一个别名,实际上是指向LNode
结构体的指针类型。这意味着LinkList
可以用来指向链表的头节点或任何节点。
3.单向链表的一些简单操作
3.1单向链表的初始化
算法步骤
(1)生成新结点作头结点,用头指针L指向头结点。
(2)将头结点的指针域置空。
算法描述
void InitLink(LinkList L) {
L = (LinkList)malloc(sizeof(Lnode));
L->next = NULL;
return true;
}
3.2单向链表的销毁
算法思路
从头指针开始,依次释放所有结点
算法描述
// 销毁链表
void DestoryLink(LinkList L) {
LinkList p;
while (L) {
p = L;
L = L->next;
free(p);
}
}
3.3清空链表
链表仍存在,但链表中无元素,成为空链表(头指针和头结点仍然在
算法思路
依次释放所有结点,并将头结点指针域设置为空
算法描述
//清空链表
void ClearLink(LinkList L) {
LinkList p,q;
p = L->next; // p指向头结点的下一个域
while (p) { // 循环条件是p不为空
q = p ->next; //q指向p的下一个域
free(p); //将p置为空
p = q; // p指向p的下一个域
}
L->next = NULL; //将头结点置为空
}
这个函数接受一个指向链表头结点的指针 L
,然后遍历链表,释放每个节点的内存,并将头结点的 next
指针设置为 NULL
,从而清空整个链表。
3.4求单链表包场
算法思路
从首元结点开始,依次计数所有结点
算法描述
//求单链表的表长
int LengthLink(LinkList L) {
int count = 0;
LinkList p = L->next;
while (p) {
count++;
p = p->next;
}
return count;
}
3.5取值——取单链表中第个元素的内容
算法思路
分别取出表中第n个元素
算法描述
// 查找链表中第n个元素
int FindLink(LinkList L,int n) {
LinkList p = L->next; //p指向第一个元素
//p找到的值应该不为空
while (n>0 && p) {
p = p->next;
n--;
}
//找到了并且p不为空
if (n == 0 && p) {
return p->data;
}
else {
return -1;
}
}
3.6按值查找
算法思路
根据指定数据获取该数据所在的位置
算法描述
//按值查找
int FindValLink(LinkList L,int n) {
int count = 0;
LinkList p = L->next;
while (p->data != n && p) {
p = p->next;
count++;
}
if (!p) {
return -1;
}
return count;
}
3.7插入
算法步骤
1、首先找到a-1的存储位置p。
2、生成一个数据域为e的新结点s。
3、插入新结点:①新结点的指针域指向结点a;②结点a;-1的指针域指向新结点
算法描述
void EnterLink(LinkList L,int n,int e) {
LinkList p = L;
LinkList s = (LinkList)malloc(sizeof(Lnode));
s->data = e;
while (n > 0 && p) {
p = p->next;
n--;
}
if(!p) {
printf("找不到相应的位置\n");
free(s); //释放刚刚的内存
}
s->next = p->next;
p->next = s;
}
3.8删除——删除第个结点
算法步骤
1、首先找到a;-1的存储位置p,保存要删除的a的值。
2、令p->next指向a41。
3、释放结点a的空间。
算法描述
//删除节点
void DestoryNode(LinkList L,int n) {
LinkList p = L;
int count = 0;
while (count < n - 1 && p) {
p = p->next;
count++;
}
if (!p) {
printf("找不到指定的元素\n");
}
p->next = p->next->next;
}
3.9链表的建立
3.9.1头插法——元素插入在链表头部
算法步骤
1.从一个空表开始,重复读入数据
2.生成新结点,将读入数据存放到新结点的数据域中
3.从最后一个结点开始,依次将各结点插入到链表的前端
算法描述
void EnterAheadLink(LinkList L,ElemType a[],int n) {
LinkList s;
L = (LinkList)malloc(sizeof(Lnode));
L->next = NULL;
for (int i = 0; i < n; i++) {
s = (LinkList)malloc(sizeof(Lnode));
s->data = a[i];
s->next = L->next;
L->next = s;
}
}
3.9.2 尾插法——元素插入在链表尾部
算法步骤
1.从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针指向链表的尾结点。
2.初始时,同均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,指向新结点。
算法描述
void EnterEndLink(LinkList L,int a[],int n) {
LinkList s;
L = (LinkList)malloc(sizeof(Lnode));
LinkList r = L;
for (int i = 0; i < n;i++) {
s = (LinkList)malloc(sizeof(Lnode));
s->data = a[i];
r->next = s;
r = s;
}
r->next = NULL;
}