3.6 栈 ADT - 3.7 队列 ADT
3.6 栈 ADT
栈是限制插入和删除只能在一个位置上进行的表,叫做栈的顶部。对栈的基本操作有进栈和出栈,进栈在顶部插入元素,出栈删除最后插入的元素。
栈是一个表,因此任何实现表的方法都能实现栈。显然 ArrayList 和 LinkedList 都支持栈操作;因为栈操作是常数时间操作,除非在非常特殊的情形下,不能产生明显改进。
栈的链表实现
在表的顶端或末端插入来实现进栈,删除顶端或末端实现出栈。
栈的数组实现
在表的末端插入实现进栈,删除末端实现出栈。
栈的应用
平衡符号
编译器检查程序语法错误时,经常由于符号错误导致,大量诊断报错。因为每个右括号必然对应其相应的左括号如{([])}是合法的,[({)]}是不合法的。通过栈可以方便的检查符号:
一个空栈,从文件头部读入字符到文件尾。如果字符是左半部分则进栈,如果字符是右半部分,当栈空时报错,否则将左半部分出栈。如果出栈的符号不是对应的左半部分则报错。文件读完,栈非空则报错。
后缀表达式
使用科学计算器计算时对于乘除法优先于加减法的记法。例如:
3 × 5 + 4 + 9 ÷ 3 = 22 将被记录为 3 5 × 4 + 9 3 ÷ +
当见到一个数时,将其进栈,遇到一个运算符该运算符作用于出栈的两个数上。再讲所得结果进栈。就可以不必知道优先规则而直接计算。
中缀到后缀的转换
除了用了计算后缀表达式,栈同样可以将标准形式表达式(中缀表达式)转换为后缀式。
读到操作数时立即将其放到后缀式中,操作符则进入栈中(包括括号)。当栈中存在高优先级操作符,且下个操作符是低优先级时,将高优先级操作符出栈到后缀式,低优先级操作符进栈。左括号进栈后在遇到右括号前不出栈。读到有括号时则一直出栈直到左括号出栈。括号出栈时不添加到后缀式中。
3.7 队列 ADT
队列也是表,基本操作是入队,在队尾插入一个元素;出队,在队头删除一个元素。
队列的数组实现
队列的链表实现很简单。
下面是使用数组实现的队列,用到了循环数组,否则数组的空间将被很快耗尽。
public class MyArrayQueue<E> {
private int front;
private int back;
private E[] queue;
private int theSize;
public static final int DEFAULT_CAPACITY = 10;
public MyArrayQueue() {
theSize = 0;
ensureCapacity(DEFAULT_CAPACITY);
front = queue.length / 2;
back = front;
}
/**
* 扩容数组的同时需要对队列进行拼接
*
* @param newCapacity 新数组长度2倍
*/
public void ensureCapacity(int newCapacity) {
if (newCapacity < theSize) {
return;
}
E[] old = queue;
queue = (E[]) new Object[newCapacity];
if (back < front) {
for (int i = front; i < theSize; i++) {
queue[i] = old[i];
}
for (int i = 0; i < back + 1; i++) {
queue[theSize + i] = old[i];
}
back = front + theSize - 1;
} else {
for (int i = 0; i < theSize; i++) {
queue[i] = old[i];
}
}
}
public void enqueue(E e) {
if (queue.length == theSize) {
ensureCapacity(theSize * 2);
}
if (theSize != 0) {
if (back == queue.length - 1) {
back = 0;
} else {
back++;
}
}
queue[back] = e;
theSize++;
}
public E dequeue() {
if (theSize == 0) {
return null;
}
E returnVal = queue[front];
if (front == queue.length - 1) {
front = 0;
} else {
front++;
}
theSize--;
return returnVal;
}
}

浙公网安备 33010602011771号