目录
- 前言
- 顺序表的介绍
- 静态顺序表
- 动态顺序表
- 一.顺序表的初始化
- 二.销毁
- 扩容顺序表
- 打印顺序表
- 三.头插
- 四.尾插
- 五.头删
- 六.尾删
- 七.指定位置之前(包括指定位置)的插入
- 八.指定位置数据的删除
- 九.查找
- 全部的代码实现
- 总结
前言
数据结构是什么?
数据结构是两个词的集合
数据:是一系列的在杂乱无章的数据的集合
结构:是有条理,清晰的集合(例如目录)
数据结构就是一些有条理数据的集合
数据结构是计算机存储和组织数据的方式
顺序表的介绍
顺序表是线性表的一种
那么顺序表和线性表到底是什么呢?
线性表:具有相同特性的数据结构的集合
例如:蔬菜:黄瓜,白菜,莲藕
相同特性:蔬菜
顺序表的底层是数组,但是在数组的基础上有增删查改等方法,就变成了数据结构
线性表有物理结构和逻辑结构
线性表:
物理结构:不一定是连续的
逻辑结构:一定是连续的
物理结构:例如数组的下标一定是连续的
逻辑结构:例如商店门口排队的人,人排队的位置不一定是一条直线,但是在逻辑上是一条直线
顺序表的物理结构一定是连续的,
逻辑结构也一定是连续的
因为顺序表的底层是数组,数组的物理结构和逻辑结构一定是连续的
顺序表需要的基础有:
动态内存管理
指针
结构体
写顺序表先创建三个文件
一个测试文件(.c):测试顺序表的方法
一个顺序表的头文件(.h):顺序表的结构,声明顺序表的方法
一个顺序表的源文件(.c):实现顺序表的方法
静态顺序表
#define N 100
//100可以用N代替,后续可以方便修改数据
struct SeqList
{
int arr[N];//定长的数组
int size;//顺序表当前有效的数据个数
};
数组给大了,空间浪费
数组给小了,空间不够
静态顺序表的缺点还是比较多的
动态顺序表
struct SeqList
{
int* arr;//指向顺序表的指针
int size;//顺序表中有效数据个数
int capacity;//顺序表的空间大小
};
动态顺序表可以动态增加顺序表的大小(动态增容)
防止了空间的大量浪费
也防止了空间的不足
所以下面我们来实现动态顺序表
一.顺序表的初始化
//SeqList.c
#include"SeqList.h"
//初始化顺序表
void SLInit(SL* ps)
{
ps->arr = NULL;//将数组置为NULL
ps->size = 0;//有效数据为0
ps->capacity = 0;//空间大小为0
}
二.销毁
//SeqList.c
//销毁顺序表
void SLDestroy(SL* ps)
{
//如果顺序表不为空,说明arr中还有动态申请的空间
//要把动态申请的空间free掉
if (ps->arr != NULL)
{
free(ps->arr);
}
ps->arr = NULL;
//顺序表不用了,置为NULL
ps->size = ps->capacity = 0;
//销毁完有效元素和空间为0
}
扩容顺序表
后面需要使用到,所以提前说
所以要用realloc增容,而不是malloc
//二倍或三倍地扩容
//扩容顺序表
void SLCheck(SL* ps)
{
assert(ps);
if(ps->capacity == ps->size)
//看看空间够不够
{
//三目操作符判断给4个空间,还是直接增容2倍
int NewCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
DataType* tmp = realloc(ps->arr, sizeof(DataType) * NewCapacity);
//用新的指针接收,防止增容失败
if (tmp == NULL)
{
perror("realloc");
return;//增容失败,提前返回
}
//增容成功
ps->arr = tmp;
ps->capacity = NewCapacity;
}
}
打印顺序表
//打印顺序表
void SLPrint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
//打印一行就换行
}
三.头插
//头插
void SLPushFront(SL* ps,DataType x)
{
assert(ps);
SLCheck(ps);
//增容
int i = 0;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i-1];
//最后一次: = 1 ,arr[1] = arr[0]
}
ps->arr[0] = x;
// 0下标的位置,放入插入的元素
(ps->size)++;
//插入一个数据,有效元素加一
}
四.尾插
//尾插
void SLPushBack(SL* ps,DataType x)
{
assert(ps);
SLCheck(ps);
//判断空间够不够
ps->arr[ps->size] = x;
// == arr[size] = 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];
// arr[size-2] = arr[size-1]
}
(ps->size)--;
//删除一个元素,有效元素少一个
}
六.尾删
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
//指向顺序表的指针不能为空指针
//为NULL,停止运行
//不为NULL,继续运行
assert(ps->size);
//顺序表不能为空
(ps->size)--;
//删除一个元素,有效元素少一个
}
七.指定位置之前(包括指定位置)的插入
//指定位置之前插入元素
//pop:下标
//x:要插入的元素
void SLInsert(SL* ps, int pop, DataType x)
{
assert(ps);
assert(pop >= 0 && pop <= ps->size);
// pop == size就是尾插
// pop < size 在中间插入
// pop == 0 头插
SLCheck(ps);
for (int i = ps->size; i > pop; i--)
{
ps->arr[i] = ps->arr[i - 1];
// arr[pop+1] = arr[pop]
}
ps->arr[pop] = x;
(ps->size)++;
}
八.指定位置数据的删除
//指定位置之前的删除
void SLEarse(SL* ps, int pop)
{
assert(ps);
assert(pop >= 0 && pop < ps->size);
for(int i = pop;i < ps->size - 1;i++)
{
ps->arr[i] = ps->arr[i+1];
//arr[size-2] = arr[size-1]
}
(ps->size)--;
//删除一个元素,有效元素减一
}
九.查找
//查找
int SLCheck1(SL* ps, DataType x)
// x是要找的数据
{
assert(ps);
assert(ps->size > 0);
//至少有一个元素才能够查找
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
//找到了,返回下标
}
}
return -1;
}
全部的代码实现
// test.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void SLtest()
{
SL sl;
SLInit(&sl);//传址调用
//不能使用传值调用,因为这里sl并没有值
//传址调用,改变指针指向的内容,给指向内容初始化
SLPushFront(&sl, 1);
SLPushFront(&sl, 2);
SLPrint(sl);//2 1
//SLPushBack(&sl, 12);
//SLPopFront(&sl);
//SLPopBack(&sl);
//SLInsert(&sl, 2, 10);
//SLEarse(&sl, 1);
int find = SLCheck1(&sl, 2);
if (find < 0)
{
printf("找不到\n");
}
else
{
printf("找到了,下标是:%d\n", find);
}
SLPrint(sl);
SLDestroy(&sl);
}
int main()
{
SLtest();
return 0;
}
// SeqList.h文件
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;//顺序表可以使用多种数据类型
typedef struct SeqList
{
DataType* arr;
int size;//有效数据
int capacity;//空间大小
}SL;//将struct SeqList 改名为SL,更为方便简洁
void SLInit(SL* p);//初始化
void SLDestroy(SL* sl);//销毁
void SLPushFront(SL* sl,DataType x);//头插
void SLPrint(SL sl);//打印顺序表
void SLPushBack(SL* sl, DataType x);//尾插
void SLPopFront(SL* sl);//头删
void SLPopBack(SL* sl);//尾删
void SLInsert(SL* sl,int pop,DataType x);//指定位置之前的插入
void SLEarse(SL* sl, int pop);//指定位置之前的删除
int SLCheck1(SL* sl, DataType x);//查找
// SeqList.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
//初始化顺序表
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->size = 0;//有效数据为0
ps->capacity = 0;//空间大小为0
}
//销毁顺序表
void SLDestroy(SL* ps)
{
//如果顺序表不为空,说明arr中还有动态申请的空间
//要把动态申请的空间free掉
if (ps->arr != NULL)
{
free(ps->arr);
}
ps->arr = NULL;
//顺序表不用了,置为NULL
ps->size = ps->capacity = 0;
//销毁完有效元素和空间为0
}
//二倍或三倍地扩容
//扩容顺序表
void SLCheck(SL* ps)
{
assert(ps);
if(ps->capacity == ps->size)
//看看空间够不够
{
//三目操作符判断给4个空间,还是直接增容2倍
int NewCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
DataType* tmp = realloc(ps->arr, sizeof(DataType) * NewCapacity);
//用新的指针接收,防止增容失败
if (tmp == NULL)
{
perror("realloc");
return;//增容失败,提前返回
}
//增容成功
ps->arr = tmp;
ps->capacity = NewCapacity;
}
}
//头插
void SLPushFront(SL* ps,DataType x)
{
assert(ps);
SLCheck(ps);
//增容
int i = 0;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i-1];
//最后一次: = 1 ,arr[1] = arr[0]
}
ps->arr[0] = x;
// 0下标的位置,放入插入的元素
(ps->size)++;
//插入一个数据,有效元素加一
}
//打印顺序表
void SLPrint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
//尾插
void SLPushBack(SL* ps,DataType x)
{
assert(ps);
SLCheck(ps);
//判断空间够不够
ps->arr[ps->size] = x;
// == arr[size] = 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];
// arr[size-2] = arr[size-1]
}
(ps->size)--;
//删除一个元素,有效元素少一个
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
//指向顺序表的指针不能为空指针
//为NULL,停止运行
//不为NULL,继续运行
assert(ps->size);
//顺序表不能为空
(ps->size)--;
//删除一个元素,有效元素少一个
}
//指定位置之前插入元素
//pop:下标
//x:要插入的元素
void SLInsert(SL* ps, int pop, DataType x)
{
assert(ps);
assert(pop >= 0 && pop <= ps->size);
// pop == size就是尾插
// pop < size 在中间插入
// pop == 0 头插
SLCheck(ps);
for (int i = ps->size; i > pop; i--)
{
ps->arr[i] = ps->arr[i - 1];
// arr[pop+1] = arr[pop]
}
ps->arr[pop] = x;
(ps->size)++;
}
//指定位置之前的删除
void SLEarse(SL* ps, int pop)
{
assert(ps);
assert(pop >= 0 && pop < ps->size);
for(int i = pop;i < ps->size - 1;i++)
{
ps->arr[i] = ps->arr[i+1];
//arr[size-2] = arr[size-1]
}
(ps->size)--;
//删除一个元素,有效元素减一
}
//查找
int SLCheck1(SL* ps, DataType x)
{
assert(ps);
assert(ps->size > 0);
//至少有一个元素才能够查找
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
//找到了,返回下标
}
}
return -1;
}
总结
总的来说顺序表的实现还是比较简单的
主要难的地方就是扩容,for循环的结束条件要想一下