数据结构之顺序表

什么是顺序表?

  • 地址连续:所有元素挨在一起存放,中间不留空位置
  • 逻辑顺序 = 物理顺序:数据前后顺序,和内存里摆放顺序完全一致
  • 随机访问快 :通过下标能立刻找到任意元素(查询效率高)

顺序表分类

顺序表可以分为静态顺序表和动态顺序表

|----------------|-------------------|
| 静态顺序表 | 动态顺序表 |
| 一开始就规定好了数组的空间, | 数组空间由动态申请获得 |
| 适用的场景较少 | 适用场景较多,但是实现起来比较困难 |


动态顺序表的实现

顺序表的结构

cpp 复制代码
typedef int SqDataType; // 数据元素类型



typedef struct {
    SqDataType* arr;        // 存储数据的动态数组的指针
    int size;               // 记录顺序表中已经存入的数据个数
    int capacity;           // 动态数组的容量空间的大小
}SqList;

初始化顺序表

将结构体的arr,size,capacity进行初始化

对于数组我们要动态申请空间也就是要用到malloc函数

基本用法

cpp 复制代码
int *p = (int *)malloc(sizeof(int));

实现初始化

cpp 复制代码
void SqListInit(SqList* ps)
{
	SqDataType* space=(SqDataType*)malloc(sizeof(SqDataType) * 4);
	if (space == NULL)
	{
		printf("申请空间失败\n");
		return;
	}
	ps->arr = space;
	ps->size = 0;
	ps->capacity = 4;
}

销毁顺序表

将size,capacity归零,free数组

cpp 复制代码
void SqListDestroy(SqList* ps)
{
	assert(ps);
	ps->size = 0;
	free(ps->arr);
	ps->arr = NULL;
	ps->capacity = 0;
}

插入顺序表

顺序表的插入是需要挪动数据的

如果我们从前往后挪动数就会让数字覆盖其他的数字,我们要采取从后往前挪动,然后在插入

但是我们是动态顺序表我们要考虑到阔容的问题

此时我们就要用到我们的relloc

由于realloc函数阔容存在原地阔容和异地阔容这两种,还有可能阔容失败的可能,我们为了保护数据的安全性我们需要用一个新的变量先保存这个阔容好的地址

cpp 复制代码
void SqListInsert(SqList* ps, int i, SqDataType x)
{
	assert(ps);
	assert(i <= ps->size  );
	assert(i >= 0);
	//不需要扩容时
	if (ps->size< ps->capacity)
	{
		++ps->size;
		for (int j = ps->size-1; j > i; j--)
		{
			ps->arr[j] = ps->arr[j - 1];
		}
		ps->arr[i] = x;
		
	}
	//需要阔容时
	else
	{
		int newcapacity = 2 * ps->capacity;
		SqDataType* newspace = (SqDataType*)realloc(ps->arr, sizeof(SqDataType) * newcapacity);
		if (newspace == NULL)
		{
			printf("申请空间失败\n");
			return;
		}
		ps->arr = newspace;
		ps->capacity = newcapacity;
		for (int j = ps->size; j > i; j--)
		{
			ps->arr[j] = ps->arr[j - 1];
		}
		ps->arr[i] = x;
		++ps->size;
	}
	

}

删除顺序表

这里也涉及到数据的移动,和插入不同的是数据是从前往后挪动,完成数据的覆盖

cpp 复制代码
SqDataType SqListDelete(SqList* ps, int i)
{
	assert(ps && ps->arr);
	assert(i < ps->size && i >= 0);
	SqDataType del = ps->arr[i];
	for (int j = i; j < ps->size; j++)
	{
		ps->arr[j] = ps->arr[j + 1];
	}
	--ps->size;
	return del;
}

打印顺序表

大音顺序表,就是将表进行遍历一遍,从头走到尾

cpp 复制代码
void SqListPrint(SqList* ps)
{
	assert(ps);
	assert(ps->arr);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d  ", ps->arr[i]);
	}
}

顺序表的判空,有效元素个数,,头插尾插,头删尾删

这些都比较简单不做过多赘述,头插尾插,头删尾删都可以复用插入和删除

cpp 复制代码
// 检测顺序表是否为空,空返回true,否则返回false
bool EmptySqList(SqList* ps)
{
	assert(ps);
	return ps->size == 0;
}


// 获取顺序表中有效元素个数
int SqListSize(SqList* ps)
{
	assert(ps);
	return ps->size;
}


// 以下接口复用上面的Insert和Delete即可完成
// 头插尾插
void SqListPushBack(SqList* ps, SqDataType x)
{
	assert(ps);
	SqListInsert(ps, ps->size, x);
}
void SqListPushFront(SqList* ps, SqDataType x)
{
	assert(ps);
	SqListInsert(ps, 0, x);

}


// 头删尾删
void SqListPopBack(SqList* ps)
{
	assert(ps);
	SqListDelete(ps, ps->size - 1);
}
void SqListPopFront(SqList* ps)
{
	assert(ps);
	SqListDelete(ps, 0);

}

小技巧

1. 指针相关断言
  • 访问指针本身时,需要对指针断言(如 assert(ps);),防止传入空指针。
  • 访问指针指向的成员(如 ps->arr)时,需要对该成员断言(如 assert(ps->arr);),防止数组指针为空。
2. 位置合法性断言

在插入、删除操作中,必须对操作位置 i 做断言,确保位置合法:

  • 插入位置范围:0 ≤ i ≤ ps->size(尾插时 i == ps->size 是允许的)。
  • 删除位置范围:0 ≤ i < ps->size(不能删除不存在的元素)。
3. 核心原则

断言的本质是:代码里要访问什么,就对什么做断言;操作涉及边界,就对边界做断言,以此提前捕获非法操作,避免后续出现内存访问错误。

相关推荐
Darling噜啦啦2 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠3 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾3 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres8213 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q3 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒3 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
WL学习笔记4 天前
单项不带头不循环链表
数据结构·链表
小糯米6014 天前
JS 数组
数据结构·算法·排序算法
小欣加油4 天前
leetcode3612 用特殊操作处理字符串I
数据结构·c++·算法·leetcode·职场和发展
凌波粒4 天前
LeetCode--90.子集II(回溯算法)
数据结构·算法·leetcode