静态链表

最近在看《大话数据结构》,感觉思路讲的挺好的,但是和有些数据结构书籍有个共同的瑕疵,那就是没有写主函数,没有用主函数调用每一个函数,只是单列函数代码,这样感觉连不起来,然后我就看了看它们的代码,然后自己重写一遍按照自己的思路,学生党菜鸟写的不好,勿喷。

静态链表思路:
我们先定义这样一个结构体:
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data;//数据域
int cursor;//下一个数组元素的下标
}StaticLinkList ;
我们用data来保存数据,cursor保存下一个节点的下标,类似于指针实现的链表。
然后我们在定义一个这样的数组:
StaticLinkList SL[MAXSIZE];
想法是这样的,用上面的数组模拟链表,这样一个数组中包含两个逻辑上的链表:空闲链表和非空闲链表,每一个链表都有头结点;

非空闲链表的头结点时数组中最后一个元素,即SL[MAXSIZE-1],这个元素的cursor存放非空闲链表的第一个节点,而data域不使用;

空闲链表的头结点是数组中的第一个元素,即SL[0],它的cursor存放空闲链表的第一个节点,而data域我们存放非空闲链表的节点个数(其实书上没这么做,数组的首尾元素的data域均不使用,而我把SL[0].data存放非空闲节点的个数,SL[MAXSIZE-1].data不使用,其实后来才发现应该用SL[MAXSIZE-1].data放非空闲链表的节点数才更合适,但俺懒得该代码了),而非空闲链表的最后一个元素的cursor始终等于0,如下图:

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

初始化:

当我们定义了这样一个数组StaticLinkList SL[MAXSIZE]时,应把这个数组变成上面所述中的两个逻辑链表,只不过在初始化时,非空闲链表为空。

如下图:


对应代码如下:

