数据结构初阶——顺序表
引言:先从数据结构中最基础的线性表开始学习
-
关于数据结构
-
数据结构是什么:
数据存放于内存、外存中,数据结构是计算机存储、组织数据的方式,使数据能更高效地被访问和修改。 -
学习数据结构的意义与优势:
1-高效处理数据:对不同的使用场景使用不同的数据结构
2-优化程序性能:对特定功能选择最优数据结构实现最优解
3-解决不同问题、应对不同场景
4-增强程序的可维护性:多用封装的方式 -
- 线性表定义:
n个具有相同特性的数据元素的有限序列。是一种广泛使用的数据结构,常见的线性表有顺序表、链表、队列、栈、字符串等。
在逻辑上是线性结构,是连续的一条直线。但在物理结构上不一定连续,线性表在物理结构上通常以数组和链表的形式存储。 -
顺序表定义:
是用一段物理地址连续的存储单元依次存储数据元素的线性结构,c语言中多采用数组存储,在此基础上实现不同功能。 -
顺序表与数组区别:
顺序表是一种逻辑结构概念,数组是一种编程语言中的数据类型。 -
写顺序表时我们可以开三个文件,其中头文件写函数声明,两个源文件写函数的定义和主函数实现,这样能增加代码的可读性,便于管理和调试。
头文件函数声明
第一行预处理指令确保头文件只被编译一次,避免链接阶段出现重复定义和降低效率的问题
首先用typedef关键字为int类型数据定义新名字便于理解和整体修改。同时将struct Seqlist类型结构体重命名为SL(sequence list)。
动态顺序表通过一个指针可按需申请空间
一些使用功能,通过传址调用能直接修改地址所指向内容。
源文件函数定义
初始化SLInit
首先记得包含头文件。首先断言传进来的指针是否为空,通过箭头操作符访问结构体s(初始化在主函数中,后续部分可查看)中的指针a,使用动态内存函数malloc开辟空间,size代表整个空间大小
扩容问题SLCheckCapacity
在进行功能实现前需检查容量,若size==capacity需运用realloc扩容,ptr是目标指针,size是扩容后空间总大小
这里需要非常注意一点!!!由于realloc扩容失败会返回空指针,所以必须用一个临时变量来接收(这里用的tmp),先判断有效性,若不为空则赋值给指针a,否则会造成对空指针解引用的错误问题。
原地扩容与异地扩容
- 就这realloc函数来讨论一下原地与异地扩容问题
- 原地扩容
直接在原地址已有空间后直接开辟
- 异地扩容
原空间后没有足够空间时,在堆空间上找另一个合适大小的连续空间来使用,函数返回一个新内存地址
- 效率原地>异地
指定插入SLInsert
首先断言pos为数组的有效下标,检查容量,然后创建临时变量end,从数组最后依次往前遍历,进行下标为pos位置以及往后的元素整体后挪,最后对下标为pos位置进行赋值,别忘了size(已有有效元素个)加一。
指定删除SLErase
老规矩,和指定插入一样先断言,区别在于从pos下标位置赋值为下一个元素,从前往后遍历,避免元素被覆盖
查找指定元素SLFind
通过遍历找到下标对应相等元素,返回下标
尾插SLPushBack
检察容量后直接在数组末尾赋值插入,easy
尾删SLPopBack
size-1即为数组最后一个元素的下标,每次使size–等同于删除最后一个元素,这里的检查非常必要,当size=0时会弹出一个窗口,调用abort函数发出异常信号,使程序非正常退出。
头插SLPushFront
思想与尾插类似
头删SLPopFront
begin下标可以自己控制,整体向前赋值并挪到,避免数据覆盖
打印SLPrint
当实现不同功能后我们需要一个反馈,通过for循环遍历数组下标依次打印即可
删除SLDestroy
因为使用了动态内存函数所以完后需要free释放,并将指针a置空,将开辟的内存空间使用权返回操作系统。最后一行运用连等式从右往左依次赋值。
源文件主函数实现
这是对程序的调试,大家可以自行使用不同多种数据尝试
大家可以根据自身喜好来设置菜单等运行界面,我习惯突出重点(懒)
整体代码
- SeqList.h
#include<assert.h>
//typedef int SLDataType;
//#define N 100000
//
静态顺序表 -- 开少了不够用 开多了浪费
//struct SeqList
//{
// SLDataType a[N];
// int size;
//};
typedef int SLDataType;
#define INIT_CAPACITY