数据结构第二课
1. 前言
想看顺序表所有代码的同学,我的码云放在了最后
2. 线性表
我们要讲的顺序表本质上就是一个数组,通过数组形式来存储数据的结构,但是在数组的基础上,它要求数据从头开始存,并且是连续存储,不能跳跃间隔
3. 顺序表
3.1 概念以及结构
我们通常还讲顺序表分为两种:
- 静态顺序表
- 动态顺序表
3.11 静态顺序表
3.12 动态顺序表
4. 顺序表的实现
4.1 顺序表内容的命名
- SeqListPushBack(尾插)
- SeqListPopBack(尾删)
- SeqListPushFront(头插)
- SeqListPopFront(头删)
- SeqListDestory(销毁顺序表)
- SeqListInit(初始化链表)
4.2 初始化结构
像这种比较正式的代码我们都不会在一个.c文件中完成.在我们编写代码之前,我们需要创建三个文件,分别是:
- test.c文件—用来测试你写的顺序表能不能用
- SeqList.c文件—函数的代码实现
- SeqList.h文件—函数的声明和库函数的包含
我们先来定义一个结构体:
struct SeqList
{
int* a;//创建的数组用来存储数据
int size;//size代表数组中存储了多少个有效数据
int capacity;//表示数组实际能存储的空间有多大
};
#pragma once//在.h文件中操作
#include<stdio.h>//包含常见的头文件
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>
typedef int SLDataType;//想存什么类型就把int改成什么
//
typedef struct SeqList
{
SLDataType* a;
int size;//size代表数组中存储了多少个有效数据
int capacity;//表示数组实际能存储的空间有多大
}SL;//将struct SeqList重命名为SL,方便后面写代码
4.3 初始化函数
void SeqListInit(SL* ps)//初始化
{
ps->a = NULL;//这里代表数组里面最开始什么都不放
ps->size = ps->capacity = 0;//容量和空间都为0
}
写完这个函数后,我们就可以在test,c文件中先调用一下它,看看有没有问题
#include"SeqList.h"//包含头文件
void TestSeqList1()//这里我们在test.c文件中创建了一个测试函数,它的目的是为了测试我们写的SeqList.c文件中的代码是否正确
{
SL s1;//定义一个结构体(SL为重命名前的struct SeqList)
SeqListInit(&s1);
}
int main()
{
TestSeqList1();
//TestSeqList2();
return 0;
}
4.4 扩容函数
void SeqListCheckCapacity(SL* ps) //判断是否需要扩容
{
if (ps->size == ps->capacity)//两种情况,一种为顺序表没有空间,一种为空间不够(capacity已经满了)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//当空间为0时给四个空间,不为0时将原先空间乘2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));//当我们的数组a的空间为0时,realloc的功能相当于malloc
if (tmp == NULL)//判断动态开辟空间是否成功,如果不成功就退出程序
{
exit(-1);
}
ps->a = tmp;//将数组a指向我们开辟成功的这份空间
ps->capacity = newcapacity;//将数组的容量赋值为新的容量
}
}
4.5 尾插函数
在我们初始化之后,就可以开始我们的尾插了,先上代码再解释:
( (如果你不理解为什么要传结构体变量的地址,别慌,后面会讲) )
void SeqListPushBack(SL* ps, SLDataType x)//尾插
{
assert(ps!=NULL);//传进来的ps结构体不能为空
SeqListCheckCapacity(ps);//这个地方我们只需要一级指针,传ps而不是&ps,&ps是二级指针了
ps->a[ps->size] = x;//size位置刚好是数组最后一个元素的后一个位置
ps->size++;//尾插后讲有效数据加1
}
在我们尾插之前,我们一定要判断我们的数组存储的数据是不是已经满了(甚至是不是没有空间)
我们现在可以在test.c中测试一下我们写的尾插函数有没有问题:
#include"SeqList.h"
void TestSeqList1()
{
SL s1;//定义一个结构体
SeqListInit(&s1);
SeqListPushBack(&s1,1);//尾插1 2 3 4
SeqListPushBack(&s1,2);
SeqListPushBack(&s1,3);
SeqListPushBack(&s1,4);
}
int main()
{
TestSeqList1();
return 0;
}
写完后如果你想看你是否插入成功了,我们可以取调试窗口查看,但是我觉得这样每次都去看调试很麻烦,所以我写一个打印函数,可以讲数组的内容打印出来
4.6 打印函数
void SeqListPrint(SL* ps)//打印
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
写完打印函数后我们再去验证一下上面写的尾插:
#include"SeqList.h"
void TestSeqList1()
{
SL s1;//定义一个结构体
SeqListInit(&s1);
SeqListPushBack(&s1,1);//尾插1 2 3 4
SeqListPushBack(&s1,2);
SeqListPushBack(&s1,3);
SeqListPushBack(&s1,4);
SeqListPrint(&s1);
}
int main()
{
TestSeqList1();
return 0;
}
我们的1 2 3 4 就被打印出来了:
4.7 尾删函数
先上代码再解释:
void SeqListPopBack(SL* ps)//尾删
{
assert(ps);//ps不能为null
assert(ps->size > 0);//size必须大于0,不然是没有数据可以删除的,并且size--后,
//size会等于负数,下次再使用ps->a[size]时会报错
ps->size--;//直接将size减1,我们的数组就访问不到最后一个数据了
}
这里大家可以自己去测试一下我们的尾删代码:
4.8 头插函数
void SeqListPushFront(SL* ps, SLDataType x)//头插
{
assert(ps);
SeqListCheckCapacity(ps);//判断是否需要扩容
int end = ps->size-1;//将end指向数组的最后一个元素
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];//将前面的数据往后挪动,而且必须是从最后一个数据开始挪
end--;
}
ps->a[0] = x;//将插入的数据放在第一个位置
ps->size++;//将有效数据加1
}
这里我还是说明一下为什么我们挪动数据的时候要从后面开始挪动:
这里大家可以用test.c文件去测试一下:
4.9 头删函数
和尾删一样,头删之前也要判断我们的顺序表是否为空.
void SeqListPopFront(SL* ps)//头删
{
assert(ps);
assert(ps->size > 0);
for (int i = 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];//将后面的数据往前挪动,并且要从最开头开始挪动,原因和我们之前的头插是一样的
}
ps->size--;//挪动完后将size减1
}
4.10 销毁顺序表
当我们使用完顺序表表后,需要将它销毁掉,并且下次使用时再重新初始化:
void SeqListDestory(SL* ps)//销毁顺序表
{
free(ps->a);//把a指向的空间释放掉
ps->a = NULL;//将指针a置空
ps->size = 0;//将size和capacity都置空
ps->capacity = 0;
}
5. 写代码时应该注意的点
6. 顺序表问题思考以及题目推荐
问题:
1. 顺序表的中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们
再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
思考:如何解决以上问题呢? 我们下一章将会为大家揭晓!
相关题目:
- 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。OJ题链接
- 删除排序数组中的重复项。OJ题链接
- 合并两个有序数组。OJ题链接
7. 总结
我的码云:gitee:杭电码农