数据结构之顺序表

什么是顺序表?

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

顺序表分类

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

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


动态顺序表的实现

顺序表的结构

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. 核心原则

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

相关推荐
z落落1 小时前
C# Hashtable 哈希表+SortedList 有序键值对集合
数据结构·散列表
Brilliantwxx2 小时前
【C++】 深入理解红黑树:实现与原理全解
数据结构·c++·笔记·算法·青少年编程·红黑树
Dlrb12112 小时前
数据结构-排序算法
数据结构·算法·排序算法·插入排序·堆排序·希尔排序·快速排序
过期动态2 小时前
【LeetCode 热题 100】无重复字符的最长子串
java·数据结构·spring boot·算法·leetcode·职场和发展
莫等闲-3 小时前
leetcode42. 接雨水 leetcode84.柱状图中最大的矩形
数据结构·c++·算法·leetcode
浅念-3 小时前
LeetCode 记忆化搜索 刷题总结
数据结构·算法·leetcode·职场和发展·深度优先·dfs
菜菜的顾清寒3 小时前
力扣HOT100(44)对称二叉树
数据结构·算法·leetcode
六bring个六3 小时前
c/c++面试踩坑笔记
c语言·数据结构·c++
南境十里·墨染春水4 小时前
数据结构 —— 双向循环链表
数据结构·链表