本文内容:
- 线性表简要介绍
- 代码编写规范
- 顺序表概念及基本运算
- 顺序表的实现(C语言)
🎆 前言
顺序表是线性表的一种,线性结构(逻辑结构)+ 顺序存储(存储结构) = 顺序表。
那么在说顺序表之前,我们先来简单介绍两个东西:线性表和代码编写规范。
⚾ 什么是线性表?
线性表是最基本、最简单、也是最常用的一种数据结构,是一种逻辑结构为线性结构的数据结构。一个线性表是n个具有相同特性的数据元素的有限序列。
根据存储结构的不同,线性表又分为顺序表和链表。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储,但是把最后一个数据元素的尾指针指向了首位结点)。
1.线性表的数学表述:
线性表是包含若干数据元素的一个线性序列。
记为:** L=(a0, … ai-1, ai, ai+1 … an-1)**
L为表名,**ai (0≤i≤n-1)**为数据元素;
n为表长,n>0时,线性表L为非空表,否则为空表。
线性表L可用二元组形式描述:
L= (D,R)
即线性表L包含数据元素集合D和关系集合R
D={ai | ai∈datatype ,i=0,1,2, ∙∙∙∙∙∙∙∙∙n-1 ,n≥0}
R={<ai , ai+1> | ai , ai+1∈D, 0≤i≤n-2}
- 关系符<ai, ai+1>在这里称为有序对
- 表示任意相邻的两个元素之间的一种先后次序关系
- ai是ai+1的直接前驱,ai+1是ai的直接后继
举例:
设有一个顺序表L={1,2,3,4,5,6}; 他们的关系如图:
使用二元组描述L=(D,R),则
D={1 , 2 , 3 , 4 , 5 , 6}(n=6)
R={<1,2> , <2,3> ,<<3,4> , <4,5> , <5,6>}
2.线性表的特点:
- 对非空表,a0是表头,无前驱;
2) an-1是表尾,无后继;
3) 其它的每个元素ai有且仅有一个直接前驱ai-1和一个直接后继ai+1。
⚾代码编写规范
这里的代码编写规范主要指我们在实现某个功能时该如何设置程序文件,如何实现代码分层,即在.c和.h这些文件中分别写什么。
一般来说,我们写的代码要做到结构清晰、软件复用方便。如果我们能够做到这些,那编写的代码是具有一定独立性的,它所实现的功能或许可以用到别的地方去,到时候只需要拿到程序接口就行了。
以实现顺序表为例,我们一般要用三个文件:sqlist.c、sqlist.h、test.c
其中sqlist.h用来定义我们的顺序表用到的结构体、声明顺序表运算函数等等。sqlist.c则用来编写具体实现这些函数。test.c就是main函数所在的文件,在该文件中为了测试我们的程序是否可行。这样做的话,我们的sqlist.c和sqlist.h文件是相对独立的,我们以后再需要用到顺序表的话,直接将这两个文件包含进来就可以了,而不用再重新写代码。
🎆 顺序表的实现
顺序存储的线性表就是顺序表,它的模型就是一维数组。一个一维数组,每个元素紧密相连,在内存中连续存储,通过某一个元素的下标可以找到所有元素。但是,如何去实现一个顺序表呢?
试想一下,如果我们面对一个顺序表L(a0 — an),这个顺序表该实现那些运算呢?或者说,它该有哪些功能呢?
- 创建并初始化:ListInit();
- 判断是不是空表:ListIsEmpty();
- 查表长:ListLen();
- 向表中添加元素:ListInsert();
- 遍历顺序表并打印所有元素:ListShow();
- 清空顺序表:ListClear();
- 释放顺序表开辟的空间:ListFree();
- 删除某个元素:ListDelete();
- 查找定位表中某个元素:ListLoc();
10.删除重复元素:ListDupdel();
暂时我们考虑实现以上功能。下面来一一实现。
⚾1.创建并初始化:ListInit();
在具体写这个函数之前,我们需要考虑使用一个怎么样的结构来完成我们的顺序表:
typedef int data_t;//这样一来,data_t的类型就可以直接在这里更改一次就好
#define CAP_INIT 3 //初始化容量大小
#define CAP_ADD 3 //容量满了之后每一次加多少个data_t大小的容量
typedef struct sqlist
{
data_t* data;//数据内容,可以是一维数组
int cap; //当前容量
int last; //当前位置
}sqlist, * sqlink;
对于结构体的定义,我们认为直接使用data_t data[1000]这样的数组结构不利于我们对空间的高效率使用,因为一旦定死比如1000,这个数比较大而实际使用中一时半会儿又用不了这么多,会无端地在内存上开辟太多的空间。因此这里我们使用一个指针,来维护我们以后开辟的一个动态内存。
last是这个顺序表中当前最后一个元素的位置,last+1就是顺序表中所有元素的个数。
cap表示当前顺序表中能容纳元素个数最大值。
CAP_INIT是初始化时给cap的值,单位是sizeof(data_t)个字节。
CAP_ADD则是以后cap满了之后进一步开辟空间的大小,单位与CAP_INIT相同。
当有了上面这个结构体,我们就可以来实现各个运算的函数了。首先就是创建初始化函数。
既然要创建初始化一个顺序表,那么首先你要给我一个地址,我通过这个地址去创建并初始化顺序表。因此我们在main()函数中定义一个顺序表:
sqlist L;//以后给其他函数传参直接传它的地址&L,其他函数在形参可以直接用sqlink定义
//因为传参传地址才能修改内容
//sqlink是个指针
创建好一个顺序表之后,我们就要初始化了。首先考虑ListInit()的参数与返回值。参数我们知道要接受其他函数调用时传过来的sqlist结构体的地址,所以参数应该时sqlink类型。返回值我们可以考虑int类型,如果创建成功就返回0,创建不成功返回个-1。
代码:
//1.顺序表创建函数:
/**
***@ret 0:初始化成功 -1:list 初始化失败
***@para 顺序表的结构体指针,sqlink类型
**/
int ListInit(sqlink list)
{
//给顺序表中存放数据的data开辟一块长度为初始容量大小的动态内存:malloc
list->data = (data_t*)malloc(CAP_INIT * sizeof(data_t));
if (list->data == NULL)
{
return -1;
}
list->cap = CAP_INIT;//初始化最大容量为初始值
list->last = -1;//初始化当前位置的下标是-1,说明表中没有元素
return 0;
}
⚾2.判断是不是空表:ListIsEmpty();
既然已经创建并初始化成功了,那么我们来查看一下顺序表是不是空表,如果是空表返回0,如果不是空表返回当前元素个数last+1。