目录
1 线性表
线性表是n个具有相同特性的数据元素的有限序列。
常见的有:顺序表、链表、栈、队列、字符串....
特点:
-
线性表在逻辑上是线性结构的,即一条连续的直线
-
但在物理结构上不一定是连续的,其在物理存储时,通常以数组和链式结构的形式存储
2 顺序表
使用一段地址连续的存储单元依次存储数据元素的线性结构
顺序表就是数组,但在数组基础上,要求数据从头开始且连续存储,不能跳跃或间隔
2.1 缺点
-
空间不够需要扩容,而扩容会付出一定代价
原地扩容
还是原地址,代价低,至少不需要拷贝原数据
异地扩容
已经改变
-
避免频繁扩容:平常空间满了扩2倍,可能会导致一定的空间浪费 如100->200,但实际我们只需要120个空间,浪费80个
-
要求数据从头开始连续存储,那么我们在头部或中间位置插入数据需要挪动数据,效率低
2.2 优点
-
支持随机访问
-
对于有些算法,需要结构支持随机访问,如二分查找,优化后的快排等
2.3 示例
SeqList.h
头文件
2.3.1 静态顺序表
#define N 1000
typedef int SLDataType; // 以后需要存储不同类型的数据,可以直接修改
typedef struct SeqList
{
SLDataType a[N];
int size; // 表示数组中存储了多少个数据
}SL;
// 接口函数 - 函数名是根据STL风格
void SeqListInit(SL* ps); // 初始化数组
// 静态:容量给小则不够用,给大则浪费空间
void SeqListPushBack(SL* ps, SLDataType x); //尾插
void SeqListPopBack(SL* ps); //尾删
void SeqListPushFront(SL* ps, SLDataType x);//头插
void SeqListPopFront(SL* ps); //头删
2.3.2 动态顺序表
typedef int SLDataType;
// 动态顺序表
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity; // 数组实际能存储的空间容量是多少个
}SL;
void SeqListInit(SL *ps); // 形参 是实参的临时拷贝,不会影响实参 - 传值操作
// 所以传地址
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
SeqList.c
源文件
#pragma once
#include "SeqList.h"
void SeqListInit(SL*ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SeqListPushBack(SL* ps, SLDataType x)
{
// 如果没有空间(0个)或空间不足(满了)
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
// 三目操作符 是否等于0,是给4,否则给2倍原空间
SLDataType* tmp = (SLDataType * )realloc(ps->a, sizeof(SLDataType) * newcapacity);
// 开辟空间
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp; // 开辟的空间给结构体
ps->capacity = newcapacity;// 开辟的大小给原大小
}
ps->a[ps->size] = x; // 要传的值给结构体
ps->size++;
}
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
Source.c
文件
#include"SeqList.h"
void TestSeqList1()
{
SL sl;
SeqListInit(&sl);
}
int main()
{
TestSeqList1();
return 0;
}
注意:传结构体不能传值,必须传址,否则无法修改里面内容(形参是实参的临时拷贝,形参的改变不影响实参)
2.3.3 接口函数
2.3.3.1 尾删数据
注意此处,未加判断条件,size–
将可以为负值(-1,-2…)
而在–负数
后,再增加数据,会造成越界访问
在销毁时会报错
纵上
-
方法1:加上判断条件,空间大于
0
才可–
-
方法2:断言,小于等于
0
直接报错
2.3.3.2 头插数据
数据向后挪动,空出前面的位置
同尾删数据,未判断大小,若添加数据过多大于有效空间大小,将造成越界访问,报错
多处用到扩容,这里直接封装成函数
若空间满了则扩容
2.3.3.3 头删数据
和尾插数据类似
从第二个数据开始一个一个放上去
3 动态顺序表的实现
3.1 顺序表的创建
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity; // 数组实际能存储的空间容量是多少个
}SL;
3.2 接口函数的声明
void SeqListInit(SL *ps); // 初始化顺序表
// 形参 是实参的临时拷贝,不会影响实参 - 传值操作
// 所以传地址
void SeqListDestory(SL* ps); // 使用完后销毁,防止内存泄露
void SeqListCheckCapacity(SL* ps); // 检查容量+扩容
void SeqListPrint(SL* ps); // 打印数据
void SeqListPushBack(SL* ps, SLDataType x); // 尾插
void SeqListPopBack(SL* ps); // 尾删
void SeqListPushFront(SL* ps, SLDataType x); // 头插
void SeqListPopFront(SL* ps); // 头删
int SeqListFind(SL* ps, SLDataType x); // 找到x返回下标,未找到返回-1
void SeqListInsert(SL* ps, int pos, SLDataType x); // 指定pos位置插入x
void SeqListErase(SL* ps, int pos); // 删除pos位置的数据
3.3 初始化
void SeqListInit(SL*ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
3.4 销毁顺序表
void SeqListDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
3.5 找x,找到返回下标,未找到返回-1
int SeqListFind(SL* ps, SLDataType x)
{
int begin = 0;
while (begin++ <= ps->size)
{
if (ps->a[begin] == x)
return begin;
}
return -1;
}
3.6 指定pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
assert(pos <= ps->size && pos >= 0);
// 判断+扩容
SeqListCheckCapacity(ps);
// 挪动数据,从后往前
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
// 插入数据
ps->a[pos] = x;
ps->size++;
}
3.7 删除pos位置的数据
void SeqListErase(SL* ps, int pos)
{
assert(pos < ps->size && pos >= 0);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
3.8 尾插数据
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListInsert(ps, ps->size, x);
}
3.9 打印顺序表
void SeqListPrint(SL* ps)
{
int i = 0;
for (i; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
3.10 检查并扩容
void SeqListCheckCapacity(SL* ps)
{
// 如果没有空间(0个)或空间不足(满了)
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
// 三目操作符 是否等于0,是给4,否则给2倍原空间
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * newcapacity);
// 开辟空间
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1); // 异常终止程序
}
ps->a = tmp; // 开辟的空间给结构体
ps->capacity = newcapacity;// 开辟的大小给原大小
}
}
3.11 尾删数据
void SeqListPopBack(SL* ps)
{
SeqListErase(ps, ps->size-1);
}
3.12 头插数据
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListInsert(ps, 0, x);
}
3.13 头删数据
void SeqListPopFront(SL* ps)
{
SeqListErase(ps, 0);
}