一.线性表
线性表(Liner List)是n个具有相同特性元素的有限序列。
线性表具有两个结构特性:物理结构和逻辑结构。物理结构指的是数据在内存中存储的顺序;逻辑结构指的是人为抽象出来的一种结构。
线性表在逻辑结构上一定是线性的,而在物理结构上不一定是线性的。
常见的线性表有:顺序表、链表、栈、队列、字符串等 。
二.顺序表
顺序表(Sequence Liner List)是用一段内存中的连续地址存储数据的一种数据结构,其底层结构为数组。
而由于其在内存中连续存放,所以物理结构是线性的。至此我们可总结出顺序表的两大特性:逻辑结构是线性 && 物理结构也是线性。
顺序表使用数组来实现的,其与数组最大的不同在于,对数组进行了一定程度的封装,通过函数实现了较为便捷的增删查改的操作。
三.顺序表的分类
由于顺序表是由数组来实现,而数组通过实现方式的不同可以分为定长数组(直接定义好数组长度)和柔性数组(通过动态内存管理来分配栈区空间):所以顺序表也可以分为静态顺序表和动态顺序表。
3.1静态顺序表
#define N 100
typedef int SeqListDatatype
typedef struct SeqList
{
SeqListDatatype arr[N];
int size;
}SeqList;
静态顺序表由一个定长数组arr[N]和int类型的size变量组成,其中size变量用来指示当前顺序表操作到哪个下标位置的元素。而通过将int类型重命名为SeqListDatatype类型,就可以将顺序表i存储的元素进行较为方便的转换。
而静态顺序表最大的缺陷在于代码一开始实现时就决定的数组长度,而面对现实情况大部分是不确定的,所以一种针对于不同长度需要而定义的动态顺序表就应需而生。
3.2动态顺序表
typedef int SeqListDatatype;
typedef struct SeqList
{
SeqListDatatype* arr;
int size;
int capacity;
}SeqList;
动态顺序表由一个SeqListDatatype类型的指针,下标元素和指示当前顺序表空间的capacity变量构成。根据实际大小的需要,动态申请不同的空间来满足需求。
四.动态顺序表的具体实现
以下是头文件中一些函数的定义
在SeqList.h的定义中,首先定义了动态顺序表的结构体类型,然后声明了初始化、打印、增删查改等数据操作,往下一个一个函数进行说明。
4.1 顺序表的初始化函数 ——void SeqListInit(SeqList* SL);
首先定义了一个顺序表:SeqList SList = { 0 };
具体实现方式如下图所示:
首先进行数组空间的分配,初始化的时候先分配4个,之后根据需要会在增添数据的时候进行合理扩容。分配4个空间,所以capacity应该为4;由于数组中没有放置任何元素,所以size应该指向0下标的元素。
4.2数据打印函数——void SeqListPrint(SeqList* SL);
整体实现如下图:
整体思路较为简单,只需要遍历整个顺序表,然后按顺序打印数据即可。
4.3扩容函数——Judge_And_Increase(SeqList* SL)
由于扩容函数仅仅在增加数据时需要用到,所以没有将其声明在头文件,整体实现如下图:
判断条件为: size指向的下标如果和capacity相同,就代表当前顺序表的空间已经不够用了,所以需要进行扩容。合理的扩容思路为每次扩容的容量大小为上之前的倍数,在这里取2倍。用realloc函数进行空间的扩容。
4.4尾插数据函数——void SeqListPushBack(SeqList* SL, SeqListDatatype x)
尾插数据,因为要添加数据,所以要先判断容量是否足够。之后将size下标的位置替换为要添加的数据x即可,之后要将size自增从而指向下一个位置。
4.5 头插数据——void SeqListPushFront(SeqList* SL, SeqListDatatype x)
头插与尾插最大的不同在于,头插数据需要把下标为0的位置空出来,让新数据插入。而空出头元素则需要将数组的元素整体向后移,仍然需要先判断空间是否足够大。将元素依次后移,就可以将下标为0的位置放置数据x。
4.6尾删数据——void SeqListDeleteBack(SeqList* SL)
尾删数据,不需要真正把最后一个元素删除,只需要遍历数组的时候让其索引不到即可,所以将size向前挪动一位,这样下次不论是头插还是尾插数据,都会将这个元素替换为新元素。
4.7头删数据——void SeqListDeleteFront(SeqList* SL)
头删数据思路与尾删数据类似,头删只需要将从下标为1开始的数据依次向前移动,将第一个位置替代即可。
4.8查找数据并返回对应下标——int SeqListSearch(SeqList* SL, SeqListDatatype x)
仍然是遍历数组,如果找到数组中与指定x相同的数据,返回相应的下标即可。而由于数组中下标不会出现-1,所以定义如果找不到这个元素,返回-1。
4.9指定位置之前插入数据——void SeqListInsert(SeqList* SL,int pos, SeqListDatatype x)
指定位置之前插入数据,首先仍需要判断空间是否足够,然后将这个位置的数据依次向后挪动,将这个位置留出一个空间,再将数据x放入。
4.10指定位置数据删除——void SeqListDelete(SeqList* SL, int pos)
从该位置开始,将之后的数据提前,将pos位置的数据覆盖即可达到指定位置删除的效果。
4.11销毁顺序表——void SeqListDestroy(SeqList* SL)
由于顺序表的空间是通过动态内存函数calloc和realloc函数进行申请,所以用完之后需要free,同时将容量capacity和下标size置为0即可。