目录
顺序表的概念及结构
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指 用一组地址连续的存储单元依次存储线性表中的各个元素、是的线性表中在逻辑结构上相邻的数据元素 存储在相邻的物理存储单元中,即通过数据元素物理存储的 相邻关系来反映数据元素之间 逻辑上的相邻关系,采用顺序存储结构的线性表通常被称为顺序表。
顺序表可以存储各种数据类型,不管是整型、字符类型以及自定义类型都能够存储。
顺序表是将表中的节点依次存放在计算机内存中一组地址连续的存储单元中。
顺序表的底层是数组。
顺序表的分类:静态顺序表和动态顺序表
静态顺序表:使用定长数组存储元素
#define N 7
struct SeqList
{
int a[N]; //定长数组
int size; //有效数据个数
}SL;
动态顺序表:能够按需求申请空间,后面还可以增加容量。
struct SeqList
{
int* a;
int size; //有效数据个数
int capacity;//空间容量
}SL;
动态顺序表的实现
创建结构体
首先,创建一个结构体
typedef int datatype;
typedef struct SeqList
{
datatype* a;
int size; //有效数据个数
int capacity;//空间容量
}SL;
为了方便存储各种数据,我将类型int重命名成dataype,以后如果要存储char类型的数据,直接将int改成char就行了。
size记录存储数据的有效个数,capacity用来记录申请的空间容量,将结构体struct SeqList 给个名字叫SL。
顺序表的初始化
void Init(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
这里解释一下为什么传参是结构体指针。因为我们要对结构体成员进行初始化,那必然和结构有关,但是要注意传值和传址的区别。我们在初始化之前结构体成员中什么内容都没有,传值会报错。用结构体指针指向结构体,也就是这里的ps指向的是顺序表。
尾插数据
void InsertBack(SL* ps, datatype x)
{
assert(ps);
//先看空间够不够
if (ps->capacity == ps->size)
{
//空间不够,申请空间
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
datatype* tmp = (datatype*)realloc(ps->arr, newcapacity * 2 * sizeof(datatype));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
//插入数据
ps->arr[ps->size] = x;
++ps->size;
//也可以写成 ps->arr[ps->size++] = x
}
什么是尾插数据?假如说我们已经有了这么一组数据
尾插就是在size这个位置上插入一个数据。
插入数据之前,我们需要判断我们的空间够不够。
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
创建一个新的变量newcapacity来记录capacity的空间是否满足,因为我们初始化的时候size和capacity都为0,所以我们用三目操作符来初始。
如果空间不够的话,需要用realloc函数取增容空间的大小。增容一般以2到3倍取增容。
因为realloc这个函数如果空间申请失败,它会返回一个空指针NULL,所以我们防止空间申请失败时把原来的数据清除掉,创建了一个指针tmp,将申请到的空间给这个指针变量,然后再给arr数组,最后capacity也要更新一下。然后再size这个位置插入数据。size大小也要增大。
前插数据
什么是前插?先让数据整体后移一位,然后再最前面插入数据
void InsertFront(SL* ps, datatype x)
{
assert(ps);
//先看空间够不够
if (ps->capacity == ps->size)
{
//空间不够,申请空间
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
datatype* tmp = (datatype*)realloc(ps->arr, newcapacity * 2 * sizeof(datatype));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
我们观察到,每次都要检查空间够不够和增大空间容量,所以我们可以把检查空间容量和增大空间容量封装成一个函数。
检查空间的函数:
void checkspace(SL* ps)
{
//先看空间够不够
if (ps->capacity == ps->size)
{
//空间不够,申请空间
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
datatype* tmp = (datatype*)realloc(ps->arr, newcapacity * 2 * sizeof(datatype));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
尾删数据
尾删数据就有点简单了。只有保证顺序表不为空就行了。
void DeleteBack(SL* ps)
{
assert(ps);
assert(ps->size);
--ps->size;
}
头删数据
void DeleteFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];//arr[size-2] = arr[size-1];
}
ps->size--;
}
在指定位置插入数据
由于数据在数组中的位置是以0,1,2,3,4这样存储的,因为是在指定位置之前插入数据,所以指定的位置不能大于有效数据个数,也不能小于或等于0。
在插入数据之前,也要先检查空间够不够,空间不够就进行增容。
void SpecityInsert(SL* ps, int pos, datatype x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
//查看空间够不够
checkspace(ps);
//后移一位
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i-1];
}
ps->arr[pos] = x;
ps->size++;
}
在指定位置删除数据
直接用后面的数据覆盖前面的数据,有效数据个数-1.
void DeleteInsert(SL* ps, int pos)
{
assert(ps);
assert(pos < ps->size);
//前移一位
for (int i = pos; i < ps->size; i++)
{
//把后面的数据往前挪一位
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
顺序表的打印
void Print(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
我们前面已经将数据填入顺序表中了,这里就可以直接传值。
顺序表的销毁
void destroy(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
这里如果arr数组里面还有内容,就释放掉arr的空间,同时还要将arr、size和capacity初始化。
为什么要销毁?不销毁的话,你每调用一次这个程序,就会多占用一次空间,要知道空间是有限的。你向操作系统调用了空间,你还要还给操作系统。
全部代码实现
SeqList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int datatype;
typedef struct SeqList
{
datatype* arr;
int size; //有效数据个数
int capacity;//空间容量
}SL;
//顺序表的初始化
void Init(SL* ps);
//顺序表的销毁
void destroy(SL* ps);
//打印顺序表
void Print(SL s);
//前插数据
void InsertFront(SL* ps, datatype x);
//尾插数据
void InsertBack(SL* ps, datatype x);
//尾删数据
void DeleteBack(SL* ps);
//头删数据
void DeleteFront(SL* ps);
//在指定位置前插入数据
void SpecityInsert(SL* ps, int pos, datatype x);
//在指定位置之前删除数据
void DeleteInsert(SL* ps, int pos);
SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Seqlist.h"
//检查空间和增大空间
void checkspace(SL* ps)
{
//先看空间够不够
if (ps->capacity == ps->size)
{
//空间不够,申请空间
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
datatype* tmp = (datatype*)realloc(ps->arr, newcapacity * 2 * sizeof(datatype));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//空间申请成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
//顺序表的初始化
void Init(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//顺序表的销毁
void destroy(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//尾插数据
void InsertBack(SL* ps, datatype x)
{
assert(ps);
checkspace(ps);
//插入数据
ps->arr[ps->size] = x;
++ps->size;
//也可以写成 ps->arr[ps->size++] = x
}
//前插数据
void InsertFront(SL* ps, datatype x)
{
assert(ps);
checkspace(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
//打印顺序表
void Print(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
//尾删数据
void DeleteBack(SL* ps)
{
assert(ps);
assert(ps->size);
--ps->size;
}
//头删数据
void DeleteFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];//arr[size-2] = arr[size-1];
}
ps->size--;
}
//在指定位置之前插入数据
void SpecityInsert(SL* ps, int pos, datatype x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
//查看空间够不够
checkspace(ps);
//后移一位
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i-1];
}
ps->arr[pos] = x;
ps->size++;
}
//在指定位置之前删除数据
void DeleteInsert(SL* ps, int pos)
{
assert(ps);
assert(pos < ps->size);
//前移一位
for (int i = pos; i < ps->size; i++)
{
//把后面的数据往前挪一位
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Seqlist.h"
void text()
{
SL s;
Init(&s);
//尾插数据
InsertBack(&s, 1);
InsertBack(&s, 2);
InsertBack(&s, 3);
InsertBack(&s, 4);
//在指定位置之前插入数据
SpecityInsert(&s, 2, 5);
SpecityInsert(&s, 3, 6);
Print(s);
//在指点位置删除数据
DeleteInsert(&s, 1);
Print(s);//1 2 3 4
destroy(&s);
}
int main()
{
text();
return 0;
}
这个是用来测试顺序表功能能否实现。