1.什么是顺序表
顺序表是用一段物理地址连续的单元依次存储数据元素的线性结构,一般情况下采用数组来存储 顺序表的底层结构就是数组。实际上顺序表是对数组进行封装,成为一个结构体。顺序表有静态顺序表与动态顺序表之分。
1.1静态顺序表
//静态顺序表
typedef int SLDateType;//方便后续的更改数据类型
#define N 7
typedef struct SeqList
{
SLDataType a[N];//定长数组
int size;//有效数据个数
}SL;
0 | 1 | 2 | 3 | 4 | 5 | 6 |
静态顺序表的缺点:空间给小了不够用,给大了会造成空间浪费。这个时候当空间小时,需要自动扩容,但又不至于扩容太大而造成空间浪费的一个操作应该怎么进行呢?不用担心,那么这时候动态顺序表的出现,就可以解决这个难题了。
1.2动态顺序表
动态顺序表可以根据输入的数据,以及后续的增删除改操作从而判断是否需要增容,达到自动增容的效果,但又不至于因增容过多而造成空间浪费。所以在顺序表中一般使用动态顺序表,便于后期的维护。
typedef int SeqDatetype;//便于后期的更改维护
typedef struct Seqlist
{
SeqDatetype* arr;//顺序表存放的类型
int size;//顺序表的有效数据个数
int capacity;//顺序表的容量
}SL;
2增容
2.1如何增容
增容一般是以2~3倍之前的空间大小来进行增容操作。虽然这样仍会存在空间浪费,但是相比定长一个巨大的空间还是显得更划算。而且一个一个增容的话会频繁使程序进行扩容操作,从而导致程序的运行效率低下。
2.2增容的两种情况
3.一个动态顺序表的使用
3.1个文件的设计
3.1.2Seqlist.h
Seqlist.h文件中存放的是顺序表的核心内容以及对顺序表进行增删操作等一些主要函数的声明
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SeqDatetype;//便于后期的更改维护
typedef struct Seqlist
{
SeqDatetype* arr;//顺序表存放的类型
int size;//顺序表的有效数据个数
int capacity;//顺序表的容量
}SL;
void SeqInit(SL *ps);//初始化顺序表
void SeqDelete(SL *ps);//销毁顺序表
void SLPrint(SL* ps);//打印顺序表
void SLcheckcapacity(SL* ps);//检查顺序表容量是否充足,不充足则对容量capacity进行翻倍
void SLPushBack(SL* ps, SeqDatetype x);//尾插一个SeqDatetype数据
void SLPushFront(SL* ps, SeqDatetype x);//头插一个数据在顺序表中
void SLInsert(SL* ps, SeqDatetype x, int pos);//任意位置插入
void SLPopBack(SL* ps);//尾删
void SLPopFront(SL* ps);//头删
void SLErase(SL* ps, SeqDatetype x, int pos);//任意位置删除
3.1.2Seqlist.c
Seqlist.c文件中存放的是增删顺序表等函数的具体实现方法
#include "Seqlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void SeqInit(SL* ps)//初始化顺序表
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SeqDelete(SL* ps)//销毁顺序表
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->size = 0;
printf("销毁成功");
}
void SLcheckcapacity(SL* ps)//检查顺序表容量是否充足,不充足则对容量capacity进行翻倍
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//创建一个新变量,防止由于capacity是0而无法增容
SeqDatetype* temp = (SeqDatetype*)realloc(ps->arr, newcapacity * sizeof(SeqDatetype));
//realloc的第二个参数是要申请多少个字节的空间,不是多少个空间,注意单位
if (temp == NULL)
{
perror("realloc申请空间失败");
exit(1);
}
ps->arr = temp;
ps->capacity = newcapacity;
}
}
void SLPushBack(SL* ps, SeqDatetype x)//尾插一个数据在顺序表中
{
assert(ps);//等价于assert(ps!=NULL),用于判断,当ps不等于NULL时可运行
SLcheckcapacity(ps);//判断顺序表容量是否充足,否,则扩容
ps->arr[ps->size] = x;
ps->size++;
}
void SLPushFront(SL* ps, SeqDatetype 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 SLInsert(SL* ps, SeqDatetype x, int pos)//任意位置插入
{
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, SeqDatetype x, int pos)//任意位置删除
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLcheckcapacity(ps);
if (pos == ps->size - 1)
ps->size--;
else
{
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
}
void SLPopBack(SL* ps)//尾删
{
assert(ps);
assert(ps->size);//判断顺序表是否为有效数据size是否为0,若为0,则不可以删除
ps->size--;
}
void SLPopFront(SL* ps)//头删
{
assert(ps);
assert(ps->size);//判断顺序表是否为有效数据size是否为0,若为0,则不可以删除
for (int i = 0; i < ps->size - 1; i++)
ps->arr[i] = ps->arr[i + 1];
ps->size--;
}
void SLPrint(SL* ps)//打印顺序表
{
for (int i = 0; i < ps->size; i++)
printf("%d ", ps->arr[i]);//这里可以优化一下,根据SeqDatetype来确定占位符的类型
puts("");
}
3.2简要介绍顺序表以及各函数功能细分
3.2.1顺序表的介绍
typedef int SeqDatetype;//便于后期的更改维护
typedef struct Seqlist
{
SeqDatetype* arr;//顺序表存放的类型
int size;//顺序表的有效数据个数
int capacity;//顺序表的容量
}SL;
3.2.1.1typedef的妙用
如果根据功能的需要,需要将顺序表中的int类型数据更改为char类型,类型更改势必会把后面的函数的一些数据更改。那么如果进行更改这一操作(int->char),必然会有更多地方需要修改,但是第一行代码就巧妙的运行用了typedef,使得后期的更改只需修改第一行代码中的int即可
3.2.1.2存放类型为指针类型
可以直接存放数据的类型,不需要思考有多少个数据,且传地址的方式可以直接对数组进行操作,而值传递则是拷贝一份原来数组数据再进行操作,浪费空间。
3.2.2初始化顺序表
直接将数组的地址初始化置为为NULL,并将顺序表的容量和有效数据都初始化为0
void SeqInit(SL* ps)//初始化顺序表
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
注意:此处不可使用值传递的方法,需要使用传地址的方法,否则会使顺序表的初始化出现错误
即:是错误的
3.2.3销毁顺序表
每次使用完顺序表之后,需要释放空间(free),将数组地址置空,容量以及有效数据置为0,即完成销毁顺序表的操作
void SeqDelete(SL* ps)//销毁顺序表
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capacity = ps->size = 0;
printf("销毁成功");
}
3.2.4检查顺序表的容量是否充足
检查顺序表的容量是否充足,若不充足,则根据扩容的规则,对顺序表进行二倍扩容操作
void SLcheckcapacity(SL* ps)//检查顺序表容量是否充足,不充足则对容量capacity进行翻倍
{
if (ps->size == ps->capacity)//判断是否充足
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//创建一个新变量,防止由于capacity是0而无法增容
SeqDatetype* temp = (SeqDatetype*)realloc(ps->arr, newcapacity * sizeof(SeqDatetype));
//realloc的第二个参数是要申请多少个字节的空间,不是多少个空间,注意单位
if (temp == NULL)
{
perror("realloc申请空间失败");
exit(1);
}
ps->arr = temp;
ps->capacity = newcapacity;//扩容成功操作
}
}
3.2.5头插、尾插、任意位置插入
由易至复杂来介绍一下SeqDatetype类型数据在顺序表中的插入
若原来的顺序表
SeqDatetype类型数据 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
进行第一次头插操作,比如头插入在顺序表头插一个数据0:
SeqDatetype类型数据 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
这时再进行一次尾插操作,比如在顺序表尾插入一个数据8
SeqDatetype类型数据 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
若对原顺序表进行任意位置插入操作,需要确定插入位置pos,比如再pos=3这个位置插入10
SeqDatetype类型数据 | 1 | 2 | 3 | 10 | 4 | 5 | 6 | 7 |
pos | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
头插具体实现代码
void SLPushFront(SL* ps, SeqDatetype x)//头插一个数据在顺序表中
{
assert(ps);//等价于assert(ps!=NULL),用于判断,当ps不等于NULL时可运行
SLcheckcapacity(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;//将要插入的数据放在最后一个位置
ps->size++;
}
尾插具体实现代码
void SLPushBack(SL* ps, SeqDatetype x)//尾插一个数据在顺序表中
{
assert(ps);//等价于assert(ps!=NULL),用于判断,当ps不等于NULL时可运行
SLcheckcapacity(ps);//判断顺序表容量是否充足,否,则扩容
ps->arr[ps->size] = x;
ps->size++;
}
任意位置(pos)插入具体实现代码
void SLInsert(SL* ps, SeqDatetype x, int pos)//任意位置插入
{
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++;
}
3.2.6头删、尾删、任意位置删除
有了3.2.5的基础应该容易理解接下来的头删、尾删、任意位置删除等操作
头删
void SLPopFront(SL* ps)//头删
{
assert(ps);
assert(ps->size);//判断顺序表是否为有效数据size是否为0,若为0,则不可以删除
for (int i = 0; i < ps->size - 1; i++)
ps->arr[i] = ps->arr[i + 1];
ps->size--;
}
尾删
void SLPopBack(SL* ps)//尾删
{
assert(ps);
assert(ps->size);//判断顺序表是否为有效数据size是否为0,若为0,则不可以删除
ps->size--;
}
任意位置删除
void SLErase(SL* ps, SeqDatetype x, int pos)//任意位置删除
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLcheckcapacity(ps);
if (pos == ps->size - 1)
ps->size--;
else
{
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
}
3.2.7打印顺序表
void SLPrint(SL* ps)//打印顺序表
{
for (int i = 0; i < ps->size; i++)
printf("%d ", ps->arr[i]);//这里可以优化一下,根据SeqDatetype来确定占位符的类型,本示例顺序表的类型为int,若为char类型的话,则占位符为%c
puts("");
}
4.顺序表的用途
顺序表的用途很多,比如在结构体中增加一些内容就可以成为完成一个通讯录的项目实现,还可以在结构体数组中扩建,完成学生管理系统的一些核心设计,用途多多,看你要如何去使用
标签:ps,arr,顺序,int,SL,数据结构,size From: https://blog.csdn.net/2302_80961600/article/details/140803955