Status InitList(StaticLinkList *SL)//初始化链表,此时链表全为空
{
	for (int i = 0; i < MAXSIZE - 1; i++)
		SL[i].cursor = i + 1;//形参一条空闲的备用链表
	SL[MAXSIZE - 1].cursor = 0;//目前链表为空,最后一个元素的cursor为0
	SL[0].data = 0; //用SL中下标为0的元素的数据域存放非空闲链表节点的个数
	return OK;
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

此时链表为空,我们要要向链表中插入一些数据,方便后面的实验,此时我们有两种初始化插入方法:头插法和尾插法,插入的data值随机生成,n为要插入节点的个数

头插:每次将节点插入到头结点之后,即新插入的节点总位于非空闲链表的第一个位置,即新节点总是正数第一

Status InitFrontInsert(StaticLinkList *SL, int n)//初始化链表,新节点均头插,放在头部(此函数仅限初始化使用)
{
	int k,j;
	k = MAXSIZE - 1;//记录头结点的下标
	if (SL[k].cursor == 0)//条件成立说明非空闲链表为空,可以初始化
	{
		time_t tms;
		srand((unsigned int)time(&tms));//设置随机数种子
	
		for (int i = 0; i < n; i++)
		{
			j = Malloc_SL(SL);
			SL[0].cursor = SL[j].cursor;//使SL[0].cursor永远指向空闲链表的第一个节点
			SL[j].cursor = SL[MAXSIZE-1].cursor;
			SL[MAXSIZE -1].cursor = j;
			SL[j].data = rand() % 100 + 1;//取值范围为1~100;
			SL[0].data++;//非空闲节点数增加
			k = SL[k].cursor;
		}
		return OK;
	}
	return FALSE;
}
尾插:每次将新节点插入到非空闲链表的最后一个节点之后,即新节点总是倒数第一

Status InitBackInsert(StaticLinkList *SL, int n)//初始化链表,新节点均尾插,放在尾部(此函数仅限初始化使用)
{
	int k,j;
	k = MAXSIZE - 1;//记录头结点的下标
	if (SL[k].cursor == 0)//条件成立说明非空闲链表为空,可以初始化
	{
		time_t tms;
		srand((unsigned int)time(&tms));//设置随机数种子
		j = Malloc_SL(SL);//先初始化一个节点,方便接下在循环中初始化
		SL[MAXSIZE - 1].cursor = j;
		SL[0].cursor = SL[j].cursor;//使用下标为零的,即空闲链表的头结点指向空闲链表的第一个节点
		SL[j].cursor = 0;
		SL[j].data = rand() % 100 + 1;
		SL[0].data++;
		k = SL[k].cursor;//用k记录非空闲链表的最后一个节点的下标
		for (int i = 1; i < n; i++)
		{
			j = Malloc_SL(SL);
			SL[0].cursor = SL[j].cursor;//
			SL[k].cursor = j;
			SL[j].data = rand() % 100 + 1;
			SL[j].cursor = 0;//最后一个节点的cursor始终指向下标为0的节点,也就是空闲链表的头结点
			SL[0].data++;
			k = j;
		}
		return OK;
	}
	return FALSE;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
获取非空闲链表节点个数,即非空闲链表长度:

int ListLength(StaticLinkList *SL)//求SL中有值节点的个数
{
	return SL[0].data;//SL[0].data中存储了有值节点的个数
	//如果不用SL[0].data存储有直接点个数,只能把整个非空闲链表全部撸一遍,代码如下
	/*
	int i = 0;//统计节点个数
	int j = SL[MAXSIZE-1].cursor;
	while(j)//当链表不为空时
	{
		j = SL[j].cursor;//挨个撸一遍
		i++;
	}
	return i;
	*/
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

我们需要一个负责打印非空闲链表的函数,以便我们直观的看到非空闲链表的状态和方便调试:

void PrintList(StaticLinkList *SL)//打印链表
{
	if (SL[MAXSIZE-1].cursor == 0)
	{
		printf("链表为空!\n");
		return;
	}
	
	int k = SL[MAXSIZE - 1].cursor;

	while (k != 0)
	{
		printf("此节点下标:%d,节点值为:%d,下一个节点下标为:%d\n", k, SL[k].data, SL[k].cursor);
		k = SL[k].cursor;
	}
	
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

当我们用指针实现链表时,C语言的库函数malloc动态分配新的节点,但我们这里使用的是数组模拟链表,所以要自己实现类似malloc的函数:

int Malloc_SL(StaticLinkList *SL)//分配备用链表中的元素
{
	//当前数组第一个元素的cursor的值,就是要返回的备用链表的第一个节点
	int i = SL[0].cursor;
	if (i == 0)//判断备用链表是否用尽,用尽时SL[0].cursor的值为0
		printf("备用链表已空!");
	else
	{
		SL[0].cursor = SL[i].cursor;//记录下一个备用节点的下标
		return i;
	}		
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

同理,我们也需要自己实现类似free的函数:

void Free_SL(StaticLinkList *SL, int k)//回收下标为k节点到备用链表
{
	//把要删除节点的cursor置为备用链表的第一个节点
	//需要记录原来第一个备用节点的下标,并把SL[0].cursor置为被删除节点
	//的下标,因为被删除的节点成了备用链表的第一个节点
	SL[k].cursor = SL[0].cursor;
	SL[0].cursor = k;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

获取非空闲链表中的第i个元素的data值:

ElemType GetValue(StaticLinkList *SL, int i)//获取非空闲链表中第i个节点的data值
{
	//判断i的值是否合法
	if (i > SL[0].data || i < 0)//SL[0].data存储了链表中的节点个数,即链表长度
	{
		printf("不存在第%d个元素,无法获取值!\n", i);
		return FALSE;
	}

	int k;
	k = MAXSIZE - 1;
	for (int m = 0; m < i; m++)
		k = SL[k].cursor;
	printf("查找到第%d个节点,此节点下标为:%d,data:%d,cursor:%d\n"
			, i, k, SL[k].data, SL[k].cursor);
	return SL[k].data;
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在非空闲链表的第i个节点前插入新节点:

Status ListFrontInsertIndex(StaticLinkList *SL, int i, ElemType e)//在第i个元素前插入新节点置为e
{
	//判断i的值是否合法
	if (i > SL[0].data || i < 0)//SL[0].data存储了链表中的节点个数,即链表长度
	{
		printf("不存在第%d个元素,无法插入!\n", i);
		return FALSE;
	}
	int j, k, l;
	k = MAXSIZE - 1;//k为最后一个元素的下标,也是非空闲链表的头结点
	j = Malloc_SL(SL);//获取空闲链表中的第一个节点
	SL[0].cursor = SL[j].cursor;//使SL[0].cursor永远指向空闲链表的第一个节点
	if (j != k)//当j等于k时意味着空闲链表已空
	{
		SL[j].data = e;
		for (l = 1; l <= i - 1; l++)//此处应细想在第1个节点前插入的情况
			k = SL[k].cursor;//找到第i个节点前一个节点的下标
		SL[j].cursor = SL[k].cursor;//用新节点的cursor记录第i-1个节点的cursor
		SL[k].cursor = j;//改变第i-1个节点的cursor,使其等于新分配节点的下标
		SL[j].data = e;
		SL[0].data++;
		return OK;
	}
	return FALSE;
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在非空闲链表中的第i个节点后插入新节点:

Status ListBackInsertIndex(StaticLinkList *SL, int i, ElemType e)//在第i个元素后插入新节点置为e
{
	//判断i的值是否合法
	if (i > SL[0].data || i < 0)//SL[0].data存储了链表中的节点个数,即链表长度
	{
		printf("不存在第%d个元素,无法插入!\n", i);
		return FALSE;
	}
	int j, k, l;
	k = MAXSIZE - 1;//k为最后一个元素的下标,也是非空闲链表的头结点
	j = Malloc_SL(SL);//获取空闲链表中的第一个节点
	SL[0].cursor = SL[j].cursor;//使SL[0].cursor永远指向空闲链表的第一个节点
	if (j != k)//当j等于k时意味着空闲链表已空
	{
		SL[j].data = e;
		for (l = 1; l <= i; l++)//此处应细想在第1个节点前插入的情况
			k = SL[k].cursor;//找到第i个节点前一个节点的下标
		SL[j].cursor = SL[k].cursor;//用新节点的cursor记录第i个节点的cursor
		SL[k].cursor = j;//改变第i个节点的cursor,使其等于新分配节点的下标
		SL[j].data = e;
		SL[0].data++;
		return OK;
	}
	return FALSE;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在非空闲链表中寻找data值为x的节点,并在次节点前插入新节点:

Status ListFrontInsertValue(StaticLinkList *SL, ElemType x, ElemType e)//寻找第一个节点值为x的节点,在其前插入新节点,值为e
{
	int j, k, l,m;
	k = MAXSIZE - 1;
	for (j = 0; j < SL[0].data; j++)
	{
		l = SL[k].cursor;//临时记录下一个节点的cursor
		if (SL[l].data == x)
		{
			m = Malloc_SL(SL);
			SL[0].cursor = SL[m].cursor;//使SL[0].cursor永远指向空闲链表的第一个节点
			SL[m].cursor = l;//用新节点的cursor记录值为x的节点的前一个节点的cursor,也就是值为x节点的下标
			SL[k].cursor = m;//重置值为x的节点的前一个节点的cursor,使其值为新节点的下标
			SL[m].data = e;
			SL[0].data++;
			break;
			return OK;//结束循环,返回
		}
		k = SL[k].cursor;//继续向下撸一个节点
	}
	if (j == SL[0].data)//所有非空闲链表都找完,也没找到
	{
		printf("没有找到值为%d的节点!\n", x);
		return FALSE;
	}
		
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在非空闲链表中寻找data值为x的节点,并在此节点后插入新节点:

Status ListBackInsertValue(StaticLinkList *SL, ElemType x, ElemType e)//寻找第一个节点值为x的节点,在其后插入新节点,值为e
{
	int i, j, k;
	k = MAXSIZE - 1;
	for (i = 0; i < SL[0].data; i++)
	{
		k = SL[k].cursor;
		if (SL[k].data == x)
		{
			j = Malloc_SL(SL);
			SL[0].cursor = SL[j].cursor;
			SL[j].cursor =SL[k].cursor;//新节点的cursor置为值为x的节点的cusor
			SL[k].cursor = j;//重置值为x节点的cursor,使其为新节点的下标
			SL[j].data = e;
			SL[0].data++;
			return OK;
		}
	}
	if (i == SL[0].data)
	{
		printf("没有找到值为%d的节点!\n", x);
		return FALSE;
	}
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

删除非空闲链表中的第i个节点:

Status ListDeleteIndex(StaticLinkList *SL, int i)//删除第i个节点
{
	//判断i的值是否合法
	if (i > SL[0].data || i < 0)//SL[0].data存储了链表中的节点个数,即链表长度
	{
		printf("不存在第%d个元素,无法插入!\n", i);
		return FALSE;
	}

	int j, k,l;
	k = MAXSIZE - 1;
	for (l = 1; l < i; l++)
		k = SL[k].cursor;//得到要删除节点的前一个节点的下标
	j = SL[k].cursor;//记录要删除节点的下标
	SL[k].cursor = SL[j].cursor;//重置前一个节点的cursor使其指向要删除节点的下一个节点
	SL[0].data--;
	Free_SL(SL,j);//释放要删除节点,使其进入空闲链表
	return OK;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

删除非空闲链表中第一个data值为e的节点:

Status ListDeleteValue(StaticLinkList *SL, ElemType e)//删除第一个值为e的节点
{
	int i, k,l;
	k = MAXSIZE - 1;
	for (l = 0; l < SL[0].data; l++)
	{
		i = SL[k].cursor;//记录下一个节点的下标
		if (SL[i].data == e)
		{
			SL[k].cursor = SL[i].cursor;//重置值为e的节点的前一个节点的cursor,使其值为找到的节点的cursor
			SL[0].data--;
			Free_SL(SL, i);
			return OK;
		}
		k = SL[k].cursor;//向下一个节点撸
	}
		printf("没有找到值为%d的节点!\n", e);
		return FALSE;
}
//n为要初始化的节点个数
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

最后也就是主函数了,下面注释的代码均为测试单个函数所用:(代码均在vs2013中编写,运行)

int main()
{
	StaticLinkList SL[MAXSIZE];
	InitList(SL);
	InitBackInsert(SL, 5);
	PrintList(SL);
	/*InitFrontInsert(SL, 5);*/
	/*printf("非空闲链表节点个数:%d\n", ListLength(SL));*/
	/*ListFrontInsertIndex(SL, 1, 11);
	ListFrontInsertIndex(SL, 6, 66);*/
	/*ListBackInsertIndex(SL, 1, 11);
	ListBackInsertIndex(SL, 6, 66);*/
	/*GetValue(SL, 3);
	GetValue(SL, 1);
	GetValue(SL, 5);*/
	/*PrintList(SL);
	ListFrontInsertValue(SL, GetValue(SL, 3),33);
	PrintList(SL);
	ListFrontInsertValue(SL, GetValue(SL, 1), 11);
	PrintList(SL);
	ListFrontInsertValue(SL, GetValue(SL, 7), 77);
	ListFrontInsertValue(SL, 101, 101);
	PrintList(SL);*/
	/*ListBackInsertValue(SL, GetValue(SL, 1), 11);
	PrintList(SL);
	ListBackInsertValue(SL, GetValue(SL, 3), 33);
	PrintList(SL);
	ListBackInsertValue(SL, GetValue(SL, 7), 77);
	PrintList(SL);*/
	/*ListDeleteIndex(SL, 1);
	printf("\n");
	PrintList(SL);
	ListDeleteIndex(SL, 4);
	printf("\n");
	PrintList(SL);*/
	ListDeleteValue(SL, GetValue(SL, 1));
	PrintList(SL);
	ListDeleteValue(SL, GetValue(SL, 4));
	PrintList(SL);
	ListDeleteValue(SL, 101);
	

	system("pause");

	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值