【数据结构-顺序表详解】

1. 顺序表的概念及结构

1.1概念

顺序表是一种线性表,其特点是将逻辑上相邻的元素存储在物理位置上相邻的存储单元中。顺序表的存储结构是连续的存储空间,这使得顺序表具有随机访问的特性,即可以通过元素的序号直接访问对应的存储地址

1.1.1线性表

在逻辑上是线性结构(连续的一条直线),但是在物理结构上并不一定连续(可能像链表一样,地址并不相邻)。

1.2结构

顺序表的底层是数组,实际上是对数组的封装,同时需要记录顺序表中的元素个数。元素个数的类型是int,两个不同类型的数组可以用结构体存储。

所以顺序表实际上就是一个包含了数组和记录元素个数的结构体,同时实现了增删查改等接口。

1.3与数组的区别

用一个比喻来区分,一道油菜在普通饭店里面的名字可能就是油菜,而在米其林餐厅里面的名字就可能是绿野仙踪。普通饭店就相当于数组,米其林餐厅就好比顺序表。

2.顺序表分类

2.1静态顺序表

struct SeqList
{
    int arr[100];//定长数组
    int size;    //数据个数
};

即数组是定长的,存在缺陷。
不知道需要多少空间,空间给少了不够用,给多了造成较多浪费。

2.2动态顺序表

struct SL
{
    SLDataType* arr;//动态数组
    int size;//有效数据个数
    int capacity;//数组的容量
};

可以根据需要增加空间,对空间的浪费较少,所以应用较多。

3.顺序表的实现

3.1初始化

首先定义结构体

//顺序表的类型不止有int 用typedef便于后续修改
typedef int SLDataType;

//定义顺序表的结构
typedef struct SL
{
    SLDataType* arr;//动态数组
    int size;//有效数据个数
    int capacity;//数组的容量
}SL;//用typedef对数组进行重命名

要注意 这里用SLDataType重命名int类型,是因为顺序表的类型不只有int一种,代码多了改起来很麻烦。

初始化

void SLInit(SL* sl)
{
	sl->arr = NULL;
	sl->size = 0;
	sl->capacity = 0;
}

3.2插入数据

3.2.1尾插

在这里插入图片描述
首先有一个顺序表1,2,3,4,5 要将6插入尾部

初始有五个元素,所以size的值为5

刚好将6插入arr[5]的地方 之后size再加一

void SLPushBack(SL* sl, SLDataType x)
{

	sl->arr[sl->size++] = x;
}

这里会有一个问题 如果没有剩余空间了怎么办?所以先要判断空间够不够用
(头插也需要判读空间 所以我把重复的代码封装成一个函数)

如果数组的容量与有效数据个数相同,说明数组空间满了

如果满了,用realloc函数对数组进行扩容。那么问题来了,扩容到多少呢?
如果一个空间一个空间扩容确实不会有空间剩余,但是会降低效率,所以每次我们扩容到两倍。

void SLCheckCapacity(SL* sl)
{
	if (sl->size == sl->capacity)//有效数据和空间大小一样 表示被占满
	{
		//扩容
		int newCapacity = (sl->capacity == 0) ? 4 : 2 * sl->capacity;
		SLDataType* tmp = (SLDataType*)realloc(sl->arr, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		sl->arr = tmp;
		sl->capacity = newCapacity;
	}
}

注意初始空间为0时直接给4个。

3.2.2头插

在这里插入图片描述
这时需要把所有的元素向后移一位。

如果从前往后移动,那么元素会被覆盖,无法正确实现(试一下就OK了)。
所以从数组的最后开始移动,最后再给sl->arr[0]赋值成要插入的元素就可以了。

void SLPushFront(SL* sl, SLDataType x)
{
	assert(sl);

	//先判断空间是否足够
	SLCheckCapacity(sl);
	for (int i = sl->size; i > 0; i--)
	{
		sl->arr[i] = sl->arr[i - 1];
	}
	sl->arr[0] = x;
	sl->size++;
}

3.2.3 指定位置之前插入

即需要将从指定位置开始,之后的数据,进行一个头插,方法和头插一样不再赘述。

void SLInsert(SL* sl, int pos, SLDataType x)
{
	assert(sl);

	//先判断空间是否足够
	SLCheckCapacity(sl);

	for (int i = sl->size; i > pos; i--)
	{
		sl->arr[i] = sl->arr[i - 1];
	}
	sl->arr[pos] = x;
	sl->size++;
}

3.3 删除数据

3.3.1 尾删

遍历数组时,只在意有效数据,size减一就相当于把最后一个元素覆盖了。

void SLPopBack(SL* sl)
{
	assert(sl);
	sl->size--;
}

3.3.2 头删

与头插相同 ,需要把第二个以后的元素向前移动一位。
这时候就需要从头开始移动了。不要忘了size减一。

void SLPopFront(SL* sl)
{
	assert(sl&&sl->size);
	for (int i = 0; i < (sl->size - 1); i++)
	{
		sl->arr[i] = sl->arr[i + 1];
	}
	sl->size--;
}

3.3.3 删除指定位置的元素

将指定位置之后的数据全部向前移动一位就可以了。

void SLFrase(SL* sl, int pos)
{
	assert(sl);

	for (int i = pos; i < sl->size - 1; i++)
	{
		sl->arr[i] = sl->arr[i + 1];
	}
	sl->size--;
}

3.4 查找数据

int SLFind(SL sl, SLDataType val)
{
	for(int i=0;i<sl.size;i++)
	{ 
		if (val == sl.arr[i])
			return i;
	}
	return -1;
}

3.5 销毁顺序表

void SLDestroy(SL* sl)
{
	if (sl->arr)
		free(sl->arr);
	sl->arr = NULL;
	sl->size = 0;
	sl->capacity = 0;
}

4. 完成

到这里就完成啦,第一次写还是很不熟悉,欢迎大家指出不足呀。有哪里不懂的欢迎评论区提问哦!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值