[数据结构]顺序表(含源码超详解)

线性表的定义

了解顺序表之前 我们先了解一下线性表

线性表在逻辑上是线性结构,也可以说是连续的一条直线.但是在物理结构上并不一定是连续的

线性结构在物理上存储时,通常以数组或者是链式结构的形式存储.

线性表是具有相同数据类型的n个数据元素的有限序列,n为表长,n=0时,线性表为空

线性表除首元素外,其他元素有且只有一个直接前驱。线性表除尾元素外,其他元素有且只有一个直接后继

顺序表是线性表的一种

顺序表基本概念

顺序表的定义

线性表的顺序存储又称顺序表。用一组地址连续的存储的单元依次存储线性表中的数据元素。逻辑上相邻的两个元素在物理位置也相邻

顺序表存储

线性表指的是一种逻辑结构,表示元素之间存在一对一的相邻关系

顺序表和链表是指存储结构

顺序表存储可以分为静态存储动态存储

静态存储:使用定长数组存储元素,一旦空间沾满,在加入新数据会溢出

cpp 复制代码
#define N 100
typedef int SLDateType;

//静态顺序表
typedef struct SeqList
{
	SLDateType a[N];
	int size;
}SL;

动态存储:使用动态开辟的数组存储,如果空间占满,可以另外开辟一个更大的空间 替换它

cpp 复制代码
typedef int SLDateType;

typedef struct SeqList
{
	SLDateType* arr; //存储数据的底层结构
	int capacity; //存储顺序表的空间大小
	int size;//顺序表内元素个数
}SL;

顺序表基本实现

顺序表结构

cpp 复制代码
typedef int SLDateType;

typedef struct SeqList
{
	SLDateType* arr; //存储数据的底层结构
	int capacity; //存储顺序表的空间大小
	int size;//顺序表内元素个数
}SL;

顺序表的初始化

cpp 复制代码
//初始化顺序表
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->capacity = 0;
	ps->size = 0;
}

顺序表的打印

cpp 复制代码
//顺序表打印
void SLPrint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
}

判断顺序表是否满了 扩容

cpp 复制代码
void CheckCapacity(SL* ps)
{
	assert(ps != NULL);

	int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
	SLDateType* tmp = (SLDateType*)realloc(ps->arr, newcapacity * sizeof(SLDateType));
	if (tmp == NULL)
	{
		perror("reallocc fail\n");//如果没用扩容成功会报错
		exit(-1);
	}
	ps->arr = tmp;//扩容成功传给arr
	ps->capacity = newcapacity;

}

顺序表的头插

cpp 复制代码
//头插
void SLPushFront(SL* ps,SLDateType x)
{
	assert(ps != NULL);
    //判断顺序表是否满了
	if (ps->capacity == ps->size)
	{
		CheckCapacity(ps);
	}
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}

顺序表的尾插

cpp 复制代码
//顺序表尾插
void SLPushBack(SL* ps,SLDateType x)
{
	assert(ps != NULL);

	if (ps->capacity == ps->size)
	{
		CheckCapacity(ps);
	}
	ps->arr[ps->size] = x;
	ps->size++;
}

顺序表的头删

cpp 复制代码
//尾删
void SLPopBack(SL* ps)
{
	assert(ps != NULL);
	assert(ps->size != 0);
	ps->size--;
}

顺序表的尾删

cpp 复制代码
void SLPopFront(SL* ps)
{
	assert(ps != NULL);
	assert(ps->size != 0);
	for (int i = 0; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

顺序表任意部分插入

在长度为n的线性表中,第i个元素卡插入一个元素,需移动n-i-1个元素。

最好情况:在表尾插入(i = n + 1),元素后移 语句不执行,时间复杂度O(1).

最坏情况,在表头插入,元素后移语句将执行n次,时间复杂度为O(n)

顺序表任意部分删除

在长度为n的线性表中,第i个元素卡删除一个元素,需移动n-i个元素。

最好情况:删除表尾元素(i = n ),无需移动元素,时间复杂度O(1).

最坏情况,删除表头元素,需移动除第一个元素外的所有元素,时间复杂度为O(n)

顺序表查找值

最好情况:查找的元素就在表头,比较一次,时间复杂度为O(1)

最坏情况:查找的元素在表尾(或不存在)时,需要比较n次,时间复杂度为O(n)

顺序表销毁

链表和顺序表的对比

(1)存储方式:顺序表存储密度高(=1),存储空间利用率高,每个结点只存储数据元素,而链表还需要存储下一个结点的地址,存储密度低(

(2)逻辑结构与物理结构:顺序表:采用顺序存储,逻辑相邻的元素,对应的物理存储位置也相邻链表:采用链式存储,逻辑上相邻的元素,物理存储位置不一定相邻,对应的逻辑关系是通过指针来表示的

(3)空间分配:顺序表的空间分配分为静态分配和动态分配,静态内存分配时,很容易导致内存溢出或者是浪费。而动态内存分配时,如果不存在更大的连续的存储空间,导致分配失败,并且需要移动大量的元素,效率低。而链表是按需申请内存,操作灵活、高效

(4)读取方式:顺序表支持随机读取,通过首地址和元素序号可以在时间复杂度为O(1)的情况下找到指定元素。链表失去顺序表的优点,不支持随机读取

(5)查找:如果是按序号查找。顺序表支持随机查找,时间复杂度是O(1),而链表的时间复杂度是O(n),链表查找某个特定的结点时,需要从表头开始遍历依次查找

(6)插入删除操作:顺序表当执行插入和删除操作效率低,需要移动大量元素,链表插入删除元素效率高,不需要移动大量元素,只需要修改相关结点的指针域。

相关推荐
闪电悠米13 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
WL学习笔记13 小时前
堆(数据结构)
数据结构·
wabs66617 小时前
关于图论【卡码网100.最大岛屿的面积的思考】
数据结构·算法·图论
yuannl1019 小时前
图的最短路径Dijkstra
数据结构
zander25820 小时前
138. 随机链表的复制
数据结构·算法·链表
过期动态20 小时前
【LeetCode 热题 100】找到字符串中所有字母异位词
java·数据结构·算法·leetcode·职场和发展·rabbitmq
来一碗刘肉面20 小时前
栈在递归中的应用
数据结构·算法
皓月斯语20 小时前
程序设计语言的特点
开发语言·数据结构·c++
邪修king21 小时前
C++ 进阶终章:异常机制与智能指针全解 —— 从错误处理到 RAII 资源管理,打通现代 C++ 的核心命脉
android·数据结构·c++
Adios7941 天前
设置交集大小至少为2
数据结构·算法·leetcode