数据结构之线性表
定义:线性表是一种最基本、最简单的数据结构。线性表在结构上具有有序性,即数据元素之间的相对位置是线性的,集体体现在以下几点
- 集合中必须存在唯一的一个“第一元素”。
- 几何中比存在唯一的一个“最后元素”。
- 除最后元素之外,均有唯一的后继。
- 除第一元素之外,均有唯一的前驱。
可以表示为
L = (a1,a2,a3,....,ai,....,an)
抽象类型定义:
ADT List
Data : D = {ai | ai ∈ ElementSet, i = 1,2,3,...,n, n >= 0}
Relation : R = { <ai-1, ai> | ai -1, ai ∈D,i = 2,..., n}
Operation:
InitList(&L)
DestoryList(&L)
Length(L)
GetElem(L, i)
Locate(L, e)
Insert(&L, i , e)
Delete(&L, i)
Prior(L, e)
Empty(L)
ListDisplay(L)
endADT
- 顺序表
- (摘自cnblogs)
//顺序线性表的头文件 #include<iostream> const int MaxSize = 100; //定义顺序表SeqList的模板类 template<class DataType> class SeqList{ public: //顺序表无参构造器(创建一个空的顺序表) SeqList(){ length = 0 } //顺序表有参构造器(创建一个长度为n的顺序表) SeqList(DataType array[], int n); //顺序表析构函数 ~SeqList(){} //求顺序表的长度 int GetLength(){ return length; } //顺序表按位查找,返回i位置的元素 DataType GetElement(int i); //顺序表按值查找,返回该元素所在的位置 int GetLocal(DataType x); //顺序表在指定的位置插入指定的元素 void Insert(int i, DataType x); //顺序表删除元素,返回删除的元素 DataType Delete(int i); //输出顺序表中的元素 void PrintSeqList(); private: //一维数组,存放数据元素 DataType data[MaxSize]; //顺序表的长度 int length; }; //实现顺序表有参构造器 template<class DataType> SeqList<DataType>::SeqList(DataType array[], int n) { if (n > MaxSize) { throw "传入的顺序表长度过长"; } //给顺序表的存储元素的数组赋值 for (int i = 0; i < n; i++) { data[i] = array[i]; } //给顺序表的长度赋值 length = n; } //实现顺序表按位查找 template<class DataType> DataType SeqList<DataType>::GetElement(int i) { //判断是定的位置是否合理 if (i < 1 || i >length) { throw "位置有误"; } else { //返回指定位置的元素 return data[i - 1]; } } //实现顺序表按值查找,返回该元素所在的位置 template<class DataType> int SeqList<DataType>::GetLocal(DataType x) { //遍历顺序表的元素 for (int i = 0; i < length; i++) { //判断指定的元素是否在顺序表中 if (data[i] == x) { //返回指定元素在顺序表中的位置 return (i + 1); } } //如果指定的元素不在顺序表中,则返回位置为0 return 0; } //实现顺序表插入元素 template<class DataType> void SeqList<DataType>::Insert(int index, DataType x) { //判断插入的位置是否合理 if (length >= MaxSize) { throw "顺序表已存放满"; } if (index<1 || index>length + 1) { throw "插入元素的位置有误"; } //如何插入的位置合理,则把顺序表中从最后位置到指定插位置的元素整体向后移动一个位置 for (int j = length; j >= index; j--) { data[j] = data[j - 1]; } //给插入的位置放入指定的元素 data[index - 1] = x; length++; } //实现顺序表删除指定位置的元素 template<class DataType> DataType SeqList<DataType>::Delete(int index) { //声明要取出的元素 DataType x; //判断要删除的位置是否合理 if (index<1 || index>length) { throw "删除的位置有误"; } else { //取出指定位置的元素 x = data[index-1]; //将指定位置后的元素全部都向前移动一个位置 for (int i = index; i < length; i++) { data[i - 1] = data[i]; } //删除顺序表中的元素后,其长度减1 length--; } return x; } //顺序输出顺序表中的元素 template<class DataType> void SeqList<DataType>::PrintSeqList() { if (length < 1) { throw "顺序表中没有元素"; } else { //顺序输出顺序表元素 for (int i = 0; i < length; i++) { cout << data[i] << " "; } cout << endl; } }在这里我觉得要注意三点:
- 申请内存的时候需要判断是否成功,如果没有成功需要即使叫停程序
- 插入元素的时候要判断两个位置第一个是 是否上溢,第二个是插入的位置是否异常(1. 插入了负数位置2. 插入的位置不在最后一个位置以及以内,造成线性表不连续)
- 删除元素和2点所说的基本一致。
- (摘自cnblogs)
- 链式存储结构。
这样的结构的特点是逻辑上相邻的两个数据元素其存储的位置不一定相邻,这样充分使用机计算机的资源。
其中最经典的就是单链表的实现
通过使用指针将所有的element联系了起来,形成了美丽的数据链条。
关于代码实现在互联网上随便都能查到,故这里就不给出了。但是有几点需要注意
- 初始化创建的Head头节点的next指针需要封成Null不然底下连接的时候链条结尾的元素就不能NULL不能判停了。
- 单链表查找、插入、删除等操作都是基于控制自定义指针p的遍历实现的,所以要注意循环停止的条件。
- 插入的计数j要从0开始,因为有可能插入的位置是1。
- 在各种操作中我们可以常看到形如if(!p || j > i - 1)throw "Wrong positon"的语句。这句话的作用体现在两方面
- !p说明p指针遍历到了结尾依旧没有找到符合条件的位置,说明这个位置是超出界限的
- j > i - 1中j-1可是你要找的位置,而i呢是计数的起始位置,说明要找的位置是负的。
3. 循环链表
很明显首尾相接,但是前面单链表基于Null来判定停止的条件便无法使用了,需要注意的是如何判停,这里我觉得可以通过判断如果某一个
节点的next的地址和Head的地址相同,那么说明产生首尾链接了,那么那个节点就是rear,可以通过这样的方式来判断。
上面所说的线性表可以做什么?
在课本上,举出了这样几个例子,包括求两个集合的并、一元多项式的表示和运算。不仅仅局限于此,我们可以把自然界中任何的物体抽象化放入线性表中,并且
通过特定的顺序产生一定的意义都是可以实现的,还有我觉得例如一元多项式表示和的运算的时候,data也可以不止一个,可以储存很多很多的信息,就像我们大
脑存储了所有美好的回忆,在特定的时候我们可以调用,让自己回味。
除了上面所说的让我们再谈一谈一些特殊的线性表:
1. 栈
它大名鼎鼎的原因我觉着或许在乎于实现了一种由结构决定的规则,即先进后出,好像是把东西放在箱子里面,先放进去的东西会被压住,然后
必须要把放在上面的东西拿掉,然后才能取出压在下面的东西。再有它的无线地价值在于我们可以审视拿出来的物品,这样我们可以根据条件放
入其他类似的物品,这样实现我们的某种目的。
*关于实现,基于上面的结构,我们可以实现两种栈,一种是基于顺序存储结构的顺序栈,还有一种是基于单链表的链栈。
1. 顺序栈没有什么特别好说的无非就是定义一个数组,然后一个指针指向特定的位置,一开始的时候是0,然后每次添加元素的时候指针往上加一
顺便存入这个元素,并且规定访问的方式不能是传统的下表访问,必须借助指针访问。
2. 链栈的实现我们可以形象一点,就比如你手上抓着一个一节节环扣的铁链条的一端,并且这个链条很长,我们要扣上新的链条的时候由于够不到
结尾所以我们只能在手抓的这一端扣上新的链条,然后取出来也一样,这样就形成了一个有特定规则的链栈。同样也只能有一个指针指向开头,并
且你只能通过它访问元素个进行操作。
我觉得我们能理解工作原理即可,很多很多的语言都有关于这个结构的类库,无需自己每次书写。
*他有实际场景的使用?
我的想法是任何要切结果是根据进入的顺序倒着的都可以使用,比如说进制的转换。伟大的人们还发明了使用它来计算表达式的值,再有烧脑的递归
的过程也是栈的结构。
2. 队列
既然这个世界上有先进后出便少不了先进先出,队列,就像是在食堂打饭排的那个队,排在前面的先取餐,而在后面的只好等等了。
关于这个队列的实现除了我们上面的两种方式,还有循环队列
结构是上面这个样子,它的优秀之处呢是重复利用空间,只需要tail = (tail + 1) % queuesize即可。就是入队和出队的时候有没有上下溢出就是特别
需要注意的了
另外,链队需要注意的是有两个指针一个指向开头,一个指向结尾。保证入队和出队能够正常进行这二者是必不可少的。
*应用?
所有先到先得的地方?我的教材上面说的是舞伴问题,就是舞会男女结伴,先到先得,真有意思。。。。还有打印文件的缓冲队列。
这里是我所说的关于线性表的地方,数据结构的课本的第一二章节,真的很有意思,虽然上课没怎么听但是不由得觉得算法是真的牛皮。并且我希望坚持把这些激动人心的知识整理到我的cnblogs上面来永远的留存下去。。。