栈和队列是两种重要的数据结构。是线性结构,基本操作受到限制,他们是操作受限是一种限定性的数据结构
第四章 栈与队列
栈
- 栈的定义以及抽象数据类型
Stack:运算受限的线性表, 其限制是仅允许在表的一端进行插入和删除操作 ,允许插入的一端称为——栈顶,另一端称为“栈底”
后进先出表Last in FIsrt Out - 栈的顺序存储实现
顺序栈使用顺序存储结构实现的堆栈,即利用一组地址连续的存储单依次存放堆栈中的数据元素。
顺序栈的实现附设一个指针top来动态指示栈顶元素在数组中的位置。通常top可以用栈顶元素所在数组下标表示。特别的top=-1表示空栈
Stack顺序存储类实现:
public class StackArray implement Stack{
private final int LEN=8; //数组的默认大小
private Object[] elements; //数据元素数组
private int top; // 栈顶指针
//方法()
public StackArray(){}
public int getSize(){}
public boolean isEmpty(){}
......
}
对于基于数组实现的顺序栈,由于存在top指针,getSize(),isEmpty均可以在O(1)时间内完成
- 栈的链式存储实现
利用单链表存储线性表,利用单链表的头部作为顶栈,可以使用带头结点的单链表(结点的插入和删除都在头结点之后进行)也可以使用不带头结点的单链表(结点的插入和删除都在链表的首节点进行)
入栈操作
SLNode q = new SLNode(e,top); //结点 q 的 next 域指向 top,不管 top 是否为 Null
top = q;
Stack链式存储实现
public class StackSLinked implements Stack {
private SLNode top; //链表首结点引用
private int size; //栈的大小
public StackSLinked() {
top = null; size = 0;
}
method()....
}
队列
- 队列的定义及抽象数据类型
队列queue同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,在另一端进行删除。
插入数据元素的一端称为队尾,删除的一端称为队首。向尾端插入元素称为进队或入队,——先进先出表
###队列为空时或取队首元素跑出异常
public class QueueExpection extends RuntimeException{
public QueueEmptyExpection(String err){
super(err);
}
}
- 队列的顺序存储实现
1)一般数组:指针last指示队尾,对于添加元素,时间复杂度为O(1),对于删除队首元素,需要将数组中所有元素都向前移动一个位置,当数组的size是n时,执行删除操作需要O(n)的时间
2) 循环队列:利用循环数组实现的队列称为循环队列,将循环队列中从队首到队尾元素按照逆时针方向存放在循环数组中一段连续的单元内。
下面以图 4-3 所示的表示方法来说明这个问题。在图 4-3 中用队首指针front指向队首元
素所在的单元,用队尾指针rear指向队尾元素所在单元的后一个单元。如此在图 4-4(b)中
所示循环队列中,队首元素为e 0 ,队尾元素为e 3 。当e 4 、e 5 、e 6 、e 7 相继进入队列后,如图 4-4
(c)所示,队列空间被占满,此时队尾指针追上队首指针,有rear = front。反之,如果从图
4-4(b)所示的状态开始,e 0 、e 1 、e 2 、e 3 相继出队,则得到空队列,如图 4-4(a)所示,此
时队首指针追上队尾指针,所以也有front = rear。
仅凭front 与rear是否像相等,无法判断队列的状态是“空”还是“满”——两种解决方法
少使用一个存储空间 | 当队尾指针的下一个单元是队首指针所指单元,停止入队 |
---|---|
队列满时的条件 (rear+1)% capacity = front | |
增设一个标志 | 例如增设size变量表明队列中数据元素 |
如果size=Max表示队列满 |
少使用一个存储单元的循环队列实现
public class QueueArray implements Queue{
private static final int CAP=7; //队列默认大小
private Object[] elements ; //数据元素数组
private int capacity; //数组的大小 elements.length
private int front; //队首指针,指向队首
private int rear; //队尾指针,指向队尾后一个位置
public QueueArray(int cap){
capacity = cap + 1;
elements = new Object[capacity];
front = rear = 0;
}
public ints getSize() { //返回队列大小
return (rear-front+capacity)%capacity;
}
........数据元素入队,队首元素出队
}
注意: 成员变量CAP和capacity 的区别,实际的数组要比队列最大容量大1 capacity = elements.length
- 队列的链式存储实现
队列的链式存储使用单链表来实现,主要介绍带头结点的单链表:队首指针和队尾指针,
队首指针指向队首元素的前一个结点,即始终指向链表空的头结点,队尾指针指向队列当前队尾元素所在的结点。当队列为空时,队首指针与队尾指针均指向空的头结点。
队列链式存储的操作实现
public class QueueSLinked implements Queue {
private SLNode front;
private SLNode rear;
private int size;
public QueueSLinked() {
front = new SLNode();
rear = front;
size = 0;
}
public int getSize() { //返回队列的大小
return size;
......其余method()
}
//判断队列是否为空
堆栈的应用
堆栈特点:后进先出
- 进制转换
比如将十进制转换为八进制,重复直至N=0
X = N mod d //其中 mod 为求余运算
N = N div d //其中 div 为整除运算
最后得到的一系列余数就是转换后的结果。
例如:(2007) 10 = (3727) 8 ,其运算过程如下:
从低位到高位产生8进制的各个数位,越晚生成的数位需要越早来输出:后进先出!!
余数的生成使用堆栈来存储 - 括号匹配检测
表达式中有三种形式的括号:圆括号,方括号和花括号,可以任意相互嵌套
该问题可按“期待匹配消解”的思想来设计算法,对表达式中的每一个左括号都期待一个相应的右括号与之匹配,表达式中越迟出现并且没有得到匹配的左括号期待匹配的程度越高。不是期待出现的右括号则是非法的。它具有天然的后进先出的特点。
public boolean bracketMatch(String str) {
Stack s = new StackSLinked();
for (int i=0;i<str.length();i++)
{
char c = str.charAt(i);
switch (c)
{
case '{': //等待匹配同类型的右括号
case '[':
case '(': s.push(Integer.valueOf(c));
break;
case '}':
if (!s.isEmpty()&& ((Integer)s.pop()).intValue()=='{')
break;
else return false;
case ']':
if (!s.isEmpty()&& ((Integer)s.pop()).intValue()=='[')
break;
else return false;
case ')':
if (!s.isEmpty()&& ((Integer)s.pop()).intValue()=='(')
break;
else return false;
}
}
if (s.isEmpty()) return true;
else return false;
}
- 迷宫求解