栈和队列是两种相像的经典的数据类型,都是对数据添加删除做限制的线性表。其不同之处就在于出栈(pop)和出列(dequeue)的方式。栈是先进后出,队列是先进先出。
<1>Java虽说没有所谓的指针,但其实对于每个对象的引用就是指针,所以,栈可以用链表来实现:
public class Stack {
Node first = null;
private class Node {//定义内部类,实现节点的数据类型
String value;
Node next;
}
public boolean isEmpty() {//判空函数
return first == null;
}
public void push(String item) {//入栈
Node oldfirst = first;
first = new Node();
first.value = item;
first.next = oldfirst;
}
public String pop() {//出栈
String item = first.value;
first = first.next;
return item;
}
public static void main(String[] args) {
Stack zhan = new Stack();
System.out.println(zhan.isEmpty());
zhan.push("hello");
zhan.push("world!");
System.out.println(zhan.pop());
}
}
当然,栈也可以用简单数组实现:
public class Stack {
String S[];
int n = 0;//代表了栈顶
public Stack(int capacity) {//要求客户端定义栈大小
S = new String[capacity];
}
public boolean isEmpty() {//判空函数
return n == 0;
}
public void push(String item) {//入栈
S[n++] = item;
}
public String pop() {//出栈
String item = S[--n];
S[n] = null;//这两行代码是细节,如果直接返回S[--n]数组中对对象的引用还存在,可能会造成内存浪费。
return item;
}
public static void main(String[] args) {
Stack zhan = new Stack(2);
System.out.println(zhan.isEmpty());
zhan.push("hello");
zhan.push("world!");
System.out.println(zhan.pop());
}
}
数组实现栈确实方便简单,但是它却必须规定大小,而在实际应用中我们的客户端往往不知道究竟需要多大的栈,所以我们可以对程序进行改进,动态改变数组大小:
public class Stack {
String S[] = new String[1];
int n = 0;
public boolean isEmpty() //判空函数
{
return n == 0;
}
public void push(String item) //入栈
{
S[n++] = item;
if( n == S.length )
resize(2*S.length);
}
public String pop() //出栈
{
String item = S[--n];
S[n] = null;
if( n == S.length/4)//如果判断是否等于S.length/2可能会出现“抖动”现象
resize(S.length/2);
return item;
}
private void resize(int capacity)//重规定数组大小
{
String copy[] = new String[capacity];
for( int i = 0; i<n; i++ )
copy[i] = S[i];
S = copy;
}
public static void main(String[] args) {
Stack zhan = new Stack();
System.out.println(zhan.isEmpty());
zhan.push("hello");
zhan.push("world!");
System.out.println(zhan.pop());
}
}
这样就解决了数组实现栈大小限定的弊端。
对于两种不同的实现,我们应当发现,数组的基本操作要比链表更快,这就使得在处理数据时整体上是数组实现的栈效率更高。不过当数组进行动态伸缩的时候,操作时间会明显变长。所以可以根据实际需求选择实现方法。
<2>对于队列的实现,基本上和栈类似,但是链表实现我们要维护两个节点:first和last,enqueue时从last节点入队,dequeue时从first节点出队:
public class Queue {
private Node first, last;
private class Node{
String value;
Node next;
}
public boolean isEmpty()//判空函数
{
return first == null;
}
public void enqueue(String value)//从last节点入队
{
Node oldlast = last;
last = new Node();
last.value = value;
last.next = null;
if(isEmpty())
first = last;
else
oldlast.next = last;
}
public String dequeue()//从first节点出队
{
String item = first.value;
first = first.next;
return item;
}
public static void main(String[] args) {
Queue duilie = new Queue();
System.out.println(duilie.isEmpty());
duilie.enqueue("hello");
duilie.enqueue("world!");
System.out.println(duilie.dequeue());
}
}
以下是数组实现:
public class Queue {
private int first, last;
String S[] = new String[1];
public boolean isEmpty()//判空函数
{
return first == last;
}
public void enqueue(String value)//入队
{
S[last++] = value;
if( last == S.length )
resize(2*S.length);
}
public String dequeue()//出队
{
String item = S[first];
S[first++] = null;
if( last == S.length/4 )
resize(S.length/2);
return item;
}
private void resize(int size)//此处重定义数组比栈稍微复杂,需要找到first节点,将其重新归零
{
String copy[] = new String[size];
int j = 0;
while( S[j] == null)
j++;
for( int i = 0; i<last; i++ )
copy[i] = S[i+j];
S = copy;
}
public static void main(String[] args) {
Queue duilie = new Queue();
System.out.println(duilie.isEmpty());
duilie.enqueue("hello");
duilie.enqueue("world!");
System.out.println(duilie.dequeue());
}
}
数组实现多了一些栈不需要考虑的因素,关于两种实现方法的性能分析同栈。
(关于背包的实现,就是一个不可以取出的栈,除掉pop方法即可。)