目录
1.线性表
线性表是n个具有相同特性的数据元素的有限序列。
常见的线性表:顺序表、链表、栈、队列、字符串......
线性表在逻辑上是线性结构,相当于连续的一条直线。但在物理结构上不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式进行存储。
2.顺序表
2.1顺序表的概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
2.2顺序表的优缺点
优点:
连续的物理空间,方便下标随机访问。
缺陷:
不能按需申请和删除空间
插入数据时,空间不够需要扩容,扩容有性能消耗(realloc空间不够时异地扩容,性能消耗大)
头部或者中间位置插入删除数据,需要挪动数据,效率较低
可能存在一定空间占用,浪费空间
2.3动态、静态顺序表的区分
顺序表一般可以分为:
1.静态顺序表:使用定长数组存储元素
2.动态顺序表:使用动态开辟的数组存储
3.动态顺序表的实现
静态顺序表只适用于确定知道需要多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要的空间动态分配大小,所以我们来实现动态顺序表。
3.1 SeqLIst.h
进行头文件、顺序表结构体的声明、顺序表函数的声明
//防止头文件重复
#pragma once
//包含头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//将存储数据类型重定义方便修改存储数据的类型
//SL(动态顺序表SeqList的缩写) DataType(数据类型)
typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
SLDataType* list;//动态开辟数据存储数据元素
int size; //存储的数据个数
int capacity;//存储空间大小
}SeqList;
//打印
void SeqListPrint(SeqList* psl);
//初始化
void SeqListInit(SeqList* psl);
//检查空间大小是否已满,若已满则扩容
void SeqListCheckCapacity(SeqList* psl);
//顺序表销毁
void SeqListDestroy(SeqList* psl);
//尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
//尾删
void SeqListPopBack(SeqList* psl);
//头插
void SeqListPushFront(SeqList* psl, SLDataType x);
//头删
void SeqListPopFront(SeqList* psl);
//数据查找
int SeqListFind(SeqList* psl, SLDataType x);
//数据插入
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
//数据删除
void SeqListErase(SeqList* psl, size_t pos);
//数据修改
void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
3.2 SeqList.c Test.c
逐个实现顺序表函数并同时进行测试检查
对于函数功能的实现要做到写一个检查一个,千万不要一股脑全部然后写完觉得自己很nb,结果最后进行测试的时候一堆bug,而且难以寻根觅源,折磨的还是你自己。
3.2.1顺序表打印
//打印顺序表
void SeqListPrint(SeqList* psl)
{
//对空指针进行断言,显示出错位置
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->list[i]);
}
printf("\n");
}
3.2.2顺序表的初始化
//动态顺序表的初始化
void SeqListInit(SeqList* psl)
{
assert(psl);
psl->list = NULL;
psl->size = 0;
psl->capacity = 0;
}
3.2.3顺序表销毁
//顺序表销毁
void SeqListDestroy(SeqList* psl)
{
assert(psl);
free(psl->list);
psl->list = NULL;
psl->capacity = psl->size = 0;
}
3.2.4顺序表空间检查扩容
当size与capacity相等时,顺序表已满需要扩容
//顺序表空间扩容
void SeqListCheckCapacity(SeqList* psl)
{
assert(psl);
//当数据个数与存储空间相等时,数组已满
//如果数组满了,使用realloc进行扩容
if (psl->size == psl->capacity)
{
//当capacity为0时为其赋值为4,若不为0则进行倍增
size_t NewCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
//将当前的空间翻倍扩容
SLDataType* tmp = realloc(psl->list, sizeof(SLDataType) * NewCapacity);
//realloc扩容可能失败,需要对其进行检查
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);//终止程序
}
else
{
psl->list = tmp;
psl->capacity = NewCapacity;
}
}
}
3.2.5顺序表尾插
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
//检查顺序表是否已满,若满则扩容
SeqListCheckCapacity(psl);
//在表尾部插入数据
psl->list[psl->size] = x;
psl->size++;
}
Tset.c 中进行测试
#include "SeqList.h"
int main()
{
SeqList s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPrint(&s);
return 0;
}
测试结果
(之后的bug测试自己进行,不再赘述)
3.2.6顺序表尾删
当size小于0时,会越界访问导致头插失败。所以要有判断条件size > 0.
//顺序表尾删
void SeqListPopBack(SeqList* psl)
{
assert(psl);
//if判断 限制size大于0
if (psl->size > 0)
{
psl->size--;
}
}
3.2.7顺序表头插
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
//检查顺序表是否已满,若满则扩容
SeqListCheckCapacity(psl);
//将所有位置的数据从后往前,往后移动一位
int end = psl->size - 1;
while (end >= 0)
{
psl->list[end + 1] = psl->list[end];
end--;
}
//在表头位置插入数据
psl->list[0] = x;
psl->size++;
}
3.2.8顺序表头删
当表中无数据时,头删会使数组越界。导致后续操作出错。
//顺序表头删
void SeqListPopFront(SeqList* psl)
{
assert(psl);
//判断表中是否有数据
if (psl->size > 0)
{
//将表头后的数据从前往后,往前移动一位
int pos = 1;
while (pos < psl->size)
{
psl->list[pos - 1] = psl->list[pos];
++pos;
}
--psl->size;
}
}
3.2.9顺序表在pos位置插入x
//在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
//检查输入的pos是否越界
assert(pos <= psl->size);
int end = psl->size - 1;
//将pos之后的数据从后往前,往后移动一位
while (end >= pos)
{
psl->list[end + 1] = psl->list[end];
--end;
}
//在pos位置插入x
psl->list[pos] = x;
psl->size++;
}
3.2.10 删除pos位置元素
//删除pos位置数据
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
assert(pos < psl->size);
size_t begin = pos + 1;
//将pos位置后的元素从前往后,往前移动一位
while (begin < psl->size)
{
psl->list[begin - 1] = psl->list[begin];
++begin;
}
psl->size--;
}
3.2.11 修改pos位置数据为x
//数据修改
void SeqListModify(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
assert(pos < psl->size);
psl->list[pos] = x;
}
3.2.12 查找数据x的位置并返回
//数据查找
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->list[i] == x)
{
return i;
}
return -1;
}
}
3.3 SeqList.h SeqList.c 总览
//防止头文件重复
#pragma once
//包含头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//将存储数据类型重定义方便修改存储数据的类型
//SL(动态顺序表SeqList的缩写) DataType(数据类型)
typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
SLDataType* list;//动态开辟数据存储数据元素
int size; //存储的数据个数
int capacity;//存储空间大小
}SeqList;
//打印
void SeqListPrint(SeqList* psl);
//初始化
void SeqListInit(SeqList* psl);
//检查空间扩容
void SeqListCheckCapacity(SeqList* psl);
//顺序表销毁
void SeqListDestroy(SeqList* psl);
//尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
//尾删
void SeqListPopBack(SeqList* psl);
//头插
void SeqListPushFront(SeqList* psl, SLDataType x);
//头删
void SeqListPopFront(SeqList* psl);
//数据查找
int SeqListFind(SeqList* psl, SLDataType x);
//数据插入
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
//数据删除
void SeqListErase(SeqList* psl, size_t pos);
//数据修改
void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
#include "SeqList.h"
//打印顺序表
void SeqListPrint(SeqList* psl)
{
//对空指针进行断言,显示出错位置
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->list[i]);
}
printf("\n");
}
//动态顺序表的初始化
void SeqListInit(SeqList* psl)
{
//对空指针进行断言,显示出错位置
assert(psl);
psl->list = NULL;
psl->size = 0;
psl->capacity = 0;
}
//顺序表销毁
void SeqListDestroy(SeqList* psl)
{
assert(psl);
free(psl->list);
psl->list = NULL;
psl->capacity = psl->size = 0;
}
//顺序表空间扩容
void SeqListCheckCapacity(SeqList* psl)
{
assert(psl);
//当数据个数与存储空间相等时,数组已满
//如果数组满了,使用realloc进行扩容
if (psl->size == psl->capacity)
{
//当capacity为0时为其赋值为4,若不为0则进行倍增
size_t NewCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
//将当前的空间翻倍扩容
SLDataType* tmp = realloc(psl->list, sizeof(SLDataType) * NewCapacity);
//realloc扩容可能失败,需要对其进行检查
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);//终止程序
}
else
{
psl->list = tmp;
psl->capacity = NewCapacity;
}
}
}
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
//对空指针进行断言,显示出错位置
assert(psl);
//插入数据
SeqListCheckCapacity(psl);
psl->list[psl->size] = x;
psl->size++;
}
//顺序表尾删
void SeqListPopBack(SeqList* psl)
{
//对空指针进行断言,显示出错位置
assert(psl);
//if判断 限制size大于0
if (psl->size > 0)
{
psl->size--;
}
}
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListCheckCapacity(psl);
//将所有位置的数据从后往前,往后移动一位
int end = psl->size - 1;
while (end >= 0)
{
psl->list[end + 1] = psl->list[end];
end--;
}
//在表头位置插入数据
psl->list[0] = x;
psl->size++;
}
//顺序表头删
void SeqListPopFront(SeqList* psl)
{
assert(psl);
//判断表中是否有数据
if (psl->size > 0)
{
//将表头后的数据从前往后,往前移动一位
int pos = 1;
while (pos < psl->size)
{
psl->list[pos - 1] = psl->list[pos];
++pos;
}
--psl->size;
}
}
//数据查找
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->list[i] == x)
{
return i;
}
return -1;
}
}
//在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
//检查输入的pos是否越界
assert(pos <= psl->size);
int end = psl->size - 1;
//将pos之后的数据从后往前,往后移动一位
while (end >= pos)
{
psl->list[end + 1] = psl->list[end];
--end;
}
//在pos位置插入x
psl->list[pos] = x;
psl->size++;
}
//删除pos位置数据
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
assert(pos < psl->size);
size_t begin = pos + 1;
while (begin < psl->size)
{
psl->list[begin - 1] = psl->list[begin];
++begin;
}
psl->size--;
}
//数据修改
void SeqListModify(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
assert(pos < psl->size);
psl->list[pos] = x;
}
顺序表到这里也就实现完成了,现在可以通过Test.c程序对顺序表进行调试和使用了。
看到这里祝你考试科科满昏~~ ouo