🎉🎉🎉欢迎莅临我的博客空间,我是池央,一个对C++和数据结构怀有无限热忱的探索者。🙌
🌸🌸🌸这里是我分享C/C++编程、数据结构应用的乐园✨
🎈🎈🎈期待与你一同在编程的海洋中遨游,探索未知的技术奥秘💞
📝专栏指路:
📘【C++】专栏:深入解析C++的奥秘,分享编程技巧与实践。
📘【数据结构】专栏:探索数据结构的魅力,助你提升编程能力。
前言
初步认识了数据结构后,我们一起来探索它的逻辑结构里面的线性结构吧。线性结构是一对一的关系。线性表在逻辑结构上是连续的,在物理结构上不一定是连续的。线性表中的顺序表(本篇的主角)在物理结构上是连续的,而线性表中的链表在物理结构上却是不连续的。
一、线性表
线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。
几个概念:
1.ai是线性表中的“第i个”元素线性表中的位序(位序从1开始,注意区分数组下标从0开始)
2.a1是表头元素;an是表尾元素。
3.除第一个元素外,每个元素有且仅有一个直接前驱(前一个元素);除最后一个元素外,每个元素有且仅有一个直接后继(后一个元素)
二、顺序表
正片开始!
1.概念
顺序表——用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。顺序表的底层是数组。
2.静态顺序表
顺序表的空间大小固定
补充:为了简化代码,我们使用typedef 重命名自定义类型,typedef的优势是什么?
在C或C++中,typedef 关键字用于为已存在的数据类型定义一个新名称(别名)。这在需要简化复杂的数据类型声明,或者为特定的数据类型提供一个更有描述性的名称时非常有用。
代码如下:
typedef int SQLDataType;//顺序表的数据类型
//静态顺序表
typedef struct SeqList
{
SQLDataType arr[100];
int size;//有效数据个数
int capacity;//空间大小
}SL;
3.动态顺序表
顺序表的大小空间不固定,可根据需求改变。
当顺序表存满时,用realloc增容。
typedef int SQLDataType;//顺序表的数据类型
//动态顺序表
typedef struct SeqList
{
SQLDataType* arr;
int size;//有效数据个数
int capacity;//空间大小
}SL;
补充:realloc扩容的规则是什么?
一次扩充一个空间 ,插入一个元素还不会造成空间浪费程序(执行效率低下)
一次扩容固定个大小的空间(10、100…)【小了造成频繁扩容】【大了造成空间浪费】
最优解:成倍数的增加(1.5倍、2倍),数据插入的越多扩容的大小越来越大
扩容后会自动把原有空间释放掉
malloc,realloc,calloc三者区别是什么?
- malloc函数:用于动态分配指定字节数的内存空间,并返回一个指向它的指针。如果分配成功,则返回非空指针;如果内存空间不足,则返回NULL。需要注意的是,malloc分配的内存空间并未初始化,它们的值是未知的。
- calloc函数:也用于动态分配内存空间,与malloc有所不同。calloc在分配内存空间时,会将其初始化为0。它的参数是要分配的元素个数和每个元素的大小,而不是总的字节数。如果分配成功,则返回指向分配的内存的指针;如果失败,则返回NULL。
- realloc函数:用于调整之前分配的内存空间大小。它接收一个指向已分配内存的指针和一个新的大小,然后尝试调整内存块的大小。如果成功,则返回指向新的内存块的指针;如果失败,则返回NULL,而原来的内存块保持不变。
代码示例
//是否需要申请空间
void SQLcapacity(SL* ps)
{
if (ps->capacity == ps->size)//空间已满,需要申请空间
{
//realloc增容,一般增加成原本空间大小的二或三倍
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SQLDataType* tmp = (SQLDataType*)realloc(ps->arr, newcapacity * sizeof(SQLDataType));
if (tmp == NULL)
{
perror("reacoll fail!");//空间申请失败
exit(1);//退出程序
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
4.对顺序表的操作
(1)初始化
void InitSql(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
(2)尾插
void PushBackSql(SL* ps, SQLDataType x)
{
assert(ps);
//插入之前先看空间够不够
SQLcapacity(ps);
ps->arr[ps->size++] = x;//size是顺序表尾部,后置++插入x后size加一
}
(3)头插
//头部插入
void PushHeadSql(SL* ps, SQLDataType x)
{
assert(ps);
//插入之前先看空间够不够
SQLcapacity(ps);
//让原本的数据往后移一位
for (int i = ps->size;i > 0;i--)
{
ps->arr[i] = ps->arr[i - 1];//arr[1]=arr[0]让arr[1]的位置放入原本arr[0]的数据
}
ps->arr[0] = x;
ps->size++;
}
(4)查找
//查找指定数据
int FindPosSql(SL* ps, SQLDataType x)
{
assert(ps);
for (int i = 0;i < ps->size;i++)
{
if (ps->arr[i] == x)
return i;
}
return -1;
}
找到了返回下标,找不到则返回不存在的下标
(5)尾删
//尾部删除
void DelBackSql(SL* ps)
{
assert(ps);
assert(ps->size);//顺序表为空时不能执行删除操作
ps->size--;
}
(6)头删
//头部删除
void DelHeadSql(SL* ps)
{
assert(ps);
assert(ps->size);//顺序表为空时不能执行删除操作
//顺序表存放数据整体往前挪一位
for (int i = 0;i < (ps->size) - 1;i++)
{
//arr[0]的位置放原本arr[1]的数据,最后是ps->arr[size-2]=ps->arr[size-1]
ps->arr[i] = ps->arr[i + 1];//arr[0]的位置放原本arr[1]的数据,最后是ps->arr[size-2]=ps->arr[size-1]
}
ps->size--;
}
(7)在指定位置之前插入数据
//指定位置前插入
void AddPosSql(SL* ps, int pos, SQLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
//先看看空间够不够
SQLcapacity(ps);
//pos位置上的数和他后面的数整体往后挪一位
for (int i = ps->size;i > pos;i--)
{
ps->arr[i] = ps->arr[i - 1];//结束arr[pos+1]=arr[pos];
}
ps->arr[pos] = x;
ps->size++;
}
(8)在指定位置删除数据
//删除指定位置数据
void DelPosSql(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
//整体往前挪一位
for (int i = pos;i < ps->size - 1;i++)
{
ps->arr[i] = ps->arr[i + 1];//arr[size-2]=arr[size-1]
}
ps->size--;
}
(9)销毁
//销毁顺序表
void DestroySql(SL* ps)
{
if (ps->arr)//不是空表才需要释放
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
顺序表小结
下一篇预告:线性表之单链表
持续更新中...
敬请期待