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

发布于:2025-03-19 ⋅ 阅读:(15) ⋅ 点赞:(0)

线性表的定义

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

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

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

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

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

顺序表是线性表的一种

顺序表基本概念

顺序表的定义

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

顺序表存储

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

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

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

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

#define N 100
typedef int SLDateType;

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

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


typedef int SLDateType;

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

顺序表基本实现

顺序表结构


typedef int SLDateType;

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

顺序表的初始化

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

顺序表的打印


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

判断顺序表是否满了 扩容

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;

}

顺序表的头插

//头插
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++;
}

顺序表的尾插

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

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

顺序表的头删

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

顺序表的尾删

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


网站公告

今日签到

点亮在社区的每一天
去签到