数据结构与算法——第2章-静态链表基本操作(2.6.2)

一 概述

1
2
3
4
1.静态链表添加元素
2.静态链表删除元素
3.静态链表查找元素
4.静态链表中更改数据

二 静态链表

本节建立在已成功创建静态链表的基础上,假设建立好的静态链表如下

三 静态链表基本操作

3.1 静态链表添加元素

一、说明

1
2
3
4
在上图的基础上,将元素 4 添加到静态链表中的第 3 个位置上,实现过程如下:
1.从备用链表中摘除一个节点,用于存储元素 4
2.找到表中第 2 个节点(添加位置的前一个节点,这里是数据元素 2),将元素 2 的游标赋值给新元素 4
3.将元素 4 所在数组中的下标赋值给元素 2 的游标

二、图示

经过以上几步操作,数据元素 4 就成功地添加到了静态链表中,此时新的静态链表如下图所示

三、C 语言程序实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 向链表中插入数据, body 表示链表的头结点在数组中的位置, add 表示插入元素的位置, a 表示要插
// 入的数据
void insertArr(component * array,int body,int add,char a){
// tempBody 做遍历结构体数组使用
int tempBody=body;
// 找到要插入位置的上一个结点在数组中的位置
for (int i=1; i<add; i++) {
tempBody=array[tempBody].cur;
}
// 申请空间, 准备插入
int insert=mallocArr(array);
array[insert].data=a;
// 新插入结点的游标等于其直接前驱结点的游标
array[insert].cur=array[tempBody].cur;
// 直接前驱结点的游标等于新插入结点所在数组中的下标
array[tempBody].cur=insert;
}

3.2 静态链表删除元素

一、操作说明

1
2
3
4
5
6
静态链表中删除指定元素,只需实现以下 2 步操作:
1.将存有目标元素的节点从数据链表中摘除
2.将摘除节点添加到备用链表,以便下次再用

若涉及大量删除元素的操作,建议在建立静态链表之初创建一个带有头节点的静态链表,
方便实现删除链表中第一个数据元素的操作。

二、实现该操作的 C 语言代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 备用链表回收空间的函数, 其中 array 为存储数据的数组, k 表示未使用节点所在数组的下标
void freeArr(component * array,int k){
array[k].cur=array[0].cur;
array[0].cur=k;
}

// 删除结点函数, a 表示被删除结点中数据域存放的数据
void deletArr(component * array,int body,char a){
int tempBody=body;
// 找到被删除结点的位置
while (array[tempBody].data!=a) {
tempBody=array[tempBody].cur;
// 当 tempBody 为 0 时, 表示链表遍历结束, 说明链表中没有存储该数据的结点
if (tempBody==0) {
printf("链表中没有此数据");
return;
}
}
// 运行到此, 证明有该结点
int del=tempBody;
tempBody=body;
// 找到该结点的上一个结点, 做删除操作
while (array[tempBody].cur!=del) {
tempBody=array[tempBody].cur;
}
// 将被删除结点的游标直接给被删除结点的上一个结点
array[tempBody].cur=array[del].cur;
// 回收被摘除节点的空间
freeArr(array, del);
}

3.3 静态链表查找元素

一、操作说明

1
2
静态链表查找指定元素时,由于只知道静态链表第一个元素所在数组中的位置,
因此只能通过逐个遍历静态链表的方式,查找存有指定数据元素的节点。

二、C 语言实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在以 body 作为头结点的链表中查找数据域为 elem 的结点在数组中的位置
int selectElem(component * array,int body,char elem){
int tempBody=body;
// 当游标值为 0 时, 表示链表结束
while (array[tempBody].cur!=0) {
if (array[tempBody].data==elem) {
return tempBody;
}
tempBody=array[tempBody].cur;
}
// 返回 -1, 表示在链表中没有找到该元素
return -1;
}

3.4 静态链表中更改数据

一、操作说明

1
更改静态链表中的数据,只需找到目标元素所在的节点,直接更改节点中的数据域即可

二、C 语言代码

1
2
3
4
5
6
7
8
9
// 在以 body 作为头结点的链表中将数据域为 oldElem 的结点, 数据域改为 newElem
void amendElem(component * array,int body,char oldElem,char newElem){
int add=selectElem(array, body, oldElem);
if (add==-1) {
printf("无更改元素");
return;
}
array[add].data=newElem;
}

3.5 静态链表“增删查改”的完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <stdio.h>

#define maxSize 7

typedef struct {
char data;
int cur;
}component;

