碰过C的人都不禁感叹C的伟大与强大,C的强大在于它能灵活的使用指针对内存进行操作。当然指针这个搞点也不仅只在C有,其他的一些开发语言有的都是通过使用对象引用机制间接的实现指针的都某些功能。而在以前的时候,C语言还没出现,也没有java,只有一些原始的语言,这些语言没有类似于C指针的功能,但是他们又想实现单链表,于是···
代码有什么错误或者不够好的地方,请大神大牛多多指点。
有人想出了用数组代替指针来描述单链表,用数组描述的链表我们称之为静态链表,强调的是静态,我们知道数组在创建的时候就已经固定好它的类型占空间是多少字节,对于静态链表的描述法我们叫做游标实现法,神图:
上图有一个错误的地方,最有一个拥有数据的结点游标要改成0。
静态链表的第一个元素和最后一个元素不存储数据,而第一个结点的游标存储的是当前数据为空的尾结点的下标,而最后一个结点的游标存储的是存在数据的头结点的下标。图中能注意到第一个结点的游标为5,正是数据为空的结点的下标,而最后一个结点的游标为第一个含有数据的结点的下标。
除了首结点和尾结点,其他结点的游标存储的都是下一个结点的下标地址。
静态链表中的存在数据的最后一个结点游标为0,作为结尾标志,否则就没有结束了。
静态链表还有一个备用链表概念,这个概念对我们实现插入操作很有必要。我们将那些未被使用以及被删除的分量用游标链成一个备用链表。为了偷懒,我在大神的博客上挖来的(静态链表的特点):
其实我个人的理解,静态链表与单链表的差别,就是数组替换了指针,实际上其他都还是一个思想。
/*数据结构与算法之线性表_静态链表*/
#include<stdio.h>
#include<stdlib.h>
int BYD; //备用链表下标
int SSD; //链表首结点的下标
int DS; //链表所用的数据
#define MAX 100 //定义静态链表最大结点数
typedef struct
{
int data; //数据域
int cur; //游标域
}listnode;
void Ilist(listnode L[]) //初始化链表
{
//头结点记录的是备用链表的第一个结点的下标地址
//尾结点记录第一个有数据的结点的下标
//初始化时还未写入数据
int i;
for(i = 0;i <= MAX-1;i++)
{
L[i].data = 0;
L[i].cur = i+1;
}
L[0].cur = 0;
L[MAX-1].cur = 0;
for(i = 0;i <= MAX-1;i++)
{
printf("Data:%d cursor:%d\n",L[i].data,L[i].cur);
}
}
void Plist(listnode L[]) //打印链表
{
//静态链表的游标可以是不按顺序的,删除的结点还要被收回到备用链表
//cur指向下一个结点的下标
int i = 0;
for(i = L[MAX-1].cur;L[i].cur != 0;i = L[i].cur) //首结点存放第一个有数据的结点的下标,那第一个开始遍历
{
printf("Data:%d cursor:%d\n",L[i].data,L[i].cur);
}
printf("Data:%d cursor:%d\n",L[i].data,L[i].cur);
}
void SDlist(listnode L[]) //输入数据
{
int i; //从数组的第二个元素开始存储数据
int x; //x作为优化变量,记录用户需要存储的数据的数量
printf("请输入存储的数据的数量:");
scanf("%d",&x);
while( x > MAX-2 || x < 1 ) //判断是否超出链表最大限度以及非法输入
{
printf("超出链表最大限度或者非法输入(1~%d),若想退出此操作请输入0:",MAX-2);
scanf("%d",&x);
if(x == 0)
{
goto END;
}
}
printf("请依次输入存储的数据:\n");
for(i = 1;i <= x;i++)
{
scanf("%d",&L[i].data);
}
L[x].cur = 0;
L[0].cur = x+1; //备用链表地址调整
L[MAX-1].cur = 1;
DS = x;
BYD = x+1;
SSD = x;
END:;
}
int AlistF(listnode L[],int x) //辅助查找上一个结点
{
int i;
for(i = 1;L[i].cur != x;i++)
{
;
}
return i;
}
int BlistF(listnode L[],int x) //辅助查找下一个结点
{
int i;
for(i = 1;i != L[x].cur;i++)
{
;
}
return i;
}
void Alist(listnode L[]) //实现更改位置
{
int x1,x2; //区别下标和游标
int temp; //中间值
printf("将X位置的数据移动到第X个位置,请输入:");
scanf("%d %d",&x1,&x2);
while(x1 == x2)
{
printf("输入错误!将X位置的数据移动到第X个位置,请输入:");
scanf("%d %d",&x1,&x2);
}
while(x1 <= 0 || x1 >= BYD)
{
printf("输入的数据位置有误,请重新输入:");
scanf("%d",&x1);
}
while(x2 <= 0 || x2 >= BYD || x2 == x1)
{
printf("移动的位置有误,请重新输入:");
scanf("%d",&x2);
}
temp = L[x1].data;
L[x1].data = L[x2].data;
L[x2].data = temp;
printf("调整完毕!\n");
}
int Lmalloc(listnode L[])
{
int i;
for(i = 1;L[i].cur != 0;i++)
{
if(i == MAX-2)
{
return -1;
}
}
return i+1;
}
void INList(listnode L[]) //数据插入函数
{
int x,y,i;
y = Lmalloc(L);
if(y == -1)
{
printf("链表存储空间已达上限,无法写入数据\n");
goto END;
}
printf("请输入想要插入的数据:");
for(i = 1;L[i].cur != 0;i++)
{
;
}
scanf("%d",&x);
L[i].cur = y;
L[y].cur = 0;
L[y].data = x;
BYD++;
DS++;
END:;
}
void DEList(listnode L[])
{
int x;
int temp1,temp2;
printf("请输入你想删除的结点\n");
scanf("%d",&x);
while(x < 1 || x > DS)
{
printf("删除结点的位置有误,请重新输入:");
scanf("%d",&x);
}
temp1 = AlistF(L,x); //查找上一个结点
temp2 = BlistF(L,x); //查找下一个结点
L[x].data = 0;
L[temp1].cur = temp2;
printf("删除完毕!\n");
DS--;
}
int main()
{
listnode L[MAX];
system("color 0A");
printf("*****静态链表*****\n");
Ilist(L);
printf("初始化完毕!\n");
SDlist(L);
Plist(L);
Alist(L);
Plist(L);
printf("链表利用了%d空间.\n",DS);
INList(L);
Plist(L);
printf("链表利用了%d空间.\n",DS);
DEList(L);
Plist(L);
printf("链表利用了%d空间.\n",DS);
system("pause");
}
小菜写的,有些地方没有优化,函数间兼容性特别差,特别是实现删除的函数,本身就有错误,现在就先贴着,有时间再修改,所以删除函数先忽略。程序还比较冗长,有机会再返回来优化。
代码有什么错误或者不够好的地方,请大神大牛多多指点。