文章目录
前言
这一章我们来聊聊一些单链表的变形,自己写的数据结构十分的灵活,以至于我们能创造出很多我们想创造的结果。
一、静态链表
在早期的高级编程语言,如Basic、Fortran等,是没有指针的,那对于这类语言想使用链表要怎么办呢?有人想到了用数组代替指针描述单链表。数组的每个数据都和单链表的节点一样,只不过把单链表的指针换成了表示数组下标的游标。
(一)存储结构
# define MAXSIZE 1000
typedef struct{
ElemType data;
int cur; // 游标(cursor),0表示无指向
}Component, StaticLinkList[MAXSIZE];
(二)操作
1. 静态链表的初始化
通常我们把数组的第一个位置,即下标为0的元素的cur用来存放第一个没有数据的位置的下标,最后一个元素的cur用来存放第一个有数据的元素的下标。存放了数据的元素通过cur串成一个表,没放数据的也通过cur串成一个表,这样就可以通过首位元素来查询到所有存放了和没存放元素的位置了。倒数第二个元素的cur也要设置为0,这个原因我们后面再说
Status InitList(StaticLinkList space){
for(int i = 0; i < MAXSIZE-2; ++i){
space[i].cur = i + 1; // 目前所有元素为空,从下标0开始串联没有数据的元素
}
space[MAXSIZE-2] = 0;
space[MAXSIZE-1] = 0; // 表中没有数据,最后一个元素赋值为0表示没有指向
return OK;
}
2. 获取静态链表表长
int ListLength(StaticLinkList L){
int j = 0;
int i = L[MAXSIZE-1].cur;
while(i){
i = L[i].cur;
++j;
}
return j;
}
3. 静态链表的插入
在讲插入之前,我们先来看一个函数,该函数访问第一个节点,获取未被使用的空间,返回他的位置。这里我们要注意,如果静态链表存到了最后一个位置(整个数组的倒数第二个位置),倘若其cur不是0而是数组最后一个元素,也是我们静态链表的头结点,那么下一个存放数据的位置将是头结点,好像问题不是还很大,如果我们没注意到数组的容量,再放进一个元素头结点存放元素前要找位置调用以下函数,那space[0].cur就会更新为头结点的游标也就是存放第一个数据的位置,至此就构成了循环,后面在添加元素只会覆盖掉先前的元素,整个过程完全没有报错
int Malloc_SSL(StaticLinkList space){
int i = space[0].cur;
if(space[0].cur)
space[0].cur = space[i].cur;
return i;
}
要往链表里面插入数据,首先第一步是检查输入数据是否合法,第二步是找到可以存放数据的位置j,通过for循环将插入位置的前一个位置k找到,为防止k的后续信息消失,先把k的游标给到j的游标,再更新k的游标为j
Status ListInsert(StaticLinkList L, int i, ElemType e){
int k = MAXSIZE-1;
if(i < 1 || i > ListLength(L)+1) return ERROR;
int j = Malloc_SSL(L);
if(j){
L[j].data = e;
for(int l = 1; l <= i-1; ++l)
k = L[k].cur;
L[j].cur = [k].cur;
L[k].cur = j;
return OK;
}
return ERROR;
}
4. 静态链表的删除
和插入一样,我们先来看下面这个释放空间的函数
void Free——SSL(StaticLinkList space, int k){
space[k].cur = space[0].cur;
space[0].cur = k;
}
Status ListDelete(StaticLinkList L, int i){
if(i < 1 || i > ListLength(L)) return ERROR;
int k = MAXSIZE - 1;
for(int j = 1; j < i; ++j)
k = L[k].cur;
j = Lk].cur;
L[k].cur = L[j].cur;
Free_SSL(L,j);
return OK;
}
二、双向链表
每个节点都有两个指针,分别指向前驱和后继。双向链表是从单链表衍生出来的,所以有很多操作是一样的,这里主要讲插入和删除
(一)存储结构
typedef struct DuNode{
ElemType data;
struct DuNode *piror, *next;
}*DuLinkList;
(二)操作
1. 双向链表的取值(返回位置的指针)
Status GetElem(DuLinkList L, int i, DuLinkList &p){
p = L -> next;
int j = 1;
while(p || j < i){
p = p -> next;
++j;
}
if(!p || j < i) return ERROR;
return OK;
}
2. 双向链表的插入
要向双向链表中插入,首先找到要插入位置的前一个节点,我们一个方向一个方向来,先从前节点到插入节点到下一节点来更新指针,防止后节点丢失首先更新s的next为p的节点再更新p的next,同理在更新反向指针时,先更新s的prior再更新后节点的prior
void DuLinkListInsert(DuLinkList &L, int i, ElemType e){
DuLinkList p;
if(GetElem(L, i, p)){
s = new DuNode;
s -> data = e;
s -> next = p -> next;
p -> next = s;
s -> prior = p;
s -> next -> prior = s;
return OK;
}
return ERROR;
}
3. 双向链表的删除
这里的指针更新也比较好理解,因为我们找到的节点的位置是目标位置的前一个位置,所以需要更新其为下一个节点指向目标节点
void DuLinkListDelete(DuLinkList &L, int i, ElemType e){
DuLinkList p;
if(GetElem(L, i, p)){
p = p -> next;
e = p -> data;
p -> prior -> next = p -> next;
p -> next -> prior = p -> prior;
delete p;
return OK;
}
return ERROR;
}