大话数据结构与算法-程杰
第三章 线性表
线性表的定义
零个或多个数据元素的有限序列。
这里需要强调几个关键的地方:
- 首先该线性表一定是有序的,若元素有多个,则第一个元素无前驱,最后一个元素无后继,其他元素都有且只有一个前驱和后继;
- 其次,线性表强调有限,也就是元素必须是有限的。
用数学语言来进行定义。可如下:
若将线性表记为(a1, …, ai-1, ai, ai+1, …, an),则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。当i=1, 2, …, n-1时,ai有且仅有一个直接后继,当i=2, 3, …, n时,ai有且仅有一个直接前驱。如下所示:
所以线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。
在非空表中的每个数据元素都以一个确定的位置,如a1是第一个数据元素,是最后一个数据元素,ai是第i个数据元素,称i为数据元素ai在线性表中的位序。
线性表的顺序存储结构
- 顺序存储结构的定义
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。 - 顺序存储方式
线性表的顺序存储结构,说白了,就是在内存中找了块地儿,通过占位的形式,把一块内存给占了,然后把相同数据类型的数据元素依次存放在这块空地上。既然线性表的每个数据元素的类型都相同,所以可以通过一维数组来实现顺序存储结构,即把第一个数据元素存到数组下标为0的位置中,接着把线性表相邻的元素存储在数组中相邻的位置。
在建立顺序表时,除了预先申请内存空间,还需要实时记录顺序表的长度和顺序表本身申请的内存大小,便于后期对顺序表中的数据元素进行调取。所以,顺序存储的结构代码为:
#define MAXSIZE 20 // 存储空间出事分配量
typedef int ElmType; // ElmType类型根据实际情况而定,这里假设为int
typedef struct
{
ElmType data[MAXSIZE]; // 数据存储数据元素,最大值为MAXSIZE
int length; // 线性表当前长度
}SqList;
这里,我们发现描述顺序存储结构需要的三个属性:
<1> 存储空间的起始位置:数据data,它的存储位置就是存储空间的存储位置;
<2> 线性表的最大存储容量:数组长度MAXSIZE;
<3> 线性表的当前长度:length
3. 数据长度与线性表长度区别
数组的长度是存放线性表的存储空间的长度,存储分配后这个量一般是不变的。有个别人可能会问,数组的大小一定不可以变吗?我怎么看有些书中谈到可以动态分配的一维数组。是的,一般高级语言,比如C、C++、VB都可以用编程手段实现分配数组,不过会带来性能上的损耗。
线性表的长度是线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是改变的。
任意时刻,线性表的长度应该小于等于数组的长度。
4. 顺序存储结构的基本操作
ADT 线性表(List)
Data
线性表的数据对象集合为{a1, a2, …, an},每个元素的类型为DataType。其中,除第一个元素a1外,每一个元素有且只有一个前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
int InitList (SqList *L); /** 初始化一个顺序表 **/
bool ListEmpty (L); /** 若线性表为空,返回true,否则返回false **/
int printList (SqList L); /** 打印线性表中的每一个元素 **/
int getlength (SqList L); /** 返回线性表元素的个数 **/
/** 创建一个链表长度为length的线性表 **/
int createList (SqList *L,int length);
/** 在线性表的第pos个位置插入 一个新元素elem **/
int insertList (SqList *L, int pos,ElemType elem);
/** 将线性表中第pos个位置的元素返回,保存在*e中 **/
int getElem (SqList L, int pos, ElemType *e);
/** 在线性表中查找与给定元素e相等的元素,如果有相同的返回状态值1,
如果没有,返回状态值0 **/
int locateElem (SqList L, ElemType e);
/** 从线性表中删除pos位置处的元素,并将其存放在elem中; **/
int deleteList (SqList *L, int pos,ElemType *elem);
int clearList (SqList L,SqList *pL); /** 清空顺序表 **/
<一> 初始化一个顺序表
int initList (SqList *L)
{
L->length=0;
return 1;
}
<二> 判断线性表是否为空
bool ListEmpty (L)
{
if (L.length == 0)
return false;
return true;
}
<三> 打印线性表中的每一个元素
int printList (SqList L)
{
// 线性表是否为空
if (L.length==0)
return 0;
for (int i=0; i<L.length; i++)
{
printf("data[%d] = %d\n",i,L.data[i]);
}
return 1;
}
<四> 返回线性表元素的个数
int getlength (SqList L)
{
return L.length;
}
<五> 创建一个链表长度为length的线性表
int createList (SqList *L,int length)
{
srand(time(0));
int i;
for (i=0;i<length;i++)
{
L->data[i]=rand()%100;// 插入随机数
L->length++;
}
return 1;
}
<六> 指定位置pos处插入一个新的元素elem
插入算法思路:
A. 如果插入元素不合理,抛出异常;
B. 如果线性表长度大于数组长度,则抛出异常或动态增加容量;
C. 从最后一个元素开始遍历到第i个位置,分别将它们都向后移动一个位置;
D. 将要插入元素填入第i个位置;
E. 表长度加1。
int insertList (SqList *L, int i,ElemType elem)
{
if (L->length == MAXSIZE) // 顺序表已满
return 0;
if (pos<1 || pos>L->length) // 当pos不在范围内
return 0;
if (pos<L->length)
{
for (int i=L->length-1;i>=pos-1;i--)
{// 将要插入位置后数据往后移动一个位置
L->data[i+1]=L->data[i];
}
}
L->data[pos-1] = elem; // 将新元素插入
L->length++;
return 1;
}
<七> 获取顺序表中指定位置处的元素值
int getElem (SqList L, int pos, ElemType *e)
{
if (pos<1 || pos>L.length)
return 0;
*e = L.data[pos-1];
return 1;
}
<八> 查找在线性表中是否含有指定元素
int locateElem (SqList L, ElemType e)
{
for (int i=0;i<L.length;i++)
{
if (L.data[i] == e)
{
printf("在pos[%d]位置处,查找到了元素elem:%d\n",i+1,e);
return 1;
}
}
return 0;
}
<九> 删除顺序表中指定位置处的元素
删除算法的思路:
A. 如果删除位置不合理,抛出异常;
B. 取出要删除的元素;
C. 从删除元素位置开始遍历到最后一个元素,分别将它们都向前移动一个位置;
D. 表长度减去1.
int deleteList (SqList *L, int pos,ElemType *elem)
{
if (L->length == 0) //线性表为空
return 0;
if (pos<1 || pos>L->length) // 删除位置不合理
return 0;
*elem = L->data[pos-1];
if (pos<L->length) // 如果删除不是最后位置
{
for (int i=pos;i<L->length;i++)
{
L->data[i-1]=L->data[i]; // 将删除位置后继元素前移
}
}
L->length --;
return 1;
}
<十> 清空一个顺序表,将顺序表的length置为0
int clearList (SqList L,SqList *pL)
{
L.length=0;
pL->length=0;
return 1;
}
- 线性表顺序存储结构的优缺点
顺序表是用一段地址连续的存储单元依次存储线性表的数据元素。其特点是:内存中地址连续,支持随机查找,按位查找算法的时间复杂度为O(1),按值查找的平均时间性能是O(n),插入删除操作的平均时间性能是O(n),适用于需要大量访问元素,而没有或少量增添或删除元素的程序。
顺序表的优点为:随机访问较快,创建简单。
缺点为:插入和删除需要移动大量的元素;表的容量难以确定;造成存储空间的“碎片”等。