顺序表实现

顺序表介绍

顺序表是用来连续存放数据单元的一种结构,逻辑连续的两个元素在实际上也为连续的。

顺序表的实现

顺序标定义

由于我们存储多中数据,所以顺序表的定义使用结构体类型。

由于是连续的数据单元,所以我们使用数组来接收顺序表中的数据,size用来表示数据表中存放了多少个数组,capicity用来表示顺序表的容量。

typedef int Seqdata;
typedef struct SeqList
{
	Seqdata *data;
	int size;
	int capicity;
}SL;

顺序表功能的实现

顺序表的功能一定有最基础的增删查改,所以接下来就来实现这些功能。

顺序表初始化

顺序表的初始化,首先断言防止传空指针,然后就需要进行开辟空间,开辟的大小由自己来定,判断是否会开辟失败,如果成功将其capicity赋值开辟的大小,并将size初始化为0,data赋值开辟的空间地址。

void SeqInit(SL* ps)
{
	assert(ps);
	Seqdata* tmp = (Seqdata*)malloc(sizeof(Seqdata)*4);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	ps->capicity = 4;
	ps->size = 0;
	ps->data = tmp;
}
顺序表的扩容

我们在使用顺序表的时候,进行增的操作时,我们就需要检查空间是否已经满了,既然我们需要经常进行这个操作,所以我们将其封装为一个函数。

如果capicity和size相同时,那么就说明空间已经满了,需要进行增容,增容使用realloc函数,增容我们任然需要判断是否增容成功,增容成功后我们需要将capicity重新修改为增容后的大小,由于realloc的特性,如果原地址不够增容,会重新在新的地址进行操作,所以以防万一我们将data赋值扩容的地址。

void check(SL* ps)
{
	assert(ps);
	if (ps->capicity == ps->size)
	{
		Seqdata* tmp = realloc(ps->data, ps->capicity * sizeof(Seqdata) * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->capicity *= 2;
		ps->data = tmp;
	}
	return;
}
顺序表的尾增

顺序表的尾增,顾名思义就是在数组为尾部后进行插入,我们需要首先检查空间是否已满,直接将size当作下标赋值,最后记得size++。

void Pushtail(SL* ps, Seqdata data)
{
	check(ps);
	ps->data[ps->size] = data;
	ps->size++;
}
顺序表的尾删

顺序表的尾删,我们需要注意的是我们需要多判断一下顺序表中是否为有数据,如果没有就无法进行删除,当size-1是正好为数组最后的下标,将其赋值为0即可,最后将size--完成操作。

void Poptail(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	ps->data[ps->size - 1] = 0;
	ps->size--;
}
顺序表的头增

顺序表实现头增,我们需要判断两种情况,一种为顺序表没有数据,这时候就可以直接将数组第一个元素直接赋值,如果是有元素的情况下,就需要将数据进行搬运从而将数据放入头部。

我们如果搬数据我们有两种方式,一种从前向后遍历,另外一种从后向前遍历。

我们可以试想一下如果从前向后遍历的化,第二个存放第一个的数据,这时第二个的数据已经被修改了,再往后数据就一直为第一个的数据,所以从前向后无法实现头插。

所以使用头插我们需要使用另外一种遍历方式,从后向前。

void Pushhead(SL* ps, Seqdata data)
{
	check(ps);
	if (ps->size == 0)
	{
		ps->data[0] = data;
	}
	else
	{
		int i = 0;
		for (i = ps->size; i >0 ; i--)
		{
			ps->data[i] = ps->data[i - 1];
		}
		ps->data[0] = data;
	}
	ps->size++;
}
顺序表的头删

顺序表的头删既然是删除所以一定要判断顺序表是否为空,顺序表中只有一个数据时,直接将其赋值为0,如果有多个就和头插类似需要从前向后遍历或从后向前遍历。

如果时从后向前,如果是这样的写法就会是数据一直被最后数据覆盖,所以很明显是不行的。

如果是从前向后的化,就可以成功实现。

void Pophead(SL* ps)
{
	assert(ps);
	assert(ps->size>0);
	if (ps->size == 1)
	{
		ps->data[0] = 0;
	}
	else
	{
		int i = 0;
		for (i = 0; i < ps->size-1; i++)
		{
			ps->data[i] = ps->data[i + 1];
		}
	}
	ps->size--;
}
顺序表的销毁

由于我们开辟了空间,那么我们就一定需要进行释放,将空间还给操作系统。

void DestroySeq(SL* ps)
{
	assert(ps);
	free(ps->data);
	ps->data = NULL;
	ps->capicity = 0;
	ps->size = 0;
}
顺序表的查找

我们如果要查找顺序表的数据的话,可以通过size遍历整个数组,如果找到就返回下标,如果没有找到就返回-1。

int Finddata(SL* ps, Seqdata data)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == data)
		{
			return i;
		}
	}
	return -1;
}
顺序表的下标插入

执行插入操作我们需要输入插入的下标,插入的下标我们可以断言一下,插入的下标不能为负数也不能超过最后一个数据的下标,分情况讨论,如果没有数据的话且输入下标为0就直接给第一个元素赋值,其他的我们可以模仿头插的方式来搬运数据,结束条件为大于传来的下标。

void SLInsert(SL* ps, int pos, Seqdata data)
{
	assert(pos >= 0&&pos<=ps->size);
	check(ps);
	if (ps->size == 0)
	{
		ps->data[0] = data;
	}
	else
	{
		int i = 0;
		for (i = ps->size; i > pos; i++)
		{
			ps->data[i] = ps->data[i - 1];
		}
		ps->data[pos] = data;
	}
	ps->size++;
}

顺序表的下标删除

顺序表要进行下标删除时,因为是删除,所以一定要判断是否有数据,分情况讨论,如果只有一个直接将其赋值为0即可,如果要删掉的下标为最后一个的话,可以直接将最后一个数组元素置为0,剩下的进行删除时模仿头删的方式来搬运数据,从而实现删除。

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	int end = ps->size - 1;
	int i = 0;
	if (ps->size == 1)
	{
		ps->data[0] = 0;
	}
	else if (end == pos)
	{
		ps->data[end] = 0;
	}
	else
	{
		for (i = pos; i < end; i++)
		{
			ps->data[i] = ps->data[i + 1];
		}
	}
	ps->size--;
}
顺序表的打印

顺序表打印我们可以使用遍历的方式使其依次打印,同样也要判断顺序表是否为空。

void SLpint(SL* ps)
{
    assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
}

结语

顺序表在编写时要注意申请的空间及时释放,并且要注意传入的数据是否可行,如果不行会造成越界等问题记得在开始就将其断言,希望我的这篇文章对你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值