// 将结构体数组中所有分量链接到备用链表中
void reserveArr(component *array);
// 初始化静态链表
int initArr(component *array);
// 向链表中插入数据, body 表示链表的头结点在数组中的位置, add 表示插入元素的位置
// a 表示要插入的数据
void insertArr(component * array,int body,int add,char a);
// 删除链表中含有字符 a 的结点
void deletArr(component * array,int body,char a);
// 查找存储有字符 elem 的结点在数组的位置
int selectElem(component * array,int body,char elem);
// 将链表中的字符 oldElem 改为 newElem
void amendElem(component * array,int body,char oldElem,char newElem);
// 输出函数
void displayArr(component * array,int body);
// 从备用链表中摘除空闲节点的实现函数
int mallocArr(component * array);
// 将摘除下来的节点链接到备用链表上
void freeArr(component * array,int k);

int main() {
component array[maxSize];
int body=initArr(array);
printf("静态链表为:\n");
displayArr(array, body);

printf("在第3的位置上插入结点‘e’:\n");
insertArr(array, body, 3,'e');
displayArr(array, body);

printf("删除数据域为‘a’的结点:\n");
deletArr(array, body, 'a');
displayArr(array, body);

printf("查找数据域为‘e’的结点的位置:\n");
int selectAdd=selectElem(array,body ,'e');
printf("%d\n",selectAdd);
printf("将结点数据域为‘e’改为‘h’:\n");
amendElem(array, body, 'e', 'h');
displayArr(array, body);
return 0;
}

// 创建备用链表
void reserveArr(component *array){
for (int i=0; i<maxSize; i++) {
// 将每个数组分量链接到一起
array[i].cur=i+1;
array[i].data=' ';
}
// 链表最后一个结点的游标值为 0
array[maxSize-1].cur=0;
}

// 初始化静态链表
int initArr(component *array){
reserveArr(array);
int body=mallocArr(array);
// 声明一个变量, 把它当指针使, 指向链表的最后的一个结点, 因为链表为空, 所以和头结点重合
int tempBody=body;
for (int i=1; i<5; i++) {
// 从备用链表中拿出空闲的分量
int j=mallocArr(array);
// 将申请的空线分量链接在链表的最后一个结点后面
array[tempBody].cur=j;
// 给新申请的分量的数据域初始化
array[j].data='a'+i-1;
// 将指向链表最后一个结点的指针后移
tempBody=j;
}
// 新的链表最后一个结点的指针设置为 0
array[tempBody].cur=0;
return body;
}

void insertArr(component * array,int body,int add,char a){
int tempBody=body;
for (int i=1; i<add; i++) {
tempBody=array[tempBody].cur;
}
int insert=mallocArr(array);
array[insert].cur=array[tempBody].cur;
array[insert].data=a;
array[tempBody].cur=insert;
}

void deletArr(component * array,int body,char a){
int tempBody=body;
// 找到被删除结点的位置
while (array[tempBody].data!=a) {
tempBody=array[tempBody].cur;
// 当 tempBody 为 0 时, 表示链表遍历结束, 说明链表中没有存储该数据的结点
if (tempBody==0) {
printf("链表中没有此数据");
return;
}
}
// 运行到此, 证明有该结点
int del=tempBody;
tempBody=body;
// 找到该结点的上一个结点, 做删除操作
while (array[tempBody].cur!=del) {
tempBody=array[tempBody].cur;
}
// 将被删除结点的游标直接给被删除结点的上一个结点
array[tempBody].cur=array[del].cur;

freeArr(array, del);
}

int selectElem(component * array,int body,char elem){
int tempBody=body;
// 当游标值为 0 时, 表示链表结束
while (array[tempBody].cur!=0) {
if (array[tempBody].data==elem) {
return tempBody;
}
tempBody=array[tempBody].cur;
}
// 返回 -1, 表示在链表中没有找到该元素
return -1;
}

void amendElem(component * array,int body,char oldElem,char newElem){
int add=selectElem(array, body, oldElem);
if (add==-1) {
printf("无更改元素");
return;
}
array[add].data=newElem;
}

void displayArr(component * array,int body){
// tempBody 准备做遍历使用
int tempBody=body;
while (array[tempBody].cur) {
printf("%c,%d ",array[tempBody].data,array[tempBody].cur);
tempBody=array[tempBody].cur;
}
printf("%c,%d\n",array[tempBody].data,array[tempBody].cur);
}

// 提取分配空间
int mallocArr(component * array){
// 若备用链表非空, 则返回分配的结点下标, 否则返回 0 (当分配最后一个结点时, 该结点的游
// 标值为 0)
int i=array[0].cur;
if (array[0].cur) {
array[0].cur=array[i].cur;
}
return i;
}

// 将摘除下来的节点链接到备用链表上
void freeArr(component * array,int k){
array[k].cur=array[0].cur;
array[0].cur=k;
}

/*
静态链表为:
,2 a,3 b,4 c,5 d,0
在第3的位置上插入结点‘e’:
,2 a,3 b,6 e,4 c,5 d,0
删除数据域为‘a’的结点:
,3 b,6 e,4 c,5 d,0
查找数据域为‘e’的结点的位置:
6
将结点数据域为‘e’改为‘h’:
,3 b,6 h,4 c,5 d,0
*/

四 参考

  • CSDN—静态链表基本操作