引言-线性表
线性表:
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构,也就是说是连续的一条直线。但在物理上并不一定是连续的。线性表在物理上存储时,通常以数组和链式结构的形式存储。
我们今天的主角,顺序表和链表,其实都是线性表,当然线性表不止包含这两个
线性表:
- 顺序表
- 链表
- 栈
- 队列
- 字符串
- ……
再次声明:线性表的逻辑结构是线性的,物理结构不一定是线性
顺序表
概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1.静态顺序表:使用定长存储元素
2.动态顺序表:使用动态开辟数组的存储
来跟我一起手搓个顺序表吧
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们手搓动态顺序表。
我们先写一个头文件,里面写好我们维护的动态顺序表以及要实现的接口函数
结构及接口Sqlist.h
//Sqlist.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<assert.h>
#define INIT_CAPACITY 4
typedef int SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
SLDataType* a;//指向动态开辟数组
int size; // 有效数据个数
int capacity; // 空间容量
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//顺序表打印
void SLPrint(SL* ps);
//扩容
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);
往下就可以开始实现我们的顺序表内容了,下面对于接口的实现放在 Sqlist.c 中
初始化和销毁
void SLInit(SL* ps)
{
ps->a = NULL; //开始时,给一个空指针
ps->capacity = ps->size = 0;
}
void SLDestroy(SL* ps)
{
assert(ps); //断言,防止ps为空指针
ps->capacity = ps->size = 0;
free(ps->a);
ps->a = NULL;
}
顺序表打印
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++) {
printf("%d\n",ps->a[i]);
}
printf("\n");
}
这里需要注意的是,在打印过程中,往顺序表中放置的数据类型不同,所打印的方式也会有所不同,在头文件Sqlist.h中
typedef int SLDataType;
这句代码说明放入的数据类型是int,所以我这里就使用int的打印方式了。
扩容
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity) {
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
//防止开辟空间失败返回空指针
if (tmp == NULL) {
perror("malloc fail:");
exit(1);
}
ps->a = tmp;
//更新容量
ps->capacity = newCapacity;
}
}
扩容的部分在整个动态顺序表中占据非常重要的地位,关系到堆中空间的开辟,保证后续数据操作的顺利进行。
头部插入删除和尾部插入删除数据
//头部插入删除
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);//保证插入时不会越界
for (int i = ps->size; i > 0; i--) {
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
void SLPopFront(SL* ps)
{
assert(ps);