栈是非常重要的数据结构,栈具有后进先出的特点。
在JVM内部,每个线程维护一个栈,对于每个方法调用,入栈一个元素,成为栈帧,当方法执行完成后,对应的栈帧出栈。
栈帧中,也包含一个栈,称为操作数栈。
一、定义栈
public interface Stack {
// 添加一个元素
void push(Item item);
// 删除最近添加的元素
Item pop();
// 栈是否为空
boolean isEmpty();
// 栈中的元素数量
int size();
}
二、数组实现
/**
* 数组实现
* @param
*/
public class ResizingArrayStack implements Stack, Iterable {
private Item[] a;
// 表示栈实际大小
int N;
/**
* 初始的数组容量为16
*/
public ResizingArrayStack(){ this(16); }
public ResizingArrayStack(int cap){ a = (Item[]) new Object[cap]; }
@Override
public void push(Item item) {
if(N == a.length) resize(2 * N);
a[N++] = item;
}
/**
* 扩容,每次扩2倍的空间
* @param size
*/
private void resize(int size) {
if(size <= 16){
return;
}
System.out.println("触发扩容,原容量: " + a.length + ", 扩容后:" + size);
Item[] temp = (Item[]) new Object[size];
for (int i = 0; i < N; i++) {
temp[i] = a[i];
}
a = temp;
}
@Override
public Item pop() {
if(N == a.length / 4) resize(a.length / 2);
return a[--N];
}
@Override
public boolean isEmpty() { return N == 0; }
@Override
public int size() { return N; }
// *********** 以下代码与算法实现无关,仅为方便测试使用 *************
@Override
public Iterator iterator() {
return new Iterator() {
private int i = N;
@Override
public boolean hasNext() { return i>0; }
@Override
public Item next() { return a[--i]; }
@Override
public void remove() { }
};
}
public void print(){
System.out.print("当前元素(自栈顶至栈底):\t");
Iterator iterator = iterator();
while (iterator.hasNext()){
System.out.print(iterator.next() + "\t");
}
System.out.println();
}
}
三、链表实现
/**
* 链表实现
* @param
*/
public class LinkStack implements Stack, Iterable {
private Node top;
private int N;
private class Node{ Item item;Node next;}
/**
* 入栈
* @param item
*/
@Override
public void push(Item item) {
Node node = new Node();
node.item = item;
node.next = top;
top = node;
N++;
}
/**
* 出栈
* @return
*/
@Override
public Item pop() {
Item item = top.item;
top = top.next;
N--;
return item;
}
@Override
public boolean isEmpty() { return N==0; }
@Override
public int size() { return N; }
//*********** 以下代码与算法实现无关,仅为方便测试使用 *************
@Override
public Iterator iterator() {
return new Iterator() {
Node temp = top;
@Override
public boolean hasNext() {
return temp != null;
}
@Override
public Item next() {
Item item = temp.item;
temp = temp.next;
return item;
}
};
}
public void print(){
System.out.print("当前元素(自栈顶至栈底):\t");
Iterator iterator = iterator();
while (iterator.hasNext()){
System.out.print(iterator.next() + "\t");
}
System.out.println();
}
}
四、测试结果
public class StackTest {
@Test
public void arrayStackTest(){
// ResizingArrayStack stack = new ResizingArrayStack<>();
LinkStack stack = new LinkStack<>();
System.out.print("初始化后, ");
stack.print();
String pop;
//压入元素to
System.out.print("入栈:to,");
stack.push("to");
stack.print();
//压入元素be
System.out.print("入栈:be,");
stack.push("be");
stack.print();
//压入元素or
System.out.print("入栈:or,");
stack.push("or");
stack.print();
//压入元素not
System.out.print("入栈:not,");
stack.push("not");
stack.print();
//压入元素to
System.out.print("入栈:to,");
stack.push("to");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//压入元素be
System.out.print("入栈:be,");
stack.push("be");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//压入元素that
System.out.print("入栈:that,");
stack.push("that");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//压入元素is
System.out.print("入栈:is,");
stack.push("is");
stack.print();
}
@Test
public void test2(){
ResizingArrayStack stack = new ResizingArrayStack<>();
for (int i = 0; i < 100; i++) {
stack.push(i);
}
}
}
测试结果如下:
test1
test2
五、多说一点
在网上搜索数组和链表的区别时,最常见的说法是:数组访问较快,插入操作较慢;链表访问操作慢,修改便捷。
在实现栈时,由于栈仅涉及在最后插入一个值、在最后删除一个值,所以在这里比较2种实现方式优劣时,不能按照上述方式描述。
个人认为,数组实现,劣势在于需要扩容操作;链表实现,需要额外维护一个指向next的链接,占用更多空间;总体来说,2种实现方式优劣不明显,均比较合适。