题目:
Question:写一个不带头节点的静态链表的插入和删除算法。
主要思路
链表的特性:
- 由于静态链表的存储结构是由数组实现的,但由于与顺序表不同,其并不是按顺序存储的,而是按照游标的顺序来存储的,也就是说假设静态链表的第一个元素存储在 中,那么链表第二个元素就可能不是 ,而是根据 的游标域来决定下一个元素在何处,若 的游标域的数值为4,则下一个元素为 。
- 静态链表有两个链,一个用于存放数据,在这里称作链表;另一个用于存放空数组,也就是备用链表,用于需要插入新的数据进静态链表中时,就调用备用链表中的节点,完成赋值操作后接到链表之后。
该题的特殊之处:
该静态链表为不带头节点的静态链表。一般来说静态链表会有两个头节点,因为有两个链表(链表和备用链表)。其中第一个节点为备用链表的头节点、最后一个节点为链表的头节点。(如下图所示)。因此我们采用双指针的思想,设p、q分别指向备用链表和链表的第一个元素,由于一开始链表为空,整个数组为备用链表,故 (q 为 -1 表示链表不存在)。
代码:
定义的部分:
一些基础的操作:
插入和删除操作:
(由于在调用删除和插入函数时链表不能为空,故使用之前必须的要运行基础操作中的InsertFirstData函数)。
完整代码(可运行):
#include <stdio.h>
#define MAXSIZE 100
typedef struct
{
int data;
int cur;
}node;
node LinkList [MAXSIZE];
//备用链表第一个首元节点的下标
int p = 0;
//链表的一个首元节点的下标,若链表为空则q为-1
int q = -1;
//将下标为K的空闲节点回收到备用链表
void Free(int k)
{
LinkList[k].cur = LinkList[p].cur;
LinkList[p].cur = k;
}
//初始化
void Init()
{
unsigned short i;
for(i = 0; i < MAXSIZE - 1; i++) LinkList[i].cur = i + 1;
LinkList[MAXSIZE - 1].cur = -1;
}
//插入第一个元素,值为i
void InsertFirstData(int i)
{
q = p;
LinkList[q].data = i;
p = LinkList[p].cur;
LinkList[q].cur = -1;
}
//返回LinkList中数据元素个数
int ListLength()
{
int cacu = 0;//记录元素个数
int q1 = q;
if(q1 == -1) return 0;
while(q1 != -1)
{
q1 = LinkList[q1].cur;
cacu ++;
}
return cacu;
}
//求前驱元素 cur_e是当前数据值,m返回为其前驱元素下标
int PriorElem(int cur_e, int m)
{
int j, i = q; //i指示链表第一个结点的位置
do //向后移动结点
{ j = i; //j指向i所指元素
i = LinkList[i].cur; //i指向下一个元素
}while(i && cur_e!=LinkList[i].data); //i所指元素存在且其值不是cur_e,继续循环
if(LinkList[i].data == cur_e) //找到该元素
{
m = j;
return m; //j是i所指元素的前驱元素的下标,赋给m
}
}
//在第i个元素前插入元素e
void SLinkInsert(int i, int e){
if(i < 1||i > ListLength()) return;
int m = -1;//前驱元素返回的数组下标
if(i > 1)
{
int q1 = q;
for(int k = 1; k < i; k++)
{
q1 = LinkList[q1].cur;
int cur_e = LinkList[q1].data;
m = PriorElem(cur_e, m);
}
int n = p; //临时指针
LinkList[p].data = e;
LinkList[m].cur = p;
p = LinkList[n].cur;
LinkList[n].cur = q1;
return;
}
if(i = 1)
{
int k = q; //临时指针
q = p;
p = LinkList[p].cur;
LinkList[q].cur = k;
LinkList[q].data = e;
}
return;
}
//删除第i个元素
void SLinkListDelet(int i)
{
if(i < 1||i > ListLength()) return;
int m = -1;//前驱元素返回的数组下标
if(i > 1)
{
int q1 = q;
for(int k = 1; k < i; k++)
{
q1 = LinkList[q1].cur;
int cur_e = LinkList[q1].data;
m = PriorElem(cur_e, m);
}
LinkList[m].cur = LinkList[q1].cur;
Free(q1);
return;
}
if(i = 1)
{
int k = q;//临时指针
q = LinkList[q].cur;
Free(k);
return;
}
}
int main(){
Init();//初始化
InsertFirstData(1);//插入之前需要链表中有至少一个元素 (1)
SLinkInsert(1, 0);//在第一个元素之前插入0 输出:0 -> 1
SLinkInsert(2, 2);//在第二个元素之前插入2 输出:0 -> 2 -> 1
SLinkListDelet(2);//删除第二个元素 输出:0 -> 1
printf("链表长度为%d\n",ListLength());
int k = q;//临时指针
for(int i = 1; i <= ListLength(); i++){
printf("第%d个数据是%d\t",i,LinkList[k].data);
k = LinkList[k].cur;
}
return 0;
}
一些图解:
结束语
因为是算法小菜,所以提供的方法和思路可能不是很好,请多多包涵~如果有疑问欢迎大家留言讨论,你如果觉得这篇文章对你有帮助可以给我一个免费的赞吗?我们之间的交流是我最大的动力!