目录
一、线性表的原理
二、线性表的实现(顺序表)
1.定义顺序表
2.初始化顺序表
3.判断顺序表是否为空
4.获取顺序表的长度
5.向顺序表中插入元素
6.删除指定位置的元素
7.遍历顺序表
8.得到指定位置的元素
三、打印测试功能
1.测试
2.结果输出
3.总代码
四、顺序表的优缺点
1.顺序表的优点:
2.顺序表的缺点
一、线性表的原理
线性表,从名字上你就能感觉到,是具有像线一样的性质的表。在广场上,有很多人分散在各处,当中有些是小朋友,可也有很多大人,甚至还有不少宠物,这些小朋友的数据对于整个广场人群来说,不能算是线性表的结构。
但像刚才提到的那样,一个班级的小朋友,一个跟着一个排着队,有一个打头,有一个收尾,当中的小朋友每一个都知道他前面一个是谁,他后面一个是谁,这样如同有一根线把他们串联起来了。就可以称之为线性表。
线性表(List) : 零个多个数据元素的有限序列。
二、线性表的实现(顺序表)
1.定义顺序表
主要完成一些常量的定义以及创建线性表的操作。
#define MAX_SIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;typedef struct
{ElemType data[MAX_SIZE];int length;
}SqList;
2.初始化顺序表
初始化主要指对表的数量置为0。
// 初始化顺序表
Status InitList(SqList* L)
{if (L == NULL){return ERROR;}L->length = 0;return OK;
}
// 重置顺序表
Status ClearList(SqList* L)
{if (L == NULL){return ERROR;}L->length = 0;return OK;
}
3.判断顺序表是否为空
对表中的length长度进行判断。
// 判断是否为空
Status isEmpty(SqList* L)
{if (L->length > 0){return FALSE;}else if(L->length == 0 || L->length == NULL){return TRUE;}
}
4.获取顺序表的长度
getter操作,返回length长度。
// 获取长度
int ListLength(SqList* L)
{return L->length;
}
5.向顺序表中插入元素
主要用到for循环操作,让后一位的值等于前一位的值,其中 index 指的是第几个元素。
需要注意:要用的for循环的逆向循环,因为正向循环最后一位元素无法向后进一。
如图所示:
代码主要为:
// 插入元素
Status InsertList(SqList* L, int index, const ElemType e)
{if ( (index >= 1) && (L->length < MAX_SIZE)){if ((index <= L->length)){for (int k = L->length - 1; k >= index - 1; k-- ){L->data[k + 1] = L->data[k];}}L->data[index - 1] = e;L->length++;return OK;}return ERROR;
}
6.删除指定位置的元素
原理和插入元素一样,都是用到for循环,对数据进行赋值操作。同样这里需要用的for循环的正向遍历。
如图所示:
代码为:
// 删除元素
Status DeleteList(SqList* L, int index, ElemType* e)
{ if (L->length == 0 || index < 1 || index > L->length) return ERROR;if (index < L->length){*e = L->data[index - 1];for (int k = index - 1; k < L->length; k++){L->data[k] = L->data[k + 1];}L->length--;return OK;}
}
7.遍历顺序表
很简单,就是简单的数组遍历。
Status visit(ElemType e)
{printf("%d->", e);return OK;
}
// 遍历
Status ListTraverse(SqList* L)
{for (int i = 0; i < L->length; i++){visit(L->data[i]);}printf("\n");return OK;
}
8.得到指定位置的元素
根据索引位置,获取指定元素的值。
Status GetElem(SqList* L, int index, ElemType* e)
{if (L->length == 0 || index < 1 || index > L->length) return ERROR;*e = L->data[index - 1];return OK;
}
三、打印测试功能
1.测试
输入以下代码,测试功能:
int Createlist()
{SqList L;ElemType e;Status ret;ret = InitList(&L);for (int i = 0; i < 5; i++){ret = InsertList(&L, 1, i);}ListTraverse(&L);ret = isEmpty(&L);printf("L是否为空, %d(1: 空; 0: 否)\n",ret);DeleteList(&L, 2, &e);printf("删除的第二个元素为:%d\n", e);ListTraverse(&L);GetElem(&L, 3, &e);printf("得到第三个的元素为:%d\n", e);for (int i = 5; i < 10; i++){ret = InsertList(&L, 2, i);}ListTraverse(&L);// 清空ClearList(&L);return 0;
}
2.结果输出
如图所示:
3.总代码
代码如下:
#include <iostream>#define MAX_SIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;typedef struct
{ElemType data[MAX_SIZE];int length;
}SqList;// 初始化顺序表
Status InitList(SqList* L)
{if (L == NULL){return ERROR;}L->length = 0;return OK;
}
// 重置顺序表
Status ClearList(SqList* L)
{if (L == NULL){return ERROR;}L->length = 0;return OK;
}// 判断是否为空
Status isEmpty(SqList* L)
{if (L->length > 0){return FALSE;}else if(L->length == 0 || L->length == NULL){return TRUE;}
}// 获取长度
int ListLength(SqList* L)
{return L->length;
}// 插入元素
Status InsertList(SqList* L, int index, const ElemType e)
{if ( (index >= 1) && (L->length < MAX_SIZE)){if ((index <= L->length)){for (int k = L->length - 1; k >= index - 1; k-- ){L->data[k + 1] = L->data[k];}}L->data[index - 1] = e;L->length++;return OK;}return ERROR;
}// 删除元素
Status DeleteList(SqList* L, int index, ElemType* e)
{ if (L->length == 0 || index < 1 || index > L->length) return ERROR;if (index < L->length){*e = L->data[index - 1];for (int k = index - 1; k < L->length; k++){L->data[k] = L->data[k + 1];}L->length--;return OK;}
}Status visit(ElemType e)
{printf("%d->", e);return OK;
}
// 遍历
Status ListTraverse(SqList* L)
{for (int i = 0; i < L->length; i++){visit(L->data[i]);}printf("\n");return OK;
}Status GetElem(SqList* L, int index, ElemType* e)
{if (L->length == 0 || index < 1 || index > L->length) return ERROR;*e = L->data[index - 1];return OK;
}int Createlist()
{SqList L;ElemType e;Status ret;ret = InitList(&L);for (int i = 0; i < 5; i++){ret = InsertList(&L, 1, i);}ListTraverse(&L);ret = isEmpty(&L);printf("L是否为空, %d(1: 空; 0: 否)\n",ret);DeleteList(&L, 2, &e);printf("删除的第二个元素为:%d\n", e);ListTraverse(&L);GetElem(&L, 3, &e);printf("得到第三个的元素为:%d\n", e);// 继续添加元素for (int i = 5; i < 10; i++){ret = InsertList(&L, 2, i);}ListTraverse(&L);// 清空ClearList(&L);return 0;
}int main()
{return Createlist();
}
四、顺序表的优缺点
1.顺序表的优点:
(1)无须为表示表中元素之间的逻辑关系而增加额外的存储空间
这是个什么意思呢?就是这为什么是一个优点啊?我们实际上顺序表当中这个元素,它们之间的这个逻辑关系呢,是一个单纯的就是个什么线性关系,就是一个挨着一个一个挨着一个。所以你不用去额外的有一个东西去记录它们之间的这样一个关系。
很快,我们就会学习到链表。那么,在链表当中呢?我们就额外用了一些东西去记录前一个元素和后一个元素之间的一个关系。所以那个地方是要消耗额外的存储空间的啊,这个但是在顺序表当中呢,它是不需要的。这是它的一个优点,这就意味着你记下来的数据全都是有效数据。基本上没有什么这个冗余数据,但是像比如说我们后面学到链表或者是以后我们学到其他数据结构,它多多少少还是一些冗余数据,有一些东西它是用来表达或者是记录元素和元素之间关系的,但顺序表是不需要的 。
(2)可以快速地读取表中任一位置的元素
这一点也是其他结构,或者说其他数据结构很难做到的一个事情。不要看这个东西很简单。呃,但是它其实是很难做到,就是你在用这样的一些数据结构的时候,你一定要搞清楚你的主要诉求是什么?如果这个写入或者是新增这样的操作,在你的这个里面占比并不是很大的情况下。那么,你读取将是一个很重要的一个性能指标。
2.顺序表的缺点
(1)插入和删除操作需要移动大量元素
那它缺点肯定也是一样,那首先第一个就是你插入和删除的话,需要移动大量元素。为什么?因为你元素之间没有逻辑关系的额外存储空间呐。我删掉了一个,比如说这样的一个线性表是吧?我把中间这块删掉了,你就得把后面的往前挪。你不挪的话,那这中间就空了一个元素出来,这个元素不在这个里面,它是个空的空的,那这样子行不行?不行为什么?因为我没有额外的东西记录啊,我这后面一个可以跳一个没有这样的存储空间给它,所以这个东西它是它的优点。到了这里,就是它的缺点。
你就必须要移动,你在中间删了一个就要移动,你移你要删除了这个头部的,你得把所有的都往前面移动一次。所以它最坏的情况下呢,你要移动n次也就这个移动的这个玩意儿,它是一个O(n)的时间复杂度,随着你的规模是线性增增加的。啊,移动最坏情况下是O(n)那么最差的情况下呢?那你也是要平均水平也是一半吧?对吧,随机出现的话,平均水平也是要一半吧,当然如果是删除尾巴上的是最轻松的,直接把它删掉就完就完事儿了,把长度缩减一就OK了。
(2)当线性表长度变化较大时,难以确定存储空间的容量
当这个线性表长度变化较大的时候,很难确定存储空间的容量,因为你看我们的这个线性表顺序表它是一开始我们就给了20最大的空间。然后呢?根据这个长度呢去?实际使用的这个长度会根据呃,这个东西去变化啊,它的这个长度变量length变量会根据实际使用的长度去变化。那么你在这个20范围以内。当然是OK的,没问题,这个效率都很高,不管你是增加还是减少,都还比较高,但是一旦这个变化很大。
如果比如说你从20变成200的时候,这个时候就不好说了,当然现在的计算机来讲的话,你额外增加的180个元素,它也许是有内存空间的。但是如果你存的每一个结构,每一个这个单元,它的数据本身就很大的情况下,你再增加180个。这个东西你能增加的上去吗?不好说对不对,然后你如果这个使用的长度又突然一下缩减,就是一下子增的很长。一下子呢,又减的很厉害,这种情况有没有有什么样的情况?
就是比如说像这个数据包的这样一个队列。啊,数据包的一个队列,我们要去做一个数据包啊,我从客户端收到数据包,那高峰时期它就是很长啊。它可能一瞬间能达到1000多。1000多个包,瞬间发过来很正常啊,然后但是有时候出现问题,可能到了晚上三四点钟可能一个,两个包发过来,甚至一个包都没有啊。那这个存储空间弹性就非常变化,非常大,这个东西就比较麻烦,如果你申请多了吧,浪费存储空间,因为这个东西你申请了占着没用它。
(3)造成存储空间的"碎片"
这个就是我们实际上就是遇到过这样的一些情况,我们这个里面是20,但是我们用到用满了20吗?在我们的测试当中,一直没有对不对?我们最多的时候用到了10。剩下的这些东西都是都是空的啊,那么当我在这里面删除一个以后,我要把所有东西都往前面移,都往前面移,那么在这个移动过程中,最后又会空出来一个这个东西,一直在这里。如果你有大量的这种队列是吧?你有一个两个三个这样的队列,每个队列多多少少是吧?就有一点空的。那这些东西是不是没有用的?这些东西算是一个碎片呢。