本文主要介绍了数据结构的顺序表,内容全由作者原创(无AI),同时深度解析了顺序表增删查改等功能,并带有配图帮助博友们更好的理解,点个关注不迷路,下面进入正文~~
目录
前言:什么是数据结构?
当我们学习完C语言后,我们要开始学习数据的内容,那么你是否会好奇,数据结构是什么?
简单的说,数据结构就是数据的存放方式+组织方式
例如,班里面的学生或许是按学号排列,或许是按姓名排列,这些都叫数据结构
当我们面对一堆非常庞大的数据时,就需要通关数据结构去排序
1.顺序表
1.1线性表的概念及结构
线性表=排成一条直线的数据集合
例如:蔬菜分为绿叶类、瓜类、菌类等,指的是有某一类相同特性数据的集合
常见的线性表:顺序表、链表、栈、队列、字符串......
逻辑结构:线性表是线性结构,是一条连续的结构
物理结构:在物理结构上不一定是连续的,及在内存上不一定是连续的
1.2顺序表和数组的关系
顺序表的底层结构是数组,对数组的封装,实现了常用的增删查改等接口
2.顺序表的分类
2.1静态顺序表
概念:使用定长数组存储元素
下面我们实现静态顺序表
cs
typedef int SLDataType;
cs
#define MAX 100
typedef struct SeqList
{
SLDataType arr[MAX];
int size;
}SL;
注意:一个顺序表不一定是存一种类型的数据,因此,为了方便我们后续的使用,我们将数据类型命名位SLDataType,这样当我们想要存其他类型的数据时,只需改变一处地方即可(如上图所示的int)
arr[MAX]为定长数组
size为有效数据个数
++静态顺序表缺陷:空间给少了不够用,空间给多了造成浪费++
因此,更推荐使用动态顺序表
2.2动态顺序表
cs
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;
}SL;
arr为指向一块空间的指针,可以用malloc、realloc等内存函数来分配内存
size为有效数据个数
capacity为空间容量
3.动态顺序表的具体实现
3.1源文件及头文件的建立
为了方便后续代码的实现,我们将建立一个SeqList的头文件、一个SeqList的源文件及一个用来测试代码的test源文件

3.2动态顺序表需要实现的功能
cs
void SLInit(SL* ps);
void SLDestory(SL* ps);
void SLprint(SL s);
void SLCheckCapacity(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);
int SLFind(SL* ps, SLDataType x);
3.2.1动态顺序表的初始化
cs
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
注意:当我们想通过函数改变结构体里的值时,应当使用传址调用,因此函数的接受值应是指针,及SL* ps,当我们使用函数时要传如结构体的地址,如&sl。
3.2.2动态顺序表的销毁
当我们使用动态顺序表时,我们必然会为arr分配内存,因此我们必须要将分配给arr的内存释放掉,防止内存泄露
cs
void SLDestory(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
3.2.3动态顺序表的打印
cs
void SLprint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
3.2.4动态顺序表的尾插
尾插,顾名思义实在动态顺序表的最后插入一个值。但同时存在顺序表空间不足的可能,及size==capacity,因此在头插之前,我们要先检查动态顺序表是否有空间插入值。为此我们将定义一个检查空间容量的函数。
cs
void SLCheckCapacity(SL* ps)
{
if (ps->capacity == ps->size)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = realloc(ps->arr, newCapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
注意:
1.新的空间大小通常是原来空间大小的2倍或3倍
2.原来的capacity可能是0,不能直接翻倍。可以用三目操作符判断capacity是否为0,若为0;赋值为4;若不为0,则翻倍。
下面开始实现动态顺序表的尾插
cs
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
需要注意的几个细节:
1.传入的ps有可能为空指针,需要用assert断言
2.ps->arr[ps->size]就是我们需要插入的地方,如图所示

3.插入后size要加1,具体如代码所示
3.2.5动态顺序表的头插
头插,在最前面的位置插如一个数据
为了实现头插,我们要把数据从前向后移动,先移动最后面的数据,防止数据丢失

第一步,我们需要把下标为size-1的数据移动到下标为size的位置,及arr[size]=arr[size-1],由此可得 i 的初始值为size
如此往复

到最后一步,我们需要把下标为0的元素移动到下标为1的位置,及arr[1]=arr[0],i 的最后一个值为1,所以 i 的范围应当是 i > 0
cs
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
**注意:**千万不要忘了size++
3.2.6动态顺序表的尾删
尾删就是将最后一个位置的数据删掉
我们想想看,如果size--,是不是就相当于尾删?
答案:是的!
cs
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
--ps->size;
}
如果size已经为0了,那删除数据就会出现问题,因此我们需要检测size是否等于0,如果是,就报错。
3.2.7动态顺序表的头删
头删,将最前面的位置删掉
第一步,将下标为1的元素替换下标为0的元素,及arr[0]=arr[1], i 的初始值为0

最后一步,将下标为size-1的元素替换下标为size-2的元素,及arr[size-2]=arr[size-1],i 的最大值为size-2,所以 i <size-1

下面为头删的实现代码
cs
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
3.2.8在动态顺序表的指定位置插入数据
我们将实现在指定位置之前插入数据

比如说,我们想在pos=1的位置插如一个数据,那我们应当将2 3 4都向后移动一位,并且是从前向后移动,先移动4,及arr[size]=arr[size-1],i 的初始值是size
最后一步,我们需要将下标为pos的元素移动到下标为pos+1的位置,及arr[pos+1]=arr[pos],i>pos

下面为具体代码的实现
cs
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
**注意:**我们还应当检查pos的范围,pos的范围是大于等于0并且小于等于size
3.2.9在动态顺序表的指定位置删除数据

比如说,我们要将pos=1的数据删除
第一步,将下标为pos+1的元素移动到下标为pos的位置,及arr[pos]=arr[pos+1],所以 i 的初始值是pos

最后一步,将下标为size-1的元素替换下标为size-2的元素,及arr[size-2]=arr[size-1],i 的最大值为size-2,所以 i <size-1
cs
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(ps->size);
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
3.2.10在动态顺序表查找指定数据
在动态顺序表查找指定数据,如果找到了,返回指定数据的下标;如果没找到,返回无效值(如-1)
cs
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
}
}
return -1;
}
4.全部代码的呈现
4.1SeqList.h
cs
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include "Contact.h"
typedef int SLDataType;
//typedef peoInfo SLDataType;
#define MAX 100
//typedef struct SeqList
//{
// SLDataType arr[MAX];
// int size;
//}SL;
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;
}SL;
void SLInit(SL* ps);
void SLDestory(SL* ps);
void SLprint(SL s);
void SLCheckCapacity(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);
int SLFind(SL* ps, SLDataType x);
4.2SeqList.c
cs
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLDestory(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLprint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
void SLCheckCapacity(SL* ps)
{
if (ps->capacity == ps->size)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = realloc(ps->arr, newCapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
--ps->size;
}
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(ps->size);
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
}
}
return -1;
}
4.3test.c
cs
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void SLTest01()
{
SL sl;
SLInit(&sl);
//增删查改
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);
SLprint(sl);
SLPushFront(&sl, 5);
SLPushFront(&sl, 6);
SLprint(sl);
SLPopBack(&sl);
SLPopBack(&sl);
SLprint(sl);
SLPopFront(&sl);
SLprint(sl);
SLDestory(&sl);
}
int main()
{
SLTest01();
return 0;
}
结语:
这篇文章全文由作者手写,图片由画图软件所制,无AI制作,希望各位博友能有所收获
欢迎各位博友的讨论,觉得不错的小伙伴,别忘了点赞关注哦~
