Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!
我的博客:<但凡.
我的专栏:《题海拾贝》、《编程之路》、《数据结构与算法之美》
欢迎点赞、关注!
1、 什么是顺序表
在数据结构中,顺序表是一种基本的数据组织形式,它是线性表的一种实现方式。线性表是由相同数据类型的元素构成的有序序列,而顺序表则是将这些元素顺序地存储在一块连续的存储区域内。顺序表的特点是通过元素的物理位置(内存中的位置)来表示元素之间的逻辑顺序关系。
其实说白了,数组就是一种顺序表,数组通过开辟一块连续的内存,并将数据连续的存储在这块内存之中。类似这种我们就称之为顺序表。顺序表的底层代码就是数组。
2、顺序表的实现
现在我们尝试用C语言实现顺序表。
首先我们想一下,我们想用顺序表实现什么?我们想的是,开辟一块内存,我们可以自由地往里面放数据,也可以自由的从里面删除数据。我们应该还能在顺序表中找到我们想要查找的元素。现在我们就顺着这个思路来写代码。
需要注意的是,我们在这里写一个头文件,用来声明结构体和函数,然后再写一个实验源文件,用来测试我们的顺序表功能,接着是我们的函数源文件,用来定义函数。以下的各部分代码都是以函数形式呈现的。
2.1结构体的声明
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size;//有效数据数量
int capacity;//空间大小
}SeqList;
这个结构体声明是放在我们的头文件里的
2.2顺序表的初始化
void SeqListInit(SeqList* ps)//注意传值和传址问题
{
ps->a = NULL;
ps->size =ps->capacity= 0;
}
我们将传入函数的结构体的地址进行初始化。
2.3检查并扩容函数
在进行插入的时候我们需要先判断一下空间是否足够,如果没开辟空间那就开辟,如果空间满了那就继续开辟,开辟大小为当前大小的两倍:比如现在有四个字节的大小,扩容后为八个字节,当前八个字节扩容后为16个字节(通过概率学分析这样扩容最高效)。
void SLCheckcapacity(SeqList* ps)//检查并扩容
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(int));
assert(tmp);
ps->a = tmp;
ps->capacity = newcapacity;
}
}
2.4顺序表数据的插入
(1)尾插
从顺序表的最后插入就叫尾插。
void SeqListPushBack(SeqList* ps, SLDateType x)//x为插入的数据
{
SLCheckcapacity(ps);
ps->a[ps->size++] = x;
}
(2)头插
从顺序表的最开始插入就叫头插。
void SeqListPushFront(SeqList* ps, SLDateType x)//头插
{
SLCheckcapacity(ps);
int i = 0;
for (i = 0;i < ps->size;i++)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[0] = x;
++ps->size;
}
我们可以分析一下,头插时间复杂度为O(n),尾插时间复杂度为O(1)。
2.5顺序表数据的删除
(1)尾删
void SeqListPopBack(SeqList* ps)
{
assert(ps && ps->size);//顺序表不为空,删除时注意
--ps->size;
}
(2)头删
void SeqListPopFront(SeqList* ps)//头删
{
assert(ps && ps->size);//顺序表不为空,删除时注意
int i = 0;
for (i = 0;i < ps->size - 1;i++)
{
ps->a[i] = ps->a[i+1];
}
--ps->size;//别忘了
}
2.6查找
int SeqListFind(SeqList* ps, SLDateType x)//查找
{
int i = 0;
for (i = 0;i < ps->size;i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return 0;//没找到
}
2.7指定下标插入
void SeqListInsert(SeqList* ps, int pos, SLDateType x)//pos是下标
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckcapacity(ps);//只要是插入我就得判断空间是否足够
int i = 0;
for (i = pos;i<ps->size;i++)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[pos] = x;
++ps->size;
}
2.8指定下标删除
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
int i = 0;
for (i = pos;i < ps->size-1;i++)
{
ps->a[i] = ps->a[i+1];
}
--ps->size;
}
2.9销毁顺序表
void SeqListDestroy(SeqList* ps)
{
if (ps->a)
{
free(ps->a);
}
ps->a = NULL;
ps->capacity = ps->size = 0;
}
现在我们把这些函数声明在头文件中:
#include <stdlib.h>
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size;//有效数据数量
int capacity;//空间大小
}SeqList;
//声明函数
// 对数据的管理:增删查改
void SeqListInit(SeqList* ps);//初始化顺序表
void SeqListDestroy(SeqList* ps);//销毁顺序表
void SeqListPrint(SeqList* ps);
void SeqListPushBack(SeqList* ps, SLDateType x);//尾插
void SeqListPushFront(SeqList* ps, SLDateType x);//头插
void SeqListPopFront(SeqList* ps);//头删
void SeqListPopBack(SeqList* ps);//尾删
// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);
再在测试源文件里引用这个头文件,我们对顺序表进行测试使用:
#include"SeqList.h"
int main()
{
SeqList ps;
SeqListInit(&ps);//初始化
SeqListPushFront(&ps, 5);//实验头插
printf("%d", ps.a[0]);
SeqListDestroy(&ps);
}
输出结果:
我们这里只举例使用一下头插,其他的使用都很相似。
好了,今天的内容就分享到这,我们下期再见!