静态链表
目的:为没有指针的程序语言所设计的实现单链表的方法
静态链表(游标实现法)
数组的元素被分为两个数据域data和cur
静态链表的定义与初始化
/*静态链表定义*/
#define MAXSIZE 1000
typedef struct
{
ElemType data;/*这里ElemType是数据域要存储的某种数据类型*/
int cur;
}Component,StaticLinkList[MAXSIZE];/*快捷定义结构数组的方法*/
/*语句StaticLinkList A就等价于Component A[MAXSIZE]*/
数组的第一个元素cur存放**备用链表第一个结点的下标,数组最后一个元素cur存放数组第一个有数值元素的下标**(在全表为空时为0,类似于头指针的作用)。链表最后一个元素其下一位置数据为空cur用0表示。
/*静态链表初始化*/
Status InitList(StaticLinkList space)/*space是结构数组,space[0].cur为头指针*/
{
int i;
for(i=0;i<MAXSIZE-1;i++)
space[i].cur=i+1;//将链表按照数组顺序指定
space[MAXSIZE-1].cur=0;//更改最后一个链表指针
return OK;
}
根据上述的下标规则,假设完成数据填入后的静态链表,其cur与下标的关系如图所示。
静态链表插入操作
- 情况1:在尾端插入新元素
/*在尾端插入新元素*/
int Malloc_SLL(StaticLinkList space)
{
int i=space[0].cur;/*获取备用链表第一个元素下标*/
if(space[0].cur)
space[0].cur=space[i].cur;/*调整第一个元素游标*/
/*由于备用链表的第一个元素要被使用了,因此要将它的下一个元素作为备用链表的第一个元素*/
return i;
}
- **情况2:**在列表中间插入新元素:先插入到最后,在调整cur
例如在乙和丁之间插入丙,只需要先在下标7位置处存入丙,然后修改乙的游标指向丙,再修改丙的游标指向丁即可完成。
/*在静态链表L的第i个元素之前插入新的数据元素e*/
Syayis ListInsert(StaticLinkList L,int i,ElemType e)
{
int j,k,l;
k=MAX_SIZE-1;//k是最后一个元素的下标
if(i<1||i>ListLength(L)+1)
return ERROR;
j=Malloc_SSL(L);//获取分配出的空闲元素的下标
if(j)
{
L[j].data=e;//先把元素存入分配空间
for(l=1;l<i-1;l++)
k=L[k].cur;/*通过指针不断迭代的方式,找到第i个元素之前的下标*/
L[j].cur=L[k].cur
L[k].cur=j;
return OK
}
return ERROR;
}
静态链表删除操作
简单描述为
1.脱链释放 将前后元素链接,完成释放。
如上图,无论是要删除已经使用的元素A,B还是C之中的一个,都先找到其上游元素。(遍历)如A的上游元素是末个元素。B的上游元素是A。
以删除B为例。找到上游元素,即A元素后,将其指向B的下一个元素C。如图。
2. 加入备用头 被释放元素成为备用链表首位元素。
主要分为两步
- 被释放元素指向原备用链表首元素
- 数组首个元素指向该被释放元素
至此,B就被释放到了备用链表区域
/*删除在L中第i个数据元素e*/
Status ListDelete(StaticLinkList L,int i)
{
int j,k;
if(i<1||i>ListLength(L))//遍历法计算链表已使用元素个数
return ERROR;
k=MAX_SIZE-1;/*指向数组末个元素*/
for(j=1;j<=i-1;j++)/*循环结束后,得到第i-1个元素的下标k*/
k=L[k].cur;
j=L[k].cur;/*j为第i个元素的下标*/
L[k].cur=L[j].cur;//1.脱链释放
Free_SSL(L,j);//2.加入备用头
return OK;
}
void Free_SSL(StaticLinkList space,int k)
{
space[k].cur=space[0].cur;/*2.1被释放元素指向原备用链表首元素*/
space[0].cur=k;/*2.2数组首个元素指向该被释放元素*/
}
循环链表
将单链表终端的结点指针部分由空指针改为指向头结点,形成==循环链表(circular linked list)==
循环链表所解决的问题是,单链表由于其单向性,无法回溯。循环链表可以从任何一个结点触发,访问到链表的全部结点。
由于该结构访问最后一个结点效率太低,将头指针改为尾指针
双向链表
在单链表的每个结点中,再设置一个指向其前驱结点的指针域,形成==双向链表(double linked list)==。
结点中有两个指针域,一个指向直接后继,一个指向直接前驱
链表例题
链表逆转
题目来源:https://pintia.cn/problem-sets/15/problems/724
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read();
void Print( List L );
List Reverse( List L );
int main()
{
List L1, L2;
L1 = Read();
std::cout<<"\033[0;32mread OK \033[0m"<<std::endl;
L2 = Reverse(L1);
std::cout<<"\033[0;32mreverse OK \033[0m"<<std::endl;
Print(L1);
Print(L2);
return 0;
}
/* 你的代码将被嵌在这里 */
List Read()
{
int n,i;
List L,p;
p=L=(List)malloc(sizeof(Node));
scanf("%d ",&n);//获取链表长度
for(i=0;i<n;i++){
p->Next=(List)malloc(sizeof(Node));
p=p->Next;
std::cin>>p->Data;//数据流格式读取的好处,可以同时匹配换行符或者空格分割的输入
}
p->Next=NULL;
return L;
}
void Print( List L )
{
while(L->Next!=NULL){
L=L->Next;
std::cout<<L->Data<<" ";
}
std::cout<<std::endl;
}
List Reverse(List L)
{ List p,q,r;
p=L->Next;
q=p->Next;
r=q->Next;
p->Next=NULL;
while(r!=NULL)
{
q->Next=p;
p=q;
q=r;
r=q->Next;
}
q->Next=p;
r=(List)malloc(sizeof(Node));
r->Next=q;
return r;
}
/*结果显示如下*/
1 3 4 5 2
read OK
reverse OK
1
2 5 4 3 1