乐观学习,乐观生活,才能不断前进啊!!!
我的主页:optimistic_chen
我的专栏:c语言
点击主页:optimistic_chen和专栏:c语言,
创作不易,大佬们点赞鼓励下吧~
前言
什么是数据结构呢?
· 数据结构是由“数据”和“结构”两词组合⽽来。
· 概念:数据结构是计算机存储、组织数据的⽅式。
· 数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么⽅式构成,以及数据元素之间呈现的结构。
总结:
1)能够存储数据(如顺序表、链表等结构)
2)存储的数据能够⽅便查找
文章目录
顺序表概念
线性表( linear list )是n个具有相同特性的数据元素的有限序列。线性表是⼀种在实际中⼴泛使⽤的数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表的分类
顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝
静态顺序表
struct SeqList
{
int arr[100];//开少了不够,开多了浪费
int size;//有效数据个数
};
typedef int SLDataType;
静态顺序表是一种在数据结构中常见的线性表实现方式,它使用定长数组来存储数据元素。这种数据结构在初始化时就确定了存储空间的大小,并在运行期间保持不变。
动态顺序表
typedef struct SeqList
{
SLDataType* arr;//按需申请空间
int size;//有效数据个数
int capacity;//空间大小
}SL;
动态顺序表是一种可以动态调整其大小的顺序表,它克服了静态顺序表在大小固定方面的限制。
对比
分类 | 静态顺序表 | 动态顺序表 |
---|---|---|
空间分配 | 创建时就分配了固定的内存空间,这避免了在运行时频繁地申请或释放内存 | 根据数据的实际需求动态地扩展或缩小存储空间,从而避免了静态顺序表在存储空间分配上可能出现的浪费或不足的问题 |
灵活性 | 不灵活 | 灵活性强 |
效率 | 由于数据元素在内存中是连续存放的,因此可以通过下标直接计算出元素在内存中的位置,实现快速存取 | 尽管动态顺序表能够动态调整大小,但在插入或删除元素时,仍可能需要移动其他元素以保持数据的连续性。这尤其在元素数量较大或需要频繁进行插入/删除操作时可能导致效率降低 |
管理 | 静态顺序表的实现相对简单,因为数组的操作是基础的编程技能,所以理解和使用都比较容易 | 动态顺序表需要维护额外的元数据(如当前大小、最大容量等),并需要实现相应的内存管理操作(如分配、释放等)。这增加了实现的复杂性和出错的可能性 |
总结:
静态顺序表在数据规模确定且不需要频繁改变的情况下表现良好,但在面对动态变化的数据规模时,其缺点就显得尤为突出。动态顺序表在处理动态变化的数据规模时具有较大的优势,但也需要考虑其带来的额外开销和实现的复杂性。在选择使用顺序表时,需要根据具体的应用场景和需求进行权衡。
顺序表的实现
我们以动态顺序表为例,完成顺序表的实现:
我们首先将顺序表的所有需求定义在一个头文件SeqList.h中。
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType
#define INIT_CAPACITY 4
/动态顺序表
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;//空间大小
}SL;
//顺序表初始化
void SLInit(SL* ps);
//顺序表的销毁
void SLDestroy(SL* ps);
//顺序表的扩容
void SLCheckCapacity(SL* ps);
//顺序表的打印
void SLPrintf(SL* ps);
//头部插入 / 尾部插入
void SLPushBack(SL* ps, SLDataType x);
void SLPushPront(SL* ps, SLDataType x);
//头部删除 / 尾部删除
void SLPopBack(SL* ps);
void SLPopPront(SL* ps);
//在指定位置之前插入/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLFrase(SL* ps, int pos);
//查找数据
int SLFind(SL* ps, SLDataType x);
我们后期的一系列操作都是根据头文件中的需求实现的。
一、顺序表的初始化
void SLInit(SL* ps)
{
assert(ps);
ps->arr = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);
if (ps->arr == NULL)
{
perror("malloc fail");
return;
}
ps->size =0;
ps->capacity = INIT_CAPACITY;
}
代码分析:
断言(assert):
assert(ps);
断言用于检查传入的指针 ps 是否为 NULL。如果 ps 是 NULL,程序会在调试模式下终止执行,并显示一条错误消息。提前检查错误,确保ps 指向了一个有效的内存地址。
开辟内存空间
ps->arr = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);
malloc 函数分配了 INIT_CAPACITY 个 SLDataType 大小的内存块,并将其地址赋值给 ps->arr。
这里SLDataType 是顺序表中存储的数据类型,而 INIT_CAPACITY 是一个常量,表示顺序表初始的容量
内存开辟失败:
if (ps->arr == NULL)
{
perror("malloc fail");
return;
}
如果malloc函数没有顺利开辟内存,那么就会打印错误信息,不执行后面的代码。
初始化顺序表:
ps->size =0;
ps->capacity = INIT_CAPACITY;
size 表示顺序表中当前存储的元素数量,初始化为0,表示顺序表是空的。
capacity 表示顺序表的容量,即它可以存储的最大元素数量,初始化为 INIT_CAPACITY。
二、顺序表的销毁
void SLDestroy(SL* ps)//销毁
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
代码分析:
释放内存:
if (ps->arr)
{
free(ps->arr);
}
if 的判断条件为ps->arr不为NULL则释放arr所占用的空间
这是一个非常重要的步骤,因为如果不释放已分配的内存,会导致内存泄漏
重置指针和属性
ps->arr = NULL;
p