数据结构读书笔记(一)——表、栈和队列

来源:互联网 发布:js数组each遍历 编辑:程序博客网 时间:2024/05/22 06:41

1.1 抽象数据类型

抽象数据类型(abstract data type, ADT)是带有一组操作的一些对象的集合。


1.2 表ADT

1.2.1 表的简单数组实现

对表的所有操作都可以通过使用数组来实现。数组的实现可以使得printList以线性时间被执行,而findKth操作则花费常数实现。不过,插入和删除的花费却有昂贵的开销,这要看插入和删除发生在什么地方。

1.2.2 简单链表

链表由一系列节点组成,这些节点不必在内存中相连。每一个节点均含有表元素和包含该元素后继元节点的链(link)。称之为next链。最后一个单元的next链引用null。

执行printList或find(x),只要从表的第一个节点开始然后用一些后继的next链遍历该表即可。这种操作显然是线性的,find操作实现不如数组实现时的效率高。

如果知道变动将要发生的地方,那么向链表中插入或从链表中删除一项的操作不需要移动很多项,而只涉及常数个节点链的改变。


1.3 Java Collections API中的表

1.3.1 Collection接口

Collections API位于java.util包中。集合(collection)概念在Collection接口中得到抽象,它存储一组相同类型的对象。

Collection接口扩展了Iterable接口。

1.3.2 Iterator接口

实现Iterable接口的集合必须提供一个称为iterator的方法,该方法返回一个Iterator类型的对象。该Iterator是一个在java.util包中定义的接口。

Iterator的remove方法的优点在于,Collection的remove方法必须先找出要被删除的项。

当直接使用Iterator时(而不是通过一个增强的for循环间接使用),重要的是要记住一个基本法则:如果对正在被迭代的集合进行结构上的改变(如add,remove或clear),那么该迭代器就不再合法(抛出ConcurrentModificationException异常)。

1.3.3 List接口、ArrayList类和LinkedList类

List ADT有两种流行的实现方式。ArrayList类提供了List ADT的一种可增长数组的实现。使用ArrayList的优点在于,对get和set的调用花费常数时间。其缺点是新项的插入和现有项的删除代价昂贵,除非变动在ArrayList的末端进行。LinkedList类则提供List ADT的双链表实现,使用LinkList的优点在于,新项的插入和现有项删除均开销很小,这里假设变动项的位置是已知的。这意味着,在表的前端进行添加和删除都是常数时间的操作。使用LinkList的缺点是它不容易做索引,因此对get的调用是昂贵的,除非调用非常接近表的端点。


1.4 LinkedList类的实现

addBefore方法描述如下:

p.prev = p.prev.next = new Node(x, p.prev, p);

删除一个节点的逻辑:

p.prev.next = p.next;

p.next.prev = p.prev;


1.5 栈ADT

1.5.1 栈模型

栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈的顶(top)。对栈的基本操作由push和pop,前者相当于插入,后者则是删除最后插入的元素。最后插入的元素可以通过top例程在执行之前进行考查。对空栈进行pop和top一般被认为是栈ADT中的一个错误。另外,当运行push时空间用尽是一个实现限制,但不是ADT错误。

栈有时候又叫LIFO表。

1.5.2 栈的实现

由于栈是一个表,因此任何实现表的方法都能实现栈。ArrayList和LinkedList都支持栈操作,大部分时候它们都是合理的选择。

1.5.3 应用

平衡符号

做一个空栈。读入字符直到文件末尾。如果一个字符是一个开放符号,则将其推入栈中。如果字符使一个封闭符号,则当栈空时报错。否则,则将栈元素弹出。如果弹出的符号不是对应的开放符号,则报错。在文件结尾,如果栈非空则报错。

后缀表达式

后缀记法也叫逆波兰记法。计算这个问题最容易的方法就是使用一个栈。当见到一个数时就把他推入栈中,在遇到一个运算符时,该运算符就作用于从栈中弹出的两个数上,再将所得的结果推入栈中。

计算一个后缀表达式花费的时间是O(N),因为对输入中的每个元素的处理都是由一些栈操作组成从而花费常数时间。当一个表达式以后缀形式给出时,没有必要知道任何优先的规则,这是一个明显的优点。

中缀到后缀的转换

当读到一个操作数时,立即把它放到输出中。将已经见到过但尚未输出的操作符推入栈中。当遇到左括号也将其推入。

如果见到一个右括号,那么将栈元素弹出,将弹出的符号写出直至遇到一个左括号,但括号只弹出不输出。

如果见到其他任何的符号,那么从栈中弹出栈元素直到发现优先级更低的元素为止。除非是在处理一个)时,否则不从栈中移走(。对于这种操作,+的优先级最低,而(的优先级最高。当从栈弹出元素的工作完成后,我们再将操作符压入栈中。

读到输入的末尾,将栈元素弹出直到该栈变成空栈,将符号写到输出中。


1.6 队列ADT

队列(queue)也是表。使用队列时插入在一端进行而删除在另一端进行。

1.6.1 队列模型

队列的基本操作是enqueue,它是在表的末端(也叫队尾rear)插入一个元素,和dequeue,它是删除(并返回)在表的开头(对头front)的元素。