【数据结构初阶】2. 顺序表

1. 线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。
线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就是说是连续的一条直线。
但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组链式结构的形式存储。
在这里插入图片描述

2. 顺序表

2.1 概念及结构

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

  1. 静态顺序表:使用定长数组存储元素(一般不采用,固定长度也就意味着空间浪费或不足)
    在这里插入图片描述
  2. 动态顺序表:使用动态开辟的数组存储
    在这里插入图片描述

3. 顺序表的代码实现(重点)

静态顺序表只适用于确定知道需要存多少数据的场景。
静态顺序表的定长数组长度固定,空间开多了浪费,开少了不够用。
所以现实中基本都是使用动态顺序表,根据需要来动态的分配空间大小
在这里插入图片描述

3.1 创建顺序表(结构体)

#include <stdio.h>
typedef int DataType;
// 顺序表
typedef struct SeqList
{
	// 整型数据
	DataType* a;
	// 数据个数
	int size;
	// 顺序表空间大小
	int capacity;
}SL;

3.2 初始化顺序表 SLInit

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = NULL;
	psl->capacity = 0;
	psl->size = 0;
}

3.3 检查容量大小 CheckCapacity

若容量不够则进行扩容,若容量为0则进行初始化

void CheckCapacity(SL* psl)
{
	assert(psl);
	if (psl->size == psl->capacity)
	{
		// 进行扩容或者初始化
		// 新容量 若capcity为0 则初始化为4 ; 若不为零 扩容到2倍
		int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		DataType* tmp =(DataType*)realloc(psl->a,newCapacity * sizeof(DataType));
		// 判断扩容是否成功
		if (tmp == NULL)
		{
			// 扩容失败
			perror("CheckCapaity");
			return;
			// eixt(-1);
		}
		// 将扩容后的空间重新由psl->a维护
		psl->a = tmp;
		// 容量改为新容量
		psl->capacity = newCapacity;
	}
}

3.4 尾插尾删接口 SLPushBack / SLPopBack

// 尾插
void SLPushBack(SL* psl,DataType x)
{
	assert(psl);
	CheckCapacity(psl);
	// 在下标为size的位置插入元素
	psl->a[psl->size] = x;
	psl->size++;
}
// 尾删
void SLPopBack(SL* psl)
{
	assert(psl);
	// 个数直接减减 ,顺序表是通过size来访问元素的
	psl->size--;
}

3.5 头插头删接口 SLPushFront / SLPopFront

// 头插
void SLPushFront(SL* psl,DataType x)
{
	assert(psl);
	int end = 0;
	// 将所有元素向后移动一位
	for (end = psl->size; end > 0; end--)
	{
		psl->a[end] = psl->a[end - 1];
	}
	// 头部插入x
	psl->a[0] = x;
	// size++
	psl->size++;
}
// 头删
void SLPopFront(SL* psl)
{
	assert(psl);
	int begin = 0;
	for (begin = 0; begin <= psl->size - 2; begin++)
	{
		// 将后面的元素各自往前覆盖即可 从前往后
		psl->a[begin] = psl->a[begin + 1];
	}
	// size 元素个数减1
	psl->size--;
}

3.7 测试上述接口 TestSeqList1

在学习数据结构的过程中,要学会边编译边调试,尽早发现问题
在这里插入图片描述
上述接口成功实现

3.8 打印元素 SLPrint

// 打印
void SLPrint(SL* psl)
{
	assert(psl);
	int begin = 0;
	// 一个for循环遍历数组即可
	for (begin = 0; begin < psl->size; begin++)
	{
		printf("%d ", psl->a[begin]);
	}
	printf("\n");
}

3.9 任意位置插入/删除接口 SLInsert / SLErase

// 顺序表在pos位置插入x
void SLInsert(SL* psl, size_t pos, DataType x)
{
	assert(psl);
	assert(pos <= psl->size);
	// 初始化顺序表大小或扩容
	CheckCapacity(psl);
	int end =psl->size;
	// 从pos开始往后移动 从后往前
	for (end; end > pos; end--)
	{
		psl->a[end] = psl->a[end-1];
	}
	// pos处插入x
	psl->a[pos] = x;
	psl->size++;
}

// 顺序表删除pos位置的值
void SLErase(SL* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);
	size_t k = pos;
	// 从pos开始往前覆盖 从前往后
	for (k; k < psl->size; k++)
	{
		psl->a[k] = psl->a[k + 1];
	}
	psl->size--;
}

3.10 测试上述接口 TestSeqList2

在这里插入图片描述

3.11 一些细节上的问题

在这里插入图片描述

3.12 优化 头插/尾插/头删/尾删 接口

在这之前,我们已经实现了在任意位置处实现插入/删除操作
那么头插相当于在下标为0的位置插入、尾插相当于在下标为size位置处插入
头删、尾删也同理
在这里插入图片描述
浅浅测试一下:
在这里插入图片描述
跟之前测试结果一样!那么到这里,顺序表的代码实现部分就结束啦 (●’◡’●)

4. 顺序表相关OJ题

4.1 移除元素

在这里插入图片描述
在这里插入图片描述
这时候可以采用双指针遍历同一个数组的策略
在这里插入图片描述

int removeElement(int* nums, int numsSize, int val){
    int src = 0,dst = 0;
    while(src < numsSize)
    {
        if(nums[src] == val)
        {
            src++;
        }
        else
        {
            nums[dst]=nums[src];
            det++;
            src++;
        }
    }
    return dst;
}

4.2 删除重复元素

在这里插入图片描述
同样采用的也是双指针遍历同一个数组的策略
大体思路一致,只是去重 也就意味着相同往后遍历,不同覆盖
在这里插入图片描述

int removeDuplicates(int* nums, int numsSize){
    int src = 0,dst = 0;
    while(src<numsSize)
    {
        if(nums[src] == nums[dst])
        {
            src++;
        }
        else
        {
            //在dst+1的位置赋值src
            nums[dst+1]=nums[src];
            src++;
            dst++;
        }
    }
    return dst+1;
}

在这里插入图片描述

4.3 合并有序序列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5. 顺序表的缺陷

缺陷:

  1. 中间/头部的插入/删除(需要移动后面的数据),时间复杂度为O(N)
  2. 增容可能需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?可以采用链表的形式来解决

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值