1.什么是顺序表?
顺序表是一种线性表的存储结构,它是由一组连续的存储单元(通常是数组)依次存储线性表中的元素,元素之间的顺序关系由它们在存储空间中的相对位置来表示。
2.顺序表与数组之间的区别
顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口。
3.顺序表的特点:
顺序表的特点是元素之间的顺序是固定的,可以通过元素在数组中的下标来访问和操作元素。顺序表的插入、删除操作比较耗时,但是查找操作效率很高。
顺序表是一种基本的数据结构,具有以下几个好处:
-
随机访问:顺序表可以通过下标直接访问任意位置的元素,具有O(1)的时间复杂度,使得查找和访问元素非常高效。
-
连续存储:顺序表的元素在内存中是连续存储的,这样可以充分利用计算机的缓存机制,提高访问效率。
-
简单易实现:顺序表的实现相对简单,只需要一个数组和一个记录元素个数的变量即可,操作也比较直观。
-
空间利用率高:顺序表的存储空间是连续的,不会产生额外的空间开销,因此空间利用率比较高。
-
支持动态扩展:顺序表可以通过动态扩展的方式增加存储空间,使得其具有一定的灵活性和扩展性。
4.顺序表的分类:
1.静态顺序表
2.动态顺序表
5.下面来进行顺序表的实现:
1.创建动态顺序表
#include<iostream>
using namespace std;
typedef int SLDateType;
#define N 10
//静态顺序表的实现
typedef struct Seqlist
{
SLDateType a[N];//定义的静态数组
int size=N; //有效的个数
}SL;
//动态顺序表的实现
typedef struct Seqlist
{
SLDateType* a;
int size; //有效的空间
int capacity; //动态的容量
}SL;
注意:这里将int定义为SLDateType是便于后期适用于其他的类型。
2.初始化
//初始化
void SLInit(SL* ps)
{
assert(ps);
ps->a = NULL;
ps->size = ps->capacity = 0;
这是的初始化是将他的指针置为空,防止野指针的出现。
assert()的作用是判断你给的指针是否为空,为空会报出警告。 需要包含头文件assert.h
3.销毁
//销毁
void SLDestroy(SL* ps)
{
if (ps->a)
{
free(ps);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
}
开辟完的空间我们一定要销毁掉,否则会造成空间的浪费。
4.打印
//打印
void SLPrint(SL* ps)
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ",ps->a[i]);
}
printf("\n");
}
5.扩容以及初始给内存
//扩容以及初始给内存
void SLCheckCapacity(SL* ps)
{
if (ps->capacity == ps->size)
{
SLDataType newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tem = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * newcapacity);
ps->capacity = newcapacity;
if (tem == NULL)
{
perror("tem");
return;
}
ps->a = tem;
}
}
我们刚开始我们可以通过表达式来判断是否size为0,如果为零则先开辟4个空间,如果不为零,则将空间扩大一倍,这里的realloc虽然是用来扩充空间的,但是当你的空间为零时,他的作用与malloc的作用一样。
这里的realloc用tem来接收是因为开辟新的空间时,可能开辟失败,从而变为NULL,所以先用tem来接收,如果开辟成功再来进行新的位置。
如果开始就用ps来接收,当ps变为NULL时,我们不好找到原来的地址,当然我们也可以先创建一个指针来与ps指向同一个位置。
我们写这一个代码的作用,可以帮助我们判断你是否开辟了空间以及你所开辟的空间是否用完,用完的话,头插,尾插等功能会受到限制,所以我们只需要加入这一个代码即可摆脱很多的困难。
6.头插
//头插
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
int i = ps->size;
for (; i >0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
思路:从头开始一个一个移动的话,这个代码会非常的复杂,正所谓正难则反,所以我们可以先从尾部开始移动,从而空出第一个位置,插入我们要的值 (不要忘了让size++)
7.尾插
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
尾插 :size是指用了多少个空间,正好指向最后末尾的一个空的空间,所以代码简单。
8.头删
//头删
void SLPopFront(SL* ps)
{
assert(ps);
if (ps->size == 0)
{
return;
}
int i;
for (i = 0; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
头删是直接用第二个覆盖第一个数据,再依次类推。
9.尾删
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
ps->a[ps->size - 1] = NULL;
ps->size--;
}
10.指定位置之前插入
//指定位置之前插⼊
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0&&pos<=ps->size);
SLCheckCapacity(ps);
int i;
for (i=ps->size;i>pos;i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos] = x;
ps->size++;
}
思路与头插相似,只不过是把你指定的位置看做了头而已。
11.指定位置删除
//指定位置删除
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
int i;
for (i = pos; i < ps->size; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size++;
}
以上便是顺序表的一些基本内容,希望看完之后会对你有所帮助。