3.1 栈
学习总结
栈(Stack)是一种操作受限的线性表,其插入和删除操作只允许在线性表的一端进行。允许进行操作的一端称为栈顶( top),不允许操作的一端则称为栈底( bottom)。栈的插入操作通常称为入栈(push),栈的删除操作则称为出栈(pop)。如果栈中无数据元素,则称为空栈。
栈就像一个向上开口的箱子,刚开始放入的物体会放置在底部(栈底),继续放置物体时一层一层摞上去,当从箱子里取东西时,最上面(栈顶)的会被最先拿走。因此,栈顶元素是最后入栈、最先出栈,栈底元素则是最先入栈、最后出栈,即栈具有后进先出(LIFO,LastIn First Out)的特征。栈结构如图3-1所示。
【注意】栈限定的是元素插入和删除操作的位置,元素插入和删除操作进行的时间并未限定。也就是说,出栈可以随时进行,只要出栈的是栈顶元素即可。例如,有3个元素a、b、c按顺序先后入栈并且每个元素只入一次栈,则出栈的次序可以有abc、acb、bac、bca.cba共5种。
3.1.1 栈的抽象数据类型
虽然栈的操作受限降低了其灵活性,但也更有效和更容易实现。栈的基本操作主要有创建栈、入栈、出栈、取栈顶元素、判断栈是否为空等。
栈抽象数据类型的Java接口声明如下:
//栈抽象数据类型,T表示数据元素的数据类型
public interface Stack<T> {
public void push(T t); //在栈的顶端插入元素T
public T pop(); //栈顶元素出栈,返回值为出栈的栈顶元素
public T peek(); //取栈顶元素,但栈顶元素不出栈
public boolean isEmpty(); //判断栈是否为空
}
Java类库中的java.util.Stack类就是栈结构的典型代表。
3.1.2顺序栈
栈作为一种特殊的线性表,同样可以采用顺序存储结构和链式存储结构。采用顺序存储结构的栈称为顺序栈(Sequential Stack)。
1.顺序栈的存储结构
顺序栈用一维数组来存储元素,约定数组下标为0的一端表示栈底,则另一端是栈顶,用变量top表示栈顶元素在数组中的位置,通常也把top称为栈顶指针。当栈中有一个元素时,top值为0,因此一般用top=-1表示栈空;设数组的长度为length,当top=length-1时,表示栈满。入栈时,top 加1,指向新的栈顶位置,新元素放置到栈顶;出栈时,先取出栈顶元素,然后 top减1。顺序栈操作示意图如图3-2所示,这里数组的长度为4。
2.顺序栈的基本操作
定义顺序栈类SeqStack 实现顺序栈接口 Stack,除了实现接口中相应操作方法外,定义泛型数组stack用于存储数据元素,定义变量 length表示栈的初始容量,定义整型变量top表示栈顶指针,指向栈顶元素位置。类 SeqStack 的代码描述如下;
//顺序栈类
public class SeqStack<T>implements Stack<T>{
private T]stack = null; //声明一维数组,表示顺序栈
private int length; //表示顺序栈的容量
private int top; //栈顶指针
//栈初始化
public SeqStack(){
stack=(T[])new 0bject [16]; //构造一维的泛型数组,默认大小为16
length=16;top = -1; //栈初始化,栈中没有元素
}
public SeqStack (int size) {
stack =(T[])new Object [size]; //构造一维的泛型数组,大小由参数决定
length=size;top = -1; //栈初始化,栈中没有元素
}
//实现栈接口的相关方法//元素入栈
@override
public void push(T t){ //判断栈是否已满,如果已满,则抛出异常;如果未满,则元素入栈
if(top == length -1){
throw new RuntimeException("栈已经满,元素不能再进栈");
} else{
stack[++top]-t; //栈顶指针先加1,再将元素放入栈顶位置
}
}
//元素出栈
@override
public T pop(){ //判断栈是否为空,若为空,则抛出异常;若不空,则元素出栈
if (this.isEmptyO)){
throw new RuntimeException("栈为空,没有元素出栈");
}else {
return stack[top--];//先将栈顶元素值获取返回,并将栈顶指针减1
//获取栈顶元素coverride
public T peek {
//判断栈是否为空,若为空,则抛出异常;若不空,则返回栈顶元素
if(this.isEmpty(){
throw new RuntimeException(”栈为空");
}else{
return stack[top]; //直接将栈顶元素值返回
}
}
//判断栈是否为空
@override
public boolean isEmpty(){
//ltop为-1,代表栈中没有元素,为空栈
return top==-l;
}
}
3.1.3 链式栈
栈的另外一种实现方式是采用链表,采用链式存储结构的栈称为链式栈,简称为链栈(Linked Stack)。
1.链式栈的存储结构
链栈一般用单链表实现,由于栈的插入、删除操作只能在一端进行,而对于单链表来说,本身是有头指针的,在头部插入、删除结点要比尾部相对容易,因此将不带头结点的单链表头部作为栈顶,单链表的头指针即为栈顶指针top。
2.链式栈的基本操作
跟单链表一样,链栈的结点包含数据域和指向下一个结点的指针域。构成链栈的基本结点可以描述如下:
public class LinkedNode<T> {
private T data;
private LinkedNode next;//构造方法
public LinkedNode( {
}
public LinkedNode (T data){
this.data =data;
this.next = null;
public LinkedNode(T data, LinkedNode<T> next) {
this.data =data;
this.next = next;
)
//省略相应属性的setter和getter方法
}
//链栈类
public class Linkedstack<T> implements stack<T>{
private LinkedNode<T> top; //代表栈顶指针
//栈初始化
public Linkedstack(){
top=null;
//将栈顶指针top置空,构造一个空链栈
}
//元素入栈override
public void push(T t){
//将新结点指向栈顶,再移动栈顶指针指向新结点,新结点成为栈顶元素
LinkedNode<T> node=new LinkedNode<T>(t);/*使用传递的进栈
数值,先构造新结点*/
node . setNext(top);//设置新结点指向栈顶指针top-node;
//设置栈顶指针指向新结点
}
//元素出栈override
publicT pop( {
/*判断链栈是否为空,若为空,抛出异常;若不为空,则取栈顶元素的值并
返回,修改栈顶指针指向下一个结点*/
if (this.isEmpty(O) {
throw new RuntimeException(”栈为空,没有元素可出栈");
}
TtopData=top.getData(; //获取栈顶元素的值
//修改栈顶指针,指向下一个结点
top=top.getNext();
return topData;
//获取栈顶元素
public T peek() {
/*判断链栈是否为空,若为空,抛出异常;若不为空,则取栈顶元素的值并
返回*/
if(this.isEmptyO){
throw new RuntimeException("栈为空,没有元素可出柱");
return top.getData();
//返回栈顶元素的值
)
//判断栈是否为空@override
public boolean isEmpty() {
if(top==nul1) return true;
else return false;
}
}
3.2 队列
队列和栈一样,也是操作受限的线性表。队列(Queue)是插入和删除操作在线性表的两端进行,一端允许插入操作,而另一端只允许删除操作,允许插入的一端称为队尾(Rear),允许删除的另一端则称为队头(Front)。队列的插入操作通常称为入队或进队,删除操作通常称为出队。如果队列中没有元素,则称为空队列。
由于队列的插入操作在队尾,删除操作在队头,因此最先入队也会最先出队,最后进队的最后出队,即队列
具有先进先出(FIFO, First In First Out)的特征。队列的结
构如图所示。
3.2.1队列抽象数据类型
队列作为一种特殊的线性表,插入和删除操作在两端进行,一般会使用队指针(front)和队尾指针(rear)表示队列操作的两端。队列的基本操作主要有建队列、入队、出队、取队头元素、判断队列是否为空等。
设计队列的抽象数据类型接口 Queue,其中的操作的数据元素用泛型描述代码如下:
//队列抽象数据类型,其中T表示队列中数据的类型
public interface Queue<T>{
public void enqueue(T t); //元素入队,在队尾插入元素Tpublic T dequeue();
//元素出队,队头元素出队
public T peek();
//取队头元素值,但队头元素不出队
public boolean isEmpty(); //判断队列是否为空
}
3.2.2 顺序队列
队列作为特殊的线性表,和栈一样有顺序存储和链式存储两种结构。队列的顺序存储称为顺序队列(Sequential Queue).
1.顺序队列
与顺序表类似,顺序队列用一个一维数组存放元素数据,用两个变量front和rear,分别表示队头和队尾的位置。定义顺序队列类 SeqQueue 实现队列接口Queue,其中成员变量描述如下:
public class SeqQueue<T>implements Queue<T>{
private T[]queue-null;//声明一维数组,存储队列元素
private int length; //数组的大小,表示队列的最大容量
private int front; //表示队头位置
private int rear; //表示队尾位置
//实现队列接口的相关方法
}
3.2.3 链式队列
在循环队列中,若队列已满,再有元素入队,就会出现“真溢出”现象,这时队列可用链式结构存储。队列的链式存储称为链式队列,简称链队。
1.链式队列的存储结构
链式队列其实就是单链表,只是操作限制了队尾插入元素,队头删除元素。通常使用带头结点的单链表实现,以使空队列和非空队列操作一致,便于实现。在链队中,需要设置队头(front)和队尾(rear)两个指针,分别指向链队的头结点和尾结点,如图3-15所示。当队头和队尾指针都指向头结点时,表示是一个空队列。