动态链表之所以称之为动态,因为它的存储管理方便,用的时候申请空间,不用的时候释放掉空间。
静态链表的目的就是通过函数实现模拟动态链表中的申请空间和释放空间,同是也要记录下一个节点的位置,那么需要解决几个问题。
1.如何模拟动态节点。
动态链表需要空间的时候用malloc函数来从计算机系统中申请空间,并返回该空间的地址以便操作,释放空间时用free函数将该空间重新返回给计算机系统中。
静态链表中为了模拟这种申请空间和释放空间的做法,需要两个函数来实现。因为是静态链表,所以只能用数组来表示节点,并且节点的内容包含数据域和指针域(暂且称为指针,但并不是指针),因此可以定义一个结构体数组,将每个结构体看作是一个节点,结构体内包含数据域和指针域,可以做如下操作。
#define MAXSIZE 1000 typedef int Elem;//数据类型 typedef struct { Elem data;//数据域 int cur;//指针域 }StaticLinkList;
StaticLinkList List[MAXSIZE];
2.如何模拟申请到的空间和空闲的空间以及整个计算机空间。
申请完结构体数组后,将该数组看作是整个计算机空间。用的时候从这个里面申请出一个节点来存放内容,释放的话将该节点重新返回到空闲空间中。也就是说这整个结构体数组分为两个部分,一部分是已经用的,另一部分是空闲的,所以需要将这两部分区分开来。
为了记录空闲空间和已占用空间的位置,需要两个单独的节点来保存这两部分的起始点。
用第一个节点(也就是数组的第一个元素)来保存空闲空间的起始位置,用最后一个节点(也就是数组的最后一个元素)来保存已用空间的起始位置。
在计算机中每一个空间内存都有自己独特的地址来标记,既然要模拟计算机系统,那么每个空间也应该有着自己的独特标记,而数组最好的标记就是每个元素的索引值了。
所以可以在最初的时候将其初始化,使每个数组元素都有着自己独特的标记。
void CreateList(StaticLinkList *P)//创建一个静态链表 { int i; for(i=0;i<MAXSIZE-1;i++) { P[i].cur = i+1; } P[MAXSIZE].cur = 0;//链表最初的第一个元素下标为0 }此时并没有已占用空间,所以第一个节点中的指针(cur)的值为1,也就是说空闲空间的起始位置为1(下标为的1也是数组的第二个元素);而最后一个节点的指针的值为0,因为此时并没有以占用的空间,最后一个节点类似于动态链表中的头结点,当链表为空的时候就指向NULL(在这里指向0),所以当头结点中的指针等于0时代表链表为空。
可以在最初的时候给链表赋值。
void InitList(StaticLinkList *P) { int n,i; printf("请输入链表的长度:"); scanf("%d",&n); for(i=1;i<=n;i++)//从下标1开始输入内容 { printf("请输入数据:"); scanf("%d",&P[i].data); P[0].cur = P[i].cur;//0位置记录下一个空闲位置下标 } P[i-1].cur = 0; P[MAXSIZE-1].cur = 1;//记录链表第一个元素的下标 }最后一个存放数据的cur为0;
3.如何模拟申请空间,插入节点。
由于第一个节点中的指针用来保存空闲空间的起始地址,所以只要修改第一个元素的cur即可。
int Malloc_List(StaticLinkList *P)//模拟创建节点,返回的是节点的下标 { int i; i = P[0].cur; P[0].cur = P[i].cur; return i; }先将空闲空间的位置赋值给i,然后再将i中保存的下一个位置赋值给第一个元素。
例如图中:想要申请到位置7的空间,先修改第一个的数值,将其改为位置7中记录的数值(也就是8),那么位置7就相当于不在空闲空间中了,而且因为第一个节点中保存的是8,也就是说空闲空间的起始位置就是8了。
如果想将位置7插入到“乙”和“丁”之间的话,需要修改两个地方。1.由于“乙”中保存的是下一个元素的位置,那么需要将“乙”中下一个元素的位置修改为7,这样在“乙”结束后会找下一个元素时就会在位置7上面找。2.由于位置7中保存的是下一个元素的位置,那么需要将其修改为3(也就是“丁”的下标),这样在找完位置7之后就是位置3了。
插入的方式可以如下:
void ListInsert(StaticLinkList *P)//插入链表 { int k,n,i,j; printf("请输入插入的位置:"); scanf("%d",&n); if(n<1 || n>P[0].cur) { printf("插入位置异常,插入失败!\n"); return ; } if(ListLength(P) == MAXSIZE-1)//如果链表满了 { printf("静态链表已满,插入失败!\n"); return ; } k = MAXSIZE-1; j = Malloc_List(P); for(i=1;i<n;i++) k = P[k].cur; P[j].cur = P[k].cur; P[k].cur = j; printf("请输入插入的数据:"); scanf("%d",&P[j].data); }
4.如何模拟释放空间,删除节点。
同理,既然第一个节点保存的是空闲空间的起始位置,那么修改第一个节点中的cur值即可。
void FreeList(StaticLinkList *P,int k)//模拟释放节点 { P[k].cur = P[0].cur; P[0].cur = k; }
将需要释放的位置k传入进来,让空闲空间的起始位置为该释放的位置(也就是k),并且将k的下一个节点的值设置为刚才的起始位置,这样就将空闲空间的位置连接起来,并且需要修改相应位置上的cur,让其连接起来。
如下图要释放位置1上的空间。
修改前:
修改后:
例如上图,将位置1的空间释放,先让空闲空间的起始位置为1,并且让位置1的下一个位置为8,所以空闲空间也就连起来了,1——8——9——10..,....(原先是:8——9——10........);而且需要修改删除位置前的cur,使其能够找到下一个元素,在这里由于删除的是第一个元素,所以需要修改头结点(最后一个元素)的cur,原来是1,将其改为原先位置1中的cur(cur保存的是下一个节点的位置,1中原先保存的是2),那么查找顺序就会是2——7——3——...(原先是1——2——7——3——...)。
删除的方式可以如下:
void DeleteList(StaticLinkList *P)//删除节点 { int n,i,k,j; printf("请输入删除的位置:"); scanf("%d",&n); if(n<1 || n>ListLength(P)) { printf("删除的位置异常,删除失败!\n"); return ; } k = MAXSIZE-1; for(i=1;i<n;i++) k = P[k].cur; j = P[k].cur; P[k].cur = P[j].cur; FreeList(P,j); }
5.如何遍历链表。
因为最后一个元素相当于头结点,并且其中保存的是链表第一个元素的位置,所以应该从头结点开始遍历,然后根据每个节点中的cur的值找到下一个节点的位置。
遍历代码可以如下:
void OutputList(const StaticLinkList *P)//输出链表 { int i,k; k = MAXSIZE-1; for(i=1;i<=ListLength(P);i++) { k = P[k].cur; printf("%d\n",P[k].data); } }
k最先等于999;链表长度为6(甲乙丁戊己庚)。
依次遍历的话,i的值从1开始到6结束公六次循环。
k的值依次为;1 2 3 4 5 6.每次根据k的值将其数据输出即可。
整体代码可以如下:
#include <stdio.h> #define MAXSIZE 1000 typedef int Elem; typedef struct { Elem data; int cur; }StaticLinkList; void CreateList(StaticLinkList *P)//创建一个静态链表 { int i; for(i=0;i<MAXSIZE-1;i++) { P[i].cur = i+1; } P[MAXSIZE].cur = 0;//链表最初的第一个元素下标为0 } void InitList(StaticLinkList *P) { int n,i; printf("请输入链表的长度:"); scanf("%d",&n); for(i=1;i<=n;i++)//从下标1开始输入内容 { printf("请输入数据:"); scanf("%d",&P[i].data); P[0].cur = P[i].cur;//0位置记录下一个空闲位置下标 } P[i-1].cur = 0; P[MAXSIZE-1].cur = 1;//记录链表第一个元素的下标 } int ListLength(const StaticLinkList *P) { int i,k,m=0; k = MAXSIZE-1; while(P[k].cur) { k = P[k].cur; m++; } return m; } void GetElem(const StaticLinkList *P)//按位置查找元素 { int n,i,k; printf("请输入查找元素的位置:"); scanf("%d",&n); if(n<1 || n>P[0].cur-1) { printf("查找位置异常,查找失败!\n"); return ; } k = P[MAXSIZE-1].cur; for(i=1;i<n;i++) { k = P[k].cur; } printf("第%d位置上的元素为:%d",n,P[k].data); } void FindList(StaticLinkList *P)//按元素查找 { int n,k,i,m=1; printf("请输入查找的内容:"); scanf("%d",&n); k = P[MAXSIZE-1].cur; for(i=1;i<=ListLength(P);i++) { if(P[k].data == n) { printf("所查找的元素在位置:%d\n",m); return ; } m++; k = P[k].cur; } printf("没有查找到!\n"); } int Malloc_List(StaticLinkList *P)//模拟创建节点,返回的是节点的下标 { int i; i = P[0].cur; P[0].cur = P[i].cur; return i; } void ListInsert(StaticLinkList *P)//插入链表 { int k,n,i,j; printf("请输入插入的位置:"); scanf("%d",&n); if(n<1 || n>P[0].cur) { printf("插入位置异常,插入失败!\n"); return ; } if(ListLength(P) == MAXSIZE-1)//如果链表满了 { printf("静态链表已满,插入失败!\n"); return ; } k = MAXSIZE-1; j = Malloc_List(P); for(i=1;i<n;i++) k = P[k].cur; P[j].cur = P[k].cur; P[k].cur = j; printf("请输入插入的数据:"); scanf("%d",&P[j].data); } void FreeList(StaticLinkList *P,int k)//模拟释放节点 { P[k].cur = P[0].cur; P[0].cur = k; } void DeleteList(StaticLinkList *P)//删除节点 { int n,i,k,j; printf("请输入删除的位置:"); scanf("%d",&n); if(n<1 || n>ListLength(P)) { printf("删除的位置异常,删除失败!\n"); return ; } k = MAXSIZE-1; for(i=1;i<n;i++) k = P[k].cur; j = P[k].cur; P[k].cur = P[j].cur; FreeList(P,j); } void OutputList(const StaticLinkList *P)//输出链表 { int i,k; k = MAXSIZE-1; for(i=1;i<=ListLength(P);i++) { k = P[k].cur; printf("%d\n",P[k].data); } } int main() { StaticLinkList List[MAXSIZE]; CreateList(List); InitList(List); ListInsert(List); DeleteList(List); ListLength(List); FindList(List); OutputList(List); GetElem(List); return 0; }