数据结构与算法分析笔记(c++)_抽象数据类型(ADT)、表ADT、STL中的向量和表

第三章 表、栈和队列
本章讨论最简单和最基本的三种数据结构。实际上,每一个有意义的程序都将明晰地至少使用一个这样的数据结构,而栈则在程序中总是要间接地用到,而不管你在程序中是否进行了声明。本章将:
介绍抽象数据类型(ADT)的概念。
阐述如何对表进行高效的操作。
●介绍栈ADT及其在实现递归方面的应用。
●介绍队列ADT及其在操作系统和算法设计中的应用。
在本章中,给出了两个库类 vector和1ist的重要子集的实现代码。

3.1抽象数据类型(ADT)
抽象数据类型(abstract data type,ADT)是带有一组操作的一些对象的集合。抽象数据类型是数学的抽象;在ADT的定义中根本没有提到这组操作是如何实现的。诸如表、集合、图以及与它们各自的操作一起形成的这些对象都可以看做是抽象数据类型
可以有像加(add)、删除(remove)、大小(size)以及包含(contains)这样些操作。当然,也可以只要两种操作:并(union)和查找(find),这两种操作又在该集合上定义了一种不同的ADT。
C++的类也考虑到ADT的实现,不过适当地隐藏了实现的细节。
如果由于某种原因需要改变实现的细节,那么通过仅仅改变执行这些ADT操作的例程应该是很容易做到的。理想情况下,这种改变对于程序的其余部分是完全透明的。
对于每种ADT并不存在什么法则来告诉我们必须要有哪些操作;这是一个设计决策。错误处理和结构调整(在适当的地方)一般也取决于程序的设计者。

3.2表ADT
我们将处理形如Ao,A2,A3,…,AN-11的一般的表。这个表的大小是N。我们将称大小为0的表为空表(empty list。
后继和前驱:对于除空表外的任何表,我们说Ai后继Ai-1(或继Ai-1之后)并称Ai-1(i<N)前驱Ai(i>1)。
与这些“定义”相关的是我们要在表ADT上进行操作的集合。
printlist和 makeEmpty是常用的操作,其功能是显而易见的;
find返回 项 首次出现的位置;
insert和 remove般是从表的某个位置插入和删除一些元素;
而findkth则返回某个位置上(作为参数而被指定)的元素。
当然,一个函数的功能怎样才算恰当,完全要由程序设计者来确定。这类似于对特殊情况的处理。我们还可以自己添加一些运算
3.2.1表的简单数组实现
对表的所有操作都可以使用数组来实现。虽然数组是静态分配的,但是内部存储数组的vector类允许在需要的时候将数组的大小增加一倍(实现方法:开辟新空间,复制数据到新空间,删除原空间)。这解决了使用数组的最严重的问题。也就是在使用数组时需要对表的大小的最大值进行估计的问题。这个估计现在就不再需要了。
数组实现使得 printlist以线性时间执行,而 findkth则花费常数时间,这是最好的结果了。然而,插入和删除的花费却有可能是昂贵的,这取决于插入和删除发生的位置。在最坏的情况下,在位置0(换伺话说是在表的前面)插入需要将整个数组后移一个位置以空出空间来而删除第一个元素则需要将表中的所有元素前移个位置,因此这两种操作的最坏情况为O(N。
如果所有的操作都发生在表的末尾,就不需要移动任何元素,那么添加和删除的操作都花费O(1)的时间
在许多情况下,表是通过在末尾插入元素来建立的,之后只有数组访问(例如 finakth操作)
发生。在这种情况下,数组是适合的。然而,如果插入和删除在整个表中都发生,特别是在表的前端发生的话,数组就不是一个好选择了。下一节讨论另一种选择:链表
3.2.2简单链表
为了避免插入和删除的线性开销,我们需要允许表可以不连续存储,否则表的部分或全部就需要整体移动。图3-1表达了链表(linked list)的一般思想。
在这里插入图片描述
链表由一系列不必在内存中相连的结点组成。每一个结点均含有表元素和到包含该元素后继元的结点的链(link)。我们称之为next链。最后一个单元的next链指向NUL。
与数组实现一样,遍历显然是花费线性时间的,但是这个常数可能比用数组实现时要大。
remove方法可以通过修改一个next引用来实现。
在这里插入图片描述
lnsert方法需要使用new操作符从系统取得一个新结点,此后执行两次引用调整。
在这里插入图片描述
如果知道发生变化的位置,从链表中插入和删除一个元素不需要移动很多的元素,而只是对结点链接进行固定的几个改变。
删除最后一项有点麻烦,因为必须找到最后项前面的项,更改其next链接到NUll,然后更新这个保持为最后一项的链接。在经典的链表里每个结点存储指向下一结点的链接,但是没有提供关于上一个结点的任何信息。
我们将链表中的每一个结点都添加一个指向上一项的链接。如图34所示,这称为双向链表(doubly linked list)
在这里插入图片描述
3.3STL中的向量和表
在C+语言的库中包含有公共数据结构的实现。C++中的这部分内容就是众所周知的标准模板库(Standard Template Library,STL)。表ADT就是在STL中实现的数据结构之一。其他的数据结构我们将在第4章中介绍。一般来说,这些数据结构称为集合(collection)或容器(container)。

表ADT有两个流行的实现。vector给出了表ADT的可增长的数组实现。使用 vector的优点在于其在常量的时间里是可索引的。缺点是插入新项或删除已有项的代价是昂贵的,除非是这些操作发生在 vector的末尾。1ist提供了表ADT的双向链表实现。使用1ist的优点是,如果变化发生的位置已知的话,插入新项和删除已有项的代价是很小的。缺点是1ist不容易索引。vector和1ist两者在查找时效率都很低。在本讨论中,list总是指STL中的双向链表,而“表”则是指更一般的ADT表
公共方法:
int size()const:返回容器内的元素个数
void clear():删除容器中所有的元素。
bool empty():如果容器没有元素,返回true,香则返回 false。
vector和list两者都支持在常量的时间内在表的末尾添加或删除项。vector和list两者都支持在常量的时间内访问表的前端的项。
void push_back(const object &x):在表的未尾添加x。
void pop back():删除表的末尾的对象。
const object &back()const:返回表的末尾的对象(也提供返回引用的修改函数)
const Object &front()const:返回表的前端的对象(也提供返回引用的修改函数)。
因为双向链表允许在表的前端进行高效的改变,但是 vector不支持,所以,下面的两个方法仅对1ist有效
void push front(const object &x):在1ist的前端添加x
void pop front():在1ist的前端删除对象
vecto也有1ist所不具有的特有的方法。有两个方法可以进行高效的索引。另外两个方法允许程序员观察和改变 vector的内部容量。这些方法是
Object & operator[](int idx):返回 vector中idx索引位置的对象,不包含边界检测(也提供返回常量引用的访问函数)。重载中括号
Object & at(int idx):返回 vector中i∝索引位置的对象,包含边界检测(也提供返回常量引用的访问函数)
int capacity()const:返回 vector的内部容量(详细信息参见3.4节)。
void reserve(int new Capacity):设定 vector的新容量。如果已有良好的估计的话,这可以避免对 vector进行扩展(详细信息见34节)。
3.3.1迭代器
一些表的操作,例如那些在表的中部进行插入和删除的操作,需要位置标记。在STL中,通过内置类型 iterator来给出位置。
对 vector,位置由类 vector::iterator给出
1.获得迭代器
iterator begin():返回指向容器的第一项的一个适当的迭代器
iterator end():返回指向容器的终止标志(容器中最后一项的后面的位置)的一个适当的迭代器。

在这里插入图片描述
这段代码引出了第二个问题,迭代器必须有关联的方法(这里未知的方法由??代替)。
2.迭代器方法
因此,迭代器有许多方法,并且许多方法都使用操作符重载。除了复制以外,迭代器的最常见的操作如下:
itr++和++itr:推进迭代器itr至下一个位置。前缀和后缀两种形式都是允许的。
*itr:返回存储在迭代器ix指定位置的对象的引用。返回的引用或许能、或许不能被修改(后面我们将讨论这些细节)。
itr1==itx2:如果ir1和itr2都指向同一个位置就返回true,否则,返回 false。
itr1!=itr2:如果itr1和ir2都指向不同位置就返回true,否则,返回fa1se。

在这里插入图片描述
在这里插入图片描述
3.需要迭代器的容器操作
特定位置添加或删除项:
iterator insert(iterator pos,const Object& x):添加x到表中迭代器pos所指向的位置之前的位置。这对1ist是常量时间操作,但是对 vector则不是。返回值是个指向插入项位置的迭代器
iterator erase(iterator pos):删除迭代器所给出位置的对象。这对1ist来说是常量时间操作,但对 vector则不是。返回值是调用之前pos所指向元素的下一个元素的位置这个操作使pos失效。pos不再有用,因为它所指向的容器变量已经被删除了。
iterator erase(iterator start,iterator end):删除所有的从位置 start开始、直到位置end(但是不包括ena)的所有元素。注意,整个表的删除可以调用c.erase(c begin(),c,end())
3.3.2示例:对表使用erase
在这里插入图片描述
对list线性,对vector平方算法
3.3.3const_iterator
itr的结果不只是迭代器指向的项的值,也是该项木身。这个区别使得迭代器的功能很强,但也使其更复杂。
在这里插入图片描述
这段代码是非法的,并且不会被编译。STL提供的解决方案是每一个集合不仅包含嵌套的iterator类型,也包含嵌套的 const_a terator类型。iterator和 Const iterator之间的主要区别是:const_iterator的 operator
返回常量引用,这样 const_iterator的*itr就不能出现在赋值语句的左边。
更进一步地,编译器还会要求必须使用 const iterator类遍历常量集合

在这里插入图片描述
两个版本的 begin可以在同一个类里面,因为方法(例如,无论是访问函数还是修改函数)的定常性被认为是标号的一部分。
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值