顺序表的概念
线性表(linear list):是n个具有相同特性的数据元素的有限序列。
线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
顺序表:是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
顺序表实现
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储。
- 动态顺序表:使用动态开辟的数组存储。
//静态顺序表
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; // 定长数组
size_t size;
}SL;
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size; // 有效数据
int capacity; // 空间容量
}SL;
顺序表接口实现
基本增删查改接口
#define _CRT_SECURE_NO_WARNINGS
//顺序表:物理地址连续的储存单元,类似数组,但又不是数组,数组只要不溢出栈,可以随意存放,而顺序表必须连续存放数据
#include<stdio.h> //printf头文件
#include<assert.h> //断言头文件
#include<stdlib.h> //realloc的头文件
typedef int SLDataType; //对数据类型进行重命名
typedef struct SeqList //顺序表的内容定义为一个结构体类型,对其进行重命名
{
SLDataType* a; //指针
int size; //有效数组元素
int capacity; //顺序表的容量大小
}SL; //重命名结构体
void SLInit(SL*ps); //初始化数组
void SLDstroy(SL* ps); //顺序表销毁
void SLPrint(SL* ps); //打印顺序表
void SLPushBack(SL* ps, SLDataType x); //尾部插入数据
void SLPopBack(SL* ps); //尾部删除数据
void SLPushFront(SL* ps, SLDataType x); //头部插入数据
void SLPopFront(SL* ps); //头部删除数据
void SLInsert(SL* ps, int pos, SLDataType x); //任意位置插入数据
void SLErase(SL* ps, int pos); //任意数据删除数据
void SLFind(SL* ps, SLDataType x); //查找
顺序表初始化
初始化是指将顺序表中所有元素都赋值为0或者空字符;
//初始化顺序表
void SLInit(SL* ps)
{
assert(ps); //断言,防止出现野指针
ps->a = NULL; //初始化顺序表,把指针置为空指针,
ps->size = 0; //有效数组元素置为0
ps->capacity = 0; //容量为空
}
销毁顺序表
//销毁顺序表
void SLDstroy(SL* ps)
{
assert(ps);
if (ps->a != NULL)
{
free(ps->a); //释放动态开辟的空间,防止内存泄漏
ps->a = NULL; //把顺序表置为空
ps->size = 0; //顺序表的元素置为0
ps->capacity = 0; //顺序表空间容量置为0
}
}
顺序表遍历打印显示
//打印顺序表
void SLPrint(SL* ps)
{
assert(ps); //断言
if (ps->size == 0) //检查顺序表是否为空
{
printf("顺序表为空\n");
return;
}
int i = 0;
for (i = 0; i < (ps->size); i++) //打印顺序表的有效数据
{
printf("%d ", ps->a[i]); //打印顺序表a的每个元素
}
printf("\n");
}
检查顺序表的元素是否满
realloc函数:更改所指向的内存块的大小。
函数可能会将内存块移动到一个新位置(其地址由函数返回)。
即使内存块被移动到新的位置,内存块的内容也会保留到新大小和旧大小的较小值。如果新值较大,则新分配部分的值是不确定的。
如果是空指针,函数的行为类似于分配一个新的字节块,并返回一个指向其开头的指针。
//检查顺序表是否放满,满了就扩容
void SLCheckCapacity(SL*ps)
{
assert(ps); //断言
if (ps->size == ps->capacity) //如果顺序表的容量放满了元素,就需要对它进行扩容
{
int Newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);
// 如果原来的容量为0,就给4个空间赋给capacity,如果原来空间不为0则对其扩容2倍
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType)*Newcapacity); //扩容,开辟新的空间
if (tmp == NULL) //如果扩容失败,tmp就是空指针,那就报一下错误信息
{
perror("realloc fail");
return;
}
ps->a = tmp; //否则把新的空间赋值给顺序表
ps->capacity = Newcapacity; //然后把新的扩容空间给到顺序表的容量,更新一下顺序表的空间容量
}
}
顺序表尾插数据
尾插就是在顺序表元素后面插入元素,顺序表非常擅长尾部插入数据,时间复杂度 O(1)
//尾部插入数据
void SLPushBack(SL* ps,SLDataType x) //指针指向数组最后一个元素,在末尾添加一个元素
{
assert(ps);
SLCheckCapacity(ps); //先检查一下顺序表是否填满了
ps->a[ps->size] = x; //把元素x存放到顺序表a的最后一个元素上
ps->size++; //然后对顺序表的有效元素进行+1
}
顺序表尾删数据
//尾部删除数据
void SLPopBack(SL* ps)
{
assert(ps); //断言一下
//暴力检查一下顺序表的元素,不能冒然的将顺序表最后一个数据赋值为 0
assert(ps->size>0);
//尾部删除即在顺序表的最后一个元素位置处进行删除数据
ps->size--; //顺序表的有效数据减去一个
}
顺序表头插数据
顺序表头插数据需要找到尾巴的元素,然后将尾元素往前的全部元素全部往后移动覆盖掉,再将数据填到顺序表的表头,时间复杂度是O(n)
//头部插入数据
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps); //先检查一下顺序表是否满
//挪动数据
int end = ps->size - 1; //顺序表中[0,size-1]的元素依次向后挪动一位,这里的end是顺序表的最后一个元素
while (end >= 0)
{
ps->a[end + 1] = ps->a[end]; //当最后挪动的元素减到-1时即[0,size-1]的元素都往后移动了一位
--end; //顺序表中[0,size-1]的元素全部往后挪动一位,首元素位置就空出来
}
ps->a[0] = x; //首元素添加一个数据x
ps->size++; //头部插入数据后,整个顺序表就+1个元素
}
顺序表头删数据
顺序表头部删除数据需要找到顺序表头元素,然后将全部元素往前移动覆盖掉,时间复杂度O(n)
//头部删除数据
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0); //暴力检查一下,顺序表不能够是空的,否则就没办法头部删除了
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin]; //顺序表中[1,size-1]的元素依次向前挪动一位,把首元素的数据挪到第二个元素位置上
++begin;
}
ps->size--;
}
顺序表pos下标位置处插入
//任意位置插入数据 顺序表有效数据[0,size-1]
void SLInsert(SL* ps, int pos, SLDataType x) //pos是下标,size是顺序表有效元素个数的下一个位置
{
assert(ps);
assert(pos >= 0 && pos <= ps->size); //pos的位置是任意的,在顺序表的所以有效元素范围内
SLCheckCapacity(ps); //插入数据之前检查一下顺序表是否填满了
int end = ps->size - 1; //顺序表的最后一个元素
while (end >= pos)
{
ps->a[end + 1] = ps->a[end]; //[pos,size-1]的元素都往后挪动一位
--end;
}
ps->a[pos] = x; //在pos下标处插入有效元素x
ps->size++; //此时顺序表有效数据+1
}
顺序表pos位置处删除元素
//任意位置删除数据
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
int begin = pos+1; //pos下标的下一个位置
while ( begin < (ps->size))
{
ps->a[begin - 1] = ps->a[begin]; // 顺序表[begin,size-1]的元素往前挪一位
++begin;
}
ps->size--;
}
顺序表查找元素位置
//查找
void SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
增删查改测试代码实例:
#include"SeqList.h"
void TestSL1()
{
SL s1;
SLInit(&s1); //初始化顺序表即初始化数组
printf("尾部插入5个数据:");
SLPushBack(&s1, 1); //尾插
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPushBack(&s1, 5);
SLPrint(&s1);
printf("\n");
printf("尾部删除3个数据:");
SLPopBack(&s1); //尾删
SLPopBack(&s1);
SLPopBack(&s1);
SLPrint(&s1);
printf("\n");
printf("头部插入5个数据:\n");
SLPushFront(&s1, 11);
SLPushFront(&s1, 12);
SLPushFront(&s1, 13);
SLPushFront(&s1, 14);
SLPushFront(&s1, 15);
SLPrint(&s1);
printf("\n");
printf("头部删除2个数据:\n");
SLPopFront(&s1);
SLPopFront(&s1);
SLPrint(&s1);
printf("\n");
printf("删除顺序表下标为3的数据:\n");
SLErase(&s1, 3);
SLPrint(&s1);
printf("\n");
printf("顺序表下标为3位置处增加数据20:\n");
SLInsert(&s1, 3, 20);
SLPrint(&s1);
printf("\n");
}
//主函数
int main()
{
SL s1;
TestSL1(&s1);
return 0;
}
觉得写得不错可以点个赞呀。