栈和队列
栈和队列不适合作为数据的记录工具,它们更多地是作为程序员的工具来运用。主要作为构思算法的辅助工具,而不是完全的数据存储工具。这些数据结构的生命周期比数组、链表等数据库类型的结构要短的多。在程序操作执行期间他们才被创建,通常用它们去执行某项特殊的任务;当完成任务后,它们就被销毁。
下面的StackX类,实现一个栈的功能:
class StackX
{
private int maxSize;
private char[] stackArray;
private int top;
public StackX(int max)
{
maxSize = max;
stackArray = new char[maxSize];
top=-1;
}
public void push(char j) //入栈,注意要先将top指针上移,然后将数据赋给新的top位置
{
stackArray[++top] = j;
}
public char pop() //出栈,先取得数据,然后将top下移
{
return stackArray[top--];
}
public char peek() //只查看栈顶的元素,top指针不动
{
return stackArray[top];
}
public boolean isEmpty()
{
return (top == -1);
}
}//end class StackX
对这个类的解释:
StackX是构造方法,根据参数规定的容量创建一个新栈。栈的域包括表示最大容量的变量、数组本身以及变量top,它存储栈顶元素的下标。
Push方法中将top值增加一,使它指向原顶端数据项的上面的一个位置,并在在这个位置上存储一个数据项,再次提醒,top是在插入数据项之前递增的。
Pop方法返回top标识的数据项值,然后top减一。这个方法有效地从栈中移除了数据项;虽然数据项仍然存在数组中,直到有新的数据项压入栈中覆盖这个数据项。但是它不能再被访问了。
Peek方法仅返回top所指的数据项的值,不对栈做任何改动。
IsEmpty方法在栈空时返回true,栈空时top值为-1。
一种典型的应用是栈用于解析某种类型的文本串。通常,文本串是计算机语言写的代码行,而解析它们的程序就是编译器。一个典型的例子是分隔符匹配程序,它从字符串中不断读取字符,每次读取一个字符。若发现它是一个左分隔符,将它压入栈中。当从输入中读到一个右分隔符时,弹出栈顶的左分隔符,并且查看它是否合右分隔符相匹配。如果它们不匹配,则程序报错。如果栈中没有左分隔符和右分隔符匹配,或者一直存在着没有被匹配的分隔符,程序也报错。这个方法的可行性在于,最后出现的左边分隔符需要最先匹配,这个规律符合栈的后进先出的特点。
下面是分隔符匹配程序的代码:
import java.io.*;
class StackX
{} 这个类的定义前面已经给出
class BracketChecker
{
private String input;
public BracketChecker(String in)
{
input=in;
}
public void check()
{
int stackSize = input.length();
StackX theStack = new StackX(stackSize);
for(int j=0;j<input.length();j++)//get chars in turn
{
char ch = input.charAt(j);
switch(ch)
{
case '{': //opening symbols
case '[':
case '(':
theStack.push(ch); //push them
break;
case '}': //closing symbols
case ']':
case ')':
if(!theStack.isEmpty()) //stack not empty
{
char chx=theStack.pop(); //pop and check
if((ch=='}' && chx!='{')||(ch==']' && chx!='[')||(ch==')' && chx!='('))
System.out.println("ERROR: "+ch+" at "+j);
}
else //prematurely empty
System.out.println("Error:"+ch+" at "+j);
break;
default:
break;
}//end switch
}//end for
//at this point, all characters have been processed
if(!theStack.isEmpty())
System.out.println("Error: missing right delimiter");
}//end check
}//end class Bracketchecker
//////////////////////////////////////////////////////
class BracketsApp
{
public static void main(String[] args) throws IOException
{
//System.out.println("Hello World!");
String input;
while(true)
{
System.out.print("Enter string containing delimiters:");
System.out.flush();
input = getString();//read a string from kbd
if(input.equals(""))//quit if [Enter]
break;
BracketChecker theChecker = new BracketChecker(input);
theChecker.check();
}
}//end main
public static String getString() throws IOException
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
}//end class BracketsApp
下面是一个队列的实现代码:
class Queue
{
private int maxSize;
private long[] queArray;
private int front;
private int rear;
private int nItems;
public Queue(int s)
{
maxSize = s;
queArray = new long[maxSize];
front = 0;
rear = -1;
nItems = 0;
}
public void insert(long j) throws Exception //put item at rear of queue
{
if(this.isFull()) //throw Exception if queue is full
throw new Exception("Can't insert because queue is Full");
if(rear == maxSize-1) //deal with wraparound
rear = -1;
queArray[++rear] = j; //increment rear and insert
nItems++; //one more item
}
public long remove() throws Exception //take item fron front of queue
{
if(this.isEmpty()) //throw Exception if queue is empty
throw new Exception("Can't remove because queue is empty!");
long temp = queArray[front++]; //get value and incr front
if(front == maxSize) //deal with wraparound
front = 0;
nItems--; //one less item
return temp;
}
public long peekFront() //peek at front of queue
{
return queArray[front];
}
public boolean isEmpty()
{
return (nItems == 0);
}
public boolean isFull()
{
return (nItems == maxSize);
}
public int size()
{
return nItems;
}
//////////////////////////////////////////
public static void main(String[] args)
{
//System.out.println("Hello World!");
Queue theQueue = new Queue(5);
try
{
theQueue.insert(10);
theQueue.insert(20);
theQueue.insert(30);
theQueue.insert(40);
}
catch(Exception ex)
{
ex.printStackTrace();
}
try
{
theQueue.remove();
theQueue.remove();
theQueue.remove();
//theQueue.remove();
//theQueue.remove();
}
catch(Exception ex)
{
ex.printStackTrace();
}
try
{
theQueue.insert(50);
theQueue.insert(60);
theQueue.insert(70);
theQueue.insert(80);
//theQueue.insert(90);
}
catch(Exception ex)
{
ex.printStackTrace();
}
while(!theQueue.isEmpty())
{
long n = 0;
try
{
n = theQueue.remove();
}
catch(Exception ex)
{
ex.printStackTrace();
}
System.out.print(n);
System.out.print(" ");
}
System.out.println("");
} //end main
} //end class Queue
解释:
insert()方法:运行的前提条件是队列未满,若插入已满队列时,会抛出异常。若可以插入,则是将rear(队尾指针)加一后,在队尾指针所指的位置处插入新的数据项。但是当rear指针指向数组的顶端,即maxSize-1位置的时候,在插入数据之前,它必须回绕到数组的底端。回绕操作把rear设置为-1,因此当rear加1后,它等于0,是数组底端的下标值。最后年Items加1。
remove()方法:前提是队列不空,若对空队列执行此操作时,会抛出异常提示。若不空,由front指针得到队头数据项的值,然后将front加1。但是如果这样做是front的值超过数组的顶端,front就必须绕回到数组下标为0的位置上。作这个检验之前,先将返回值临时村到,最后年Items减1。
peek()方法:返回front指针所指数据项的值。isFull()、isEmpty()、size()都依赖于nItems字段。
优先级队列是比栈和队列更专用的数据结构。它也有队头和队尾指针,也是从队头移除数据项。不过在优先级队列中,数据项按关键字的值有序,这样关键字最小的数据项总是在队头。数据插入的时候会按照顺序插入到合适的位置以确保队列的有序性。下面给出一种使用简单的数组实现优先级队列。这种实现方法插入比较慢,但是它比较简单,适用于数据量比较小,并且不是很注重插入速度的情况。代码如下:
class PriorityQ
{
//array in sorted order, from max at 0 to min at size-1
private int maxSize;
private long[] queArray;
private int nItems;
public PriorityQ(int s)
{
maxSize = s;
queArray = new long[maxSize];
nItems = 0;
}
public void insert(long item) //insert item
{
int j;
if (nItems==0) //if no items, insert at 0
{
queArray[nItems++] = item;
}
else //if items,
{
for (j=nItems-1;j>=0 ;j-- ) //start at end,
{
if (item>queArray[j]) //if new item larger,
{
queArray[j+1]=queArray[j]; //shift upward
}
else //if smaller,
{
break; //done shifting
}
} //end for
queArray[j+1] = item; //insert it
nItems++;
} //end else
} //end insert
public long remove() //remove minimum item
{
return queArray[--nItems];
}
public long peekMin() //peek at minimum item
{
return queArray[nItems=1];
}
public boolean isEmpty() //true if queue is empty
{
return (nItems == 0);
}
public boolean isFull() //true if full
{
return (nItems == maxSize);
}
///////////////////////////////////////////
public static void main(String[] args)
{
//System.out.println("Hello World!");
PriorityQ thePQ = new PriorityQ(5);
thePQ.insert(30);
thePQ.insert(50);
thePQ.insert(10);
thePQ.insert(40);
thePQ.insert(20);
while (!thePQ.isEmpty())
{
long item = thePQ.remove();
System.out.print(item + " "); //10,20,30,40,50
} //end while
System.out.println("");
} //end main()
} //end class PriorityQ
说明:
优先级队列不像前面介绍的Queue类那样必须有front和rear字段,它的front总是nItems-1,rear总是等于0。最复杂的就是insert()方法,它先检查队列中有没有数据项,如果没有,就插入到下标为0的单元里。否则,从数组顶部开始上移存在的比待插入数据还要小的数据项,直到找到第一个比待插入项大的数据项。然后将待插入项插入该项上面,并将nItems加1。