前言
数据结构常见的结构有线性结构、树型结构、图形结构。
而线性结构又分为线性表、栈、数组、队列,本文讲的是顺序表,是线性表的一种。
一、项目
1.1 SeqList.h(头文件)
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define Max_Capacity 10
typedef int SLDateType;
//顺序表
typedef struct SeqList
{
//顺序表地址q
SLDateType* a;
//顺序表的有效长度
int size;
//顺序表大小
int capacity;
}SeqList;
//初始化顺序表
void SeqListInit(SeqList* ps);
//销毁顺序表
void SeqListDestroy(SeqList* ps);
//打印顺序表
void SeqListPrint(SeqList* ps);
//头插
void SeqListPushFront(SeqList* ps, SLDateType x);
//尾插
void SeqListPushBack(SeqList* ps, SLDateType x);
//头删
void SeqListPopFront(SeqList* ps);
//尾删
void SeqListPopBack(SeqList* ps);
//中间插
void SeqListInsert(SeqList* ps, int pos, int x);
//中间删
void SeqListErase(SeqList* ps, int pos);
//检查顺序表是否满了
SLDateType SeqListFull(SeqList* ps);
//顺序表扩容
void SeqListAdd(SeqList* ps);
//顺序表中查数
void SeqListSearch(SeqList* ps, SLDateType x);
//顺序表中改数
void SeqListChange(SeqList* ps, SLDateType x,SLDateType y);
1.2 SeqList.c
#include"SeqList.h"
//初始化顺序表
void SeqListInit(SeqList* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
//打印顺序表
void SeqListPrint(SeqList* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("顺序表无有效数据\n");
return;
}
for (SLDateType i = 0; i < ps->size; i++)
{
printf("%d ", *(ps->a + i));
}
printf("\n");
}
//销毁顺序表
void SeqListDestroy(SeqList* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
//判断顺序表是否满了
SLDateType SeqListFull(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
return 1;
}
return 0;
}
//扩容
void SeqListAdd(SeqList* ps)
{
assert(ps);
ps->a = (SLDateType*)realloc(ps->a,ps->capacity*2*sizeof(SLDateType));
if (ps->a == NULL)
{
perror("realloc error");
exit(-1);
}
ps->capacity *= 2;
}
//头插
void SeqListPushFront(SeqList* ps, SLDateType x)
{
assert(ps);
if (ps->size < 0)
{
ps->size = 0;
}
if (SeqListFull(ps))
{
SeqListAdd(ps);
}
for(SLDateType i = ps->size-1; i >=0; i--)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[0] = x;
ps->size++;
}
//尾插
void SeqListPushBack(SeqList* ps, SLDateType x)
{
assert(ps);
if (ps->size < 0)
{
ps->size = 0;
}
if (SeqListFull(ps))
{
SeqListAdd(ps);
}
ps->a[ps->size] = x;
ps->size++;
}
//头删
void SeqListPopFront(SeqList* ps)
{
assert(ps);
if (ps->size <=0)
{
ps->size = 0;
return;
}
for (SLDateType i = 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
//尾删
void SeqListPopBack(SeqList* ps)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
return;
}
ps->size--;
}
//中间插
void SeqListInsert(SeqList* ps,int pos,int x)
{
assert(ps);
if (ps->size <0)
{
ps->size = 0;
}
if (SeqListFull(ps))
{
SeqListAdd(ps);
}
if (pos - 1 < ps->size)
{
for(SLDateType i = ps->size - 1; i >= pos - 1; i--)
ps->a[i + 1] = ps->a[i];
ps->a[pos - 1] = x;
ps->size++;
}
else
printf("插入无效\n");
}
//中间删
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
return;
}
if (pos - 1 < ps->size)
{
for (SLDateType i = pos; i < ps->size; i++)
ps->a[i - 1] = ps->a[i];
ps->size--;
}
else
printf("删除无效\n");
}
//查
void SeqListSearch(SeqList* ps, int pos)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
}
if (pos - 1 < ps->size)
{
printf("%d\n",ps->a[pos - 1]);
return;
}
printf("查找无效\n");
}
//改
void SeqListChange(SeqList* ps, int pos, int x)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
return;
}
if (pos - 1 < ps->size)
{
ps->a[pos - 1] = x;
}
else
printf("修改无效\n");
}
1.3 test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
SLDateType main()
{
//创造顺序表
SeqList sl;
//初始化
SeqListInit(&sl);
sl.capacity = Max_Capacity;
sl.a = (SLDateType*)realloc(sl.a,sl.capacity * sizeof(SLDateType));
if (sl.a == NULL)
{
perror("realloc error");
exit(-1);
}
//尾插012345
for (SLDateType i = 0; i < 5; i++)
SeqListPushBack(&sl, i);
//打印顺序表
SeqListPrint(&sl);
//头插012345
for (SLDateType i = 0; i < 5; i++)
SeqListPushFront(&sl, i);
//再次打印
SeqListPrint(&sl);
//尾删3个数
for (SLDateType i = 0; i < 3; i++)
SeqListPopBack(&sl);
SeqListPrint(&sl);
//头删2个数
for(SLDateType i=0;i<2;i++)
SeqListPopFront(&sl);
SeqListPrint(&sl);
//中间第三个数插入2
SeqListInsert(&sl, 3, 2);
SeqListPrint(&sl);
//中间第三个数删除
SeqListErase(&sl, 3);
SeqListPrint(&sl);
//查找第3个数
SeqListSearch(&sl, 3);
//修改第3个数为100
SeqListChange(&sl, 3, 100);
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
return 0;
}
二、顺序表的实现
2.1 顺序表
typedef int SLDateType;
//顺序表
typedef struct SeqList
{
//顺序表地址q
SLDateType* a;
//顺序表的有效长度
int size;
//顺序表大小
int capacity;
}SeqList;
创建一个顺序表结构体,成员包含顺序表地址、长度、大小,用于创建顺序表变量。
2.2 初始化顺序表
//初始化顺序表
void SeqListInit(SeqList* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
将顺序表变量的地址传参,通过指针接收对顺序表的顺序表数组初始化为空,长度为0,大小为0。
2.3 打印顺序表
//打印顺序表
void SeqListPrint(SeqList* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("顺序表无有效数据\n");
return;
}
for (SLDateType i = 0; i < ps->size; i++)
{
printf("%d ", *(ps->a + i));
}
printf("\n");
}
同样传地址,要先断言指针是否为空,不然会出异常。
然后判断顺序表大小是否为0,为0则代表顺序表中没有有效元素,打印提示,并返回函数,如果大于0,则有元素,从下标0开始,打印size个顺序表元素,并用空格相隔。
2.4 销毁顺序表
//销毁顺序表
void SeqListDestroy(SeqList* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
当我们结束程序时,因为我们是通过动态分配空间给顺序表,所以结束时我们要手动销毁,先断言,防止free空指针,成功释放空间后,将ps->a手动置空,避免产生野指针,并将大小、空间赋0。
三、对顺序表进行操作
3.1 在顺序表的头部插入数据
//头插
void SeqListPushFront(SeqList* ps, SLDateType x)
{
assert(ps);
if (ps->size < 0)
{
ps->size = 0;
}
if (SeqListFull(ps))
{
SeqListAdd(ps);
}
for(SLDateType i = ps->size-1; i >=0; i--)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[0] = x;
ps->size++;
}
在头部插入数据,要先判断容量是否已满,如果已满,需要扩容,还有空间,那么就从下标为ps->size-1开始,到0上的值都要将前一个元素给覆盖,而将要插入的数据覆盖给下标为0的空间,长度加1。
3.2 在顺序表末尾插入数据
//尾插
void SeqListPushBack(SeqList* ps, SLDateType x)
{
assert(ps);
if (ps->size < 0)
{
ps->size = 0;
}
if (SeqListFull(ps))
{
SeqListAdd(ps);
}
ps->a[ps->size] = x;
ps->size++;
}
都是插入数据,所以前面的操作和头插一致,但是尾插要比头插看起来方便,因为只要在顺序表后一个空间内插入要插入的数据,不需要移动数据,并将长度+1。
3.3 删除在顺序表头部的数
//头删
void SeqListPopFront(SeqList* ps)
{
assert(ps);
if (ps->size <=0)
{
ps->size = 0;
return;
}
for (SLDateType i = 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
判断部分不需要判断容量是否够,且长度不能为负数。
和头插相反,我们要从下标为1,到ps->size-1,将前一个数覆盖,长度减1。
3.4 删除在顺序表末尾的数
//尾删
void SeqListPopBack(SeqList* ps)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
return;
}
ps->size--;
}
判断部分一致,尾删只需将长度减一即可。
3.5 在顺序表中间位置插入数据
//中间插
void SeqListInsert(SeqList* ps,int pos,int x)
{
assert(ps);
if (ps->size <0)
{
ps->size = 0;
}
if (SeqListFull(ps))
{
SeqListAdd(ps);
}
if (pos - 1 < ps->size)
{
for(SLDateType i = ps->size - 1; i >= pos - 1; i--)
ps->a[i + 1] = ps->a[i];
ps->a[pos - 1] = x;
ps->size++;
}
else
printf("插入无效\n");
}
中间插比较麻烦,不仅要断言,判断ps->size是否合理,容量是否够,还要判断所给插入数据下标位置是否在0到ps->size之间的合法区域,所以以上同时合法时, 才能实现插入数据,和头插相似,它们都是先从尾部数据开始,先向右整体覆盖过去,但是头插是到0停止,而中间插是到所给下标停止,并将该下标的数插入为想插入的数据,长度加1。
3.6 在顺序表中间位置删除数据
//中间删
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
return;
}
if (pos - 1 < ps->size)
{
for (SLDateType i = pos; i < ps->size; i++)
ps->a[i - 1] = ps->a[i];
ps->size--;
}
else
printf("删除无效\n");
}
和头删相似,将指定下标左边的元素往左移动一个空间,长度减1。
3.7 在顺序表中查找指定下标的值
//查
void SeqListSearch(SeqList* ps, int pos)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
}
if (pos - 1 < ps->size)
{
printf("%d\n",ps->a[pos - 1]);
return;
}
printf("查找无效\n");
}
返回指定下标下的值,比较容易实现。
3.8 将顺序表指定下标的值修改
//改
void SeqListChange(SeqList* ps, int pos, int x)
{
assert(ps);
if (ps->size <= 0)
{
ps->size = 0;
return;
}
if (pos - 1 < ps->size)
{
ps->a[pos - 1] = x;
}
else
printf("修改无效\n");
}
找到指定下标的位置,直接赋给修改的值即可,也很容易实现。
3.9 顺序表的扩容
/判断顺序表是否满了
SLDateType SeqListFull(SeqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
return 1;
}
return 0;
}
//扩容
void SeqListAdd(SeqList* ps)
{
assert(ps);
ps->a = (SLDateType*)realloc(ps->a,ps->capacity*2*sizeof(SLDateType));
if (ps->a == NULL)
{
perror("realloc error");
exit(-1);
}
ps->capacity *= 2;
}
如果ps->size等于容量,代表容量已满,则将容量扩大为原来的两倍,但是可能会出现内存泄漏的情况,严谨一点应该在realloc完后将地址赋给一个新指针,由新指针判断不为空,再赋给顺序表指针,如果扩容失败,程序将会终止。
四、实现程序
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
SLDateType main()
{
//创造顺序表
SeqList sl;
//初始化
SeqListInit(&sl);
sl.capacity = Max_Capacity;
sl.a = (SLDateType*)realloc(sl.a,sl.capacity * sizeof(SLDateType));
if (sl.a == NULL)
{
perror("realloc error");
exit(-1);
}
//尾插01234
for (SLDateType i = 0; i < 5; i++)
SeqListPushBack(&sl, i);
//打印顺序表
SeqListPrint(&sl);
//头插01234
for (SLDateType i = 0; i < 5; i++)
SeqListPushFront(&sl, i);
//再次打印
SeqListPrint(&sl);
//尾删3个数
for (SLDateType i = 0; i < 3; i++)
SeqListPopBack(&sl);
SeqListPrint(&sl);
//头删2个数
for(SLDateType i=0;i<2;i++)
SeqListPopFront(&sl);
SeqListPrint(&sl);
//中间第三个数插入2
SeqListInsert(&sl, 3, 2);
SeqListPrint(&sl);
//中间第三个数删除
SeqListErase(&sl, 3);
SeqListPrint(&sl);
//查找第3个数
SeqListSearch(&sl, 3);
//修改第3个数为100
SeqListChange(&sl, 3, 100);
SeqListPrint(&sl);
//销毁顺序表
SeqListDestroy(&sl);
return 0;
}
用SeqList.c的函数实现我们对顺序表sl的一系列操作,结束时销毁顺序表。
打印结果如下