逻辑结构上相邻的数据元素。存储在指定的一块内存空间中,数据元素只允许在该块内存空间中随机存放。该存储结构生成的链表称为静态链表。
静态链表与动态链表的区别:静态链表限制了数据元素存放的位置范围;动态链表存储元素范围是分配整个内存空间;
接下来看静态链表的数据结构:
typedef struct aa{
int data;//数据域
int cur;//游标
}component;
核心:构建结构体数组,数组作为顺序存储元素、结构体里两个变量:data实现数据存储,cur实现指向下一个结点的在数组中位置,进而实现类似链表的功能
//创建备用链表
//@nxt:人为将结构体数组划分为备用部分,每次用从备用部分去空间,并把首元地址(存放最后一个数)拿出来当为链表是否满的标志位,
void reserveArr(component *array){
int i;
for(i=0;i<maxSize;i++){
array[i].cur = i+1;//将每个数组分量链接到一起
}
array[maxSize-1].cur =0;//链表最后一个结点的游标值为0即,最右一个结点游标对应存放a[0].data值
}
核心:数组首元素array[0]作为备用链表头结点,其游标cur值为0指向数组最后一个元素,同样作为数组是否满的标志位
arry[1]作为存放数据链表的头结点,array[1].data存放数据,array[1].cur存放下一个结点在数组中的位置,例如array[1].cur=3,则下一个结点为数组中array[3];这里与传统数组区别,数据元素连续的但在上=存储空间上不是连续的;
//提前分配空间(每次使用备用链表都需要分配空间?)
int mallocArr(component *array){
//若备用链表非空,则返回分配的结点下标,否则返回0(当分配最后一个结点时,该结点的游标值为0)
int i = array[0].cur; //数组第一个元素里的 游标值,通过游标找到 一个结点在数组中的位置下标i
if(array[0].cur)
{ //重点:备用链表的游标存放下一结点在数组中的位置下标
array[0].cur = array[i].cur;//改变备用链表游标的值:通过游标i找到下一个结点在数组中的位置下标
}
printf("\n i:%d ", i);//why?1,2,3,4,5?
return i;
}
在初始化静态链表时,需要分配链表空间,分为"备用区"和"数据区";在未给链表数据区赋值时,链表全部为备用区;
向静态链表存储数据时,从"备用区"拿去空间存储数据,转换为"数据区"
//初始化静态链表
int initArr(component *array){
reserveArr(array); //链接备用链表
//从备用链表中拿出一个分量作为链表头结点,返回的事该分量的下标
int boby = mallocArr(array);//@数组首元素对应发游标值
//声明一个变量,把它当为指针使,指向链表的最后一个结点,因为链表为空,所以和头结点重合
int tempBody;
tempBody = body; //
int i;
for(i=1;i<5;i++){
int j=mallocArr(array);//从备用链表中拿出空闲的分量
array[tempBody].cur=j;//将申请的空线分量链接在链表的最后一个结点后面
array[j].data='a'+i-1;//给新申请的分量的数据域初始化
tempBody = j;
}
array[tempBody].cur=0;//新的链表最后一个结点的指针设置为0
return body;
}
静态链表初始化时,先创建备用链表(将结构体数组分区,"备用区");提前分配空间,通过首元素对应的游标,找到一个"数据区"结点位置;然后对该结点进行赋值操作,并依次根据游标找到下一个结点位置在赋值....
void display(component *array,int body){
int tempBody;
tempBody = body; //tempBody准备做遍历使用
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);
}
通过游标不为0(游标值为0位结构体数组最后一个元素)
//插入节点:向链表中插入数据,body表示链表的头结点在数组中的位置,add表示插入元素的位置,a表示要插入的数据
void insertArr(component *array,int body,int add, int a){
//@找到插入位置的结点
int tempBody =body;//tempBody做遍历结构体数组使用
printf("body:%d\n",body);
//找到要插入位置的上一个结点所在数组中的位置
int i;
for(i=1;i<add;i++){
tempBody = array[tempBody].cur;
}
int insert = mallocArr(array); //申请内存,准备插入
array[insert].cur = array[tempBody].cur; //首先要插入结点的游标等于要插入位置的上一个结点的游标
printf("cur: %d\n", array[tempBody].cur);
array[insert].data = a;
array[tempBody].cur = insert; //然后让上一结点的游标等于插入结点所在数组中的位置的下标
printf("insert: %d\n", insert);
//将结点的游标域与上下结点的游标关联起来
}
分析:
tempBody =1 |
i =1;i<add=3;i++ |
i=1; tempBody = array[1].cur ->2 |
i=2;tempBody = array[2].cur ->3 |
i=3;跳出循环 |
数组arry[0]为备用链表,array[1]为数据链表头元素 |
/*测试结果:
cur:1 cur:2 cur:3 cur:4 cur:5 cur:6 cur:7
i:1
i:2 j:2
i:3 j:3
i:4 j:4
i:5 j:5
静态链表为:
2 a 3 b 4 c 5 d, 0
查找数据域为‘a’的结点的位置:
3
将结点数据'b'改为'h'
无该元素
2 a 3 b 4 c 5 d, 0
body:1
i:6
cur: 4
insert: 6
2 a 3 b 6 e 4 c 5 d, 0
*/
//删除结点
void delArr(component *array,int body,char a){
int tempBody = body;
//找到被删除结点位置
int i,j,del;
while(array[tempBody].cur != a){
tempBody = array[tempBody].cur;
//当tempBody为0时,表示链表遍历结束,说明链表没有存储该数据的结点
if(tempBody == 0){
printf("链表中没有此数据\n");
return;
}
}
/*
while(array[tempBody].cur !=0){
if(array[tempBody].data == a){
return tempBody;
}
return -1;
} */
//找打被删除结点的上结点的
del = tempBody;
tempBody = body;
//当循环到删除结点的上一结点时,跳出循环
while(array[tempBody].cur != del){
tempBody = array[tempBody].cur;
}
array[tempBody].cur=array[del].cur;
freeArr(array,del);
}
//释放被删除结点空间
void freeArr(component *array, int k){
array[k].cur = array[0].cur;
array[0].cur = k;
}
//查找元素
//静态链表中数据查找,通过头结点(头结点在数组中的位置下标),遍历链表查找数据
//在以body作为头结点的链表中查找数据域为elem的结点在数组中的位置
int select(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;//将当前下标数组元素的游标赋值给tempBody,继续循环
}
return -1;//返回-1,表示在链表中未找到该元素
}
//修改元素
void change(component *array, int body,char oldElem,char newElem){
int add = select(array, body, oldElem);
if(add == -1){ // add = -1 报错
printf("\n无该元素\n");
return ;
}
array[add].data = newElem;
}
//测试结果
/*
cur:1 cur:2 cur:3 cur:4 cur:5 cur:6 cur:7
i:1
i:2 j:2
i:3 j:3
i:4 j:4
i:5 j:5
静态链表为:
2 a 3 b 4 c 5 d, 0
在第3位置上插入结点'e'
body:1
i:6 cur: 4
insert: 6
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
--------------------------------*/
完整代码
#include<stdio.h>
#define maxSize 7
/*
静态链表实现
*/
typedef struct aa{
int data;//数据域
int cur;//游标
}component;
void freeArr();
//创建备用链表
//@nxt:人为将结构体数组划分为备用部分,每次用从备用部分去空间,并把首元地址(存放最后一个数)拿出来当为链表是否满的标志位,
void reserveArr(component *array){
int i;
for(i=0;i<maxSize;i++){
array[i].cur = i+1;//将每个数组分量链接到一起
printf("cur:%d ",array[i].cur);
}
array[maxSize-1].cur =0;//链表最后一个结点的游标值为0即,最右一个结点游标对应存放a[0].data值
}
//提前分配空间(每次使用备用链表都需要分配空间?)
int mallocArr(component *array){
//若备用链表非空,则返回分配的结点下标,否则返回0(当分配最后一个结点时,该结点的游标值为0)
int i = array[0].cur; //数组第一个元素里的 游标值,通过游标找到 一个结点在数组中的位置下标i
if(array[0].cur)
{ //重点:备用链表的游标存放下一结点在数组中的位置下标
array[0].cur = array[i].cur;//改变备用链表游标的值:通过游标i找到下一个结点在数组中的位置下标
}
printf("\n i:%d ", i);//why?1,2,3,4,5?
return i;
}
//初始化静态链表
int initArr(component *array){
reserveArr(array); //链接备用链表
//从备用链表中拿出一个分量作为链表头结点,返回的事该分量的下标
int body = mallocArr(array);//@数组首元素对应发游标值
//声明一个变量,把它当为指针使,指向链表的最后一个结点,因为链表为空,所以和头结点重合
int tempBody;
tempBody = body; //
int i;
for(i=1;i<5;i++){
int j=mallocArr(array);//从备用链表中拿出空闲的分量
printf("j:%d\n", j);
array[tempBody].cur=j;//将申请的空线分量链接在链表的最后一个结点后面
array[j].data='a'+i-1;//给新申请的分量的数据域初始化
tempBody = j;
}
array[tempBody].cur=0;//新的链表最后一个结点的指针设置为0
return body;
}
void display(component *array,int body){
int tempBody;
tempBody = body; //tempBody准备做遍历使用
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);
}
//静态链表中数据查找,通过头结点(头结点在数组中的位置下标),遍历链表查找数据
//在以body作为头结点的链表中查找数据域为elem的结点在数组中的位置
int select(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;//将当前下标数组元素的游标赋值给tempBody,继续循环
}
return -1;//返回-1,表示在链表中未找到该元素
}
//静态链表中更改数据,通过查找算法找到要更改结点的位置,然后直接更改该结点的数据域
//一body作为头结点的链表中将数据域为oldElem的结点,数据域改为newELem
/*
void change(component *array, int body,char oldElem,char newElem){
int tempBody = body;
while(array[tempBody].cur !=0){
if(array[tempBody].data == oldElem){
array[tempBody].data = newElem;
}
tempBody = array[tempBody].cur;
}
}*/
void change(component *array, int body,char oldElem,char newElem){
int add = select(array, body, oldElem);
if(add == -1){ // add = -1 报错
printf("\n无该元素\n");
return ;
}
array[add].data = newElem;
}
//插入节点:向链表中插入数据,body表示链表的头结点在数组中的位置,add表示插入元素的位置,a表示要插入的数据
void insertArr(component *array,int body,int add, int a){
//@找到插入位置的结点
int tempBody =body;//tempBody做遍历结构体数组使用
printf("body:%d\n",body);
//找到要插入位置的上一个结点所在数组中的位置
int i;
for(i=1;i<add;i++){
tempBody = array[tempBody].cur;
}
int insert = mallocArr(array); //申请内存,准备插入
array[insert].cur = array[tempBody].cur; //首先要插入结点的游标等于要插入位置的上一个结点的游标
printf("cur: %d\n", array[tempBody].cur);
array[insert].data = a;
array[tempBody].cur = insert; //然后让上一结点的游标等于插入结点所在数组中的位置的下标
printf("insert: %d\n", insert);
//将结点的游标域与上下结点的游标关联起来
}
//
void delArr(component *array,int body,char a){
int tempBody = body;
//找到被删除结点位置
int i,j,del;
while(array[tempBody].data != a){
tempBody = array[tempBody].cur;
//当tempBody为0时,表示链表遍历结束,说明链表没有存储该数据的结点
if(tempBody == 0){
printf("链表中没有此数据\n");
return;
}
}
/*
while(array[tempBody].cur !=0){
if(array[tempBody].data == a){
return tempBody;
}
return -1;
} */
//找打被删除结点的上结点的
del = tempBody;
tempBody = body;
//当循环到删除结点的上一结点时,跳出循环
while(array[tempBody].cur != del){
tempBody = array[tempBody].cur;
}
array[tempBody].cur=array[del].cur;
freeArr(array,del);
}
void freeArr(component *array, int k){
array[k].cur = array[0].cur;
array[0].cur = k;
}
int main(){
component array[maxSize]; //定义一个结构体数组
int body = initArr(array);//将结构体数组首地址传递非结构体指针
printf("\n静态链表为:\n");
display(array,body);
printf("在第3位置上插入结点'e'\n");
insertArr(array, body, 3,'e');
display(array,body);
printf("删除数据域为'a'的结点\n");
delArr(array,body,'a');
display(array,body);
printf("查找数据域为‘e’的结点的位置:\n");
int Add;
Add = select(array, body,'e');//erro
printf("%d\n",Add);
printf("将结点数据'e'改为'h'\n");
change(array,body,'e','h');
display(array,body);
return 0;
}