顺序表的实现(详解版)

文章详细介绍了动态顺序表的概念、分类,包括静态和动态顺序表,并重点讲解了动态顺序表的实现,如初始化、增容、打印、删除、插入和查找等操作。同时,文中提到了代码实现的关键点和注意事项,如内存管理与数据挪移。
摘要由CSDN通过智能技术生成

在这里插入图片描述

概念以及分类

顺序表:是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为

  1. 静态顺序表:使用定长数组存储。
  2. 动态顺序表:使用动态开辟的数组存储。

静态顺序表

#define N 100
typedef int SLDataType;
typedef struct SeqList
{
 SLDataType array[N]; // 定长数组
 size_t size; // 有效数据的个数 
}SeqList; 

动态顺序表(重点掌握)

简单介绍(主体+接口函数)

#pragma once  // 防止被重复包含
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLDateType;

typedef struct SeqList
{
	SLDateType* a;
	int size;   //存储的有效数据
	int capacity; //  存储数据的总容量
}SL;
   //接口函数
void SeqListInit(SL* ps); //初始化
void SeqListPrint(SL* ps); //顺序表的打印
void SeqListnewcapacity(SL* ps); //检查是否需要增容
void SeqListDestory(SL* ps); // 清除数据

void SeqListPopBack(SL* ps); // 尾插
void SeqListPushBack(SL* ps, SLDateType x); // 尾删

void SeqlistPushFront(SL * ps, SLDateType x); // 头插
void SeqListPopFront(SL* ps, SLDateType x); //头删

void SeqLisFind(SL* ps, SLDateType x); //查找元素
void SeqListInsert(SL* ps, int pos, SLDateType x); //任意位置插入
void SeqListErase(SL* ps, int pos, SLDateType x); //任意位置删除

首选需要说明的,这接口函数的命名风格是跟着STL走的,这样命名有助于C++的学习。

顺序表的主体由三部分组成
1.插入的数据
2.插入数据的容量
3.该顺序表的最大容量

注意:
1.这里使用了typedef int SLDateType;,是为了后面方便对插入的数据类型进行修改
2.typedef struct SeqList ,是为了后面 用SL代替, 表示比较方便

初始化函数(SeqListInit(SL * ps))

void SeqListInit(SL* ps)  //初始化
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}

初始化步骤很简单,就让数据容量都为0, 而传入的值都为NULL。

打印函数(void SeqListPrint(SL* ps))

void SeqListPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d\n", ps->a[i]);
	}
}

这也很简单, 就用有个for循环, 挨着打印顺序表中的每个。

是否增容函数(SeqListnewcapacity(SL* ps))

void SeqListnewcapacity(SL* ps)
{
	if (ps->size == ps->capacity)       //扩容或者是开辟空间
	{
		int newcapacity = ps->size == 0 ? 4 : ps->capacity * 2;

		SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType)); //realloc(a, b) 若a = NULL,相当于开辟空间
		//  a != NULL 扩容
/*if (tmp == NULL)
{

	printf("开辟空间失败");
	exit(-1);
}*/
		assert(tmp != NULL);
		ps->a = tmp;
		ps->capacity = newcapacity;
	}

判断是否需要增容的条件是:
ps->size == ps->capacity
实际用的容量==顺序表最大容量
代表着顺序表已经没有多余读的空间,来存储下一个数据了
这里分两种情况:
1.如果ps->size = ps->capacity == 0;,那么让它等于4
2.如果ps->size = ps->capacity != 0;, 那么就 让它的最大容量加倍(当然这里你可以自己随便设置)
这两种情况的实现,这里使用了三目运算符
情况2:
这里使用了realloc函数,来进行增容。
realloc(a, b) 若a = NULL,相当于开辟空间
a != NULL 扩容

注意:
1.newcapacity * sizeof(SLDateType),需要扩容的空间大小 = 数据的个数 * 它的数据类型
2.开辟空间,自然要考虑到开辟空间失败的情况

清楚数据函数(SeqListDestory(SL* ps))

void SeqListDestory(SL* ps)
{
	free(ps->a);
	ps->a = NULL;

	ps->capacity = ps->size == 0;

这里主要就使用free函数,对内存进行释放
ps->a = NULL;
ps->capacity = ps->size == 0;

尾插函数(SeqListPopBack(SL* ps))

void SeqListPushBack(SL* ps, SLDateType x)
{
	SeqListnewcapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;

尾插:就是在顺序表最后一个元素的后面插入一个元素

第一步:检查是否要增容
第二步:在顺序表后面插入元素 ,ps->size 表示此时顺序表中所存储的个数, ps->size - 1是顺序表中最后一个元素(数组下标), ps->size 自然就是最后一个元素的后面
第三步:插入了一个元素之后,自然就要让顺序表中存储的元素个数+1, ps->size + 1;

尾删函数(SeqListPushBack(SL* ps, SLDateType x); )

void  SeqListPopBack(SL* ps)
{
	/*if (ps->a > 0)
	{
		ps->size--;
	}*/

	assert(ps->a > 0);
	ps->size--;

}

尾删:从数组的最后一个元素开始删除

删除的前提自然是 ps->szie > 0;
这里有2种解决方法
1:温柔的方式 ;用if语句来判断
2:粗暴的方式:用assert(),直接断言,如果不正确,直接就终止程序
注意要使用头文件(#inlucde<assert.h>)

删除方式:
通过让存贮的数据总数减少(ps->size–),从而达到从数组的最后一个元素开始删除的效果.

头插 (SeqlistPushFront(SL * ps, SLDateType x))

void SeqlistPushFront(SL* ps, SLDateType x)
{
	SeqListnewcapacity(ps);
	int end = ps->size - 1;

	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}

	ps->a[0] = x;
	ps->size++;
}

头插:在数组第一个元素的前面插入一个数据
主要思想
前面插入一个数据,这里就涉及到一个挪移数据的过程,
也就是新插入的数据,就是顺序表数组中的第一个元素。, 那么就要挪出一个位置, 原来数组中每个元素向后面挪一位, 自然便能把数组的第一个位置空出来。(通过赋值操作,把元素的值赋值给下一位)
第一步:检查是否需要增容
第二步:插入数据并且开始挪移数据
end为数组元素的最后一位
这里让ps->a[end + 1] = ps->a[end];, 通过赋值操作,把元素的值赋值给下一位,然后end–, 继续挪动数据,这里自然是一个循环,while(end >= 0);
第三步::插入了一个元素之后,自然就要让顺序表中存储的元素个数+1, ps->size + 1;

头删函数(SeqListPopFront(SL* ps, SLDateType x); )

void SeqListPopFront(SL* ps, SLDateType x)
{
	assert(ps->size > 0);

	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

头删:从数组的第一个元素开始删除
思想:(采用赋值删除的思想
第二个值赋给第一个值, 第三个个值赋给第二个值,这样第一个元素自然就被消掉了,结果自然是每个元素向前移一位,也是通过赋值的操作
(挪移数据跟头插相似, 只是挪移的方向不一样)

第一步:删除元素,肯定要考虑越界问题 ps->size > 0
assert(ps->size > 0);

第二步:
beign 代表数组第一个元素的下标
通过赋值操作挪移数据,第二个值赋给第一个值, 第三个个值赋给第二个值,ps->a[begin - 1] = ps->a[begin];
begin++,挪动下一组数据
循环条件:while (begin < ps->size)
第三步:
删除了一个元素之后,自然就要让顺序表中存储的元素个数-1, ps->size - 1;

查找任意位置的函数( SeqLisFind(SL* ps, SLDateType x); )

void SeqLisFind(SL* ps, SLDateType x)
{
	for(int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
		return -1;
	}
}

查找元素,其实很简单
自然,
就是通过for循环和if语句, 与顺序表中的每个数据进行比较
找到的话, 就返回就这个数组的元素位置的下标

插入任意位置的函数(SeqListInsert(SL* ps, int pos, SLDateType x); )

oid SeqListInsert(SL* ps, int pos, SLDateType x)
{

	assert(pos >= 0 && pos <= ps->size);

	int end = ps->size - 1;

	while (pos <= ps->size)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	
	ps->a[pos] = x;
	ps->size++;
}

第一步:自然要进行一个判断, 插入的位置肯定在这个数组中,不能够造成越界的非法操作,这里我还是用的assert函数来进行处理,当然你如果用if语句的话也是可以的。

第二步:里面插入了一个数据,自然要向后面挪移一个数据来腾出空间
end代表数组的最后一个元素

在pos位置之后的元素,都向后挪移一位
循环条件自然是:pos <= ps->size
while (pos <= ps->size)
{
ps->a[end + 1] = ps->a[end];
end–;
}
第三步:腾出的位置,赋值给插入的数据ps->a[pos] = x;

第四步:插入了一个元素之后,自然就要让顺序表中存储的元素个数+1, ps->size + 1;

运用:
如果pos = 0, 那么其实这就是头插
如果pos = ps->size, 那么这也就是尾插

任意位置删除的函数(SeqListErase(SL* ps, int pos, SLDateType x); )

void SeqListErase(SL* ps, int pos, SLDateType x)
{
	assert(pos >= 0 && pos < ps->size);

	int begin = pos + 1;

	while (begin <= ps->size)
	{
		ps->a[begin - 1] = ps->a[begin + 1];
		begin++;
	}

	ps->size--;
}

第一步:
自然要进行一个判断, 删除的位置肯定在这个数组中,不能够造成越界的非法操作,这里我还是用的assert函数来进行处理,当然你如果用if语句的话也是可以的。

第二步:挪移数据,pos之后的元素向前挪移一个数据,通过赋值删除的方法。方法跟思路也是跟之前一样的。
while (begin <= ps->size)
{
ps->a[begin - 1] = ps->a[begin + 1];
begin++;
}

第三步:删除了一个元素之后,自然就要让顺序表中存储的元素个数-1, ps->size - 1;

运用:
1.同样的如果pos = 0,那么这就是头删
2,如果pos = ps->size - 1,那么这就是尾删

小总结

1.删除是向前挪移数据
2.插入式向后挪移数据
3.挪移数据的目的,是为了让数据在内存存储中是连续的

希望这能够对你们有所帮助,你们的关注与支持就是对我最大的鼓励
在这里插入图片描述

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温柔了岁月.c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值