Java的順序棧和鏈式棧
棧的定義
棧是限制在表的一段進行插入和刪除的運算的線性表,通常能夠將插入、刪除的一端為棧頂,例外一端稱為棧底,當表中沒有任何元素的時候稱為空棧。
通常刪除(又稱“退棧”)叫做彈出pop
操作,每次刪除的都是棧頂最新的元素;每次插入(又稱“進棧”)稱為壓入push
操作。
當棧滿的時候,進行push 操作,會上溢
,當空棧的時候進行退棧操作的時稱為下溢
。
上溢是一種出錯的情況,下溢可能是正常的情況處理。
堆棧的運算是按照後進先出的原則,簡稱LIFO
。
棧的基本運算定義:
- initStack:構造一個空棧;
- stackEmpty:判斷是否為空;
- stackFUll:判斷是否棧滿;
- push:進棧,將該元素壓入棧頂;
- pop:退棧,彈出棧頂元素,註意非空判斷;
- stackTop:去除棧頂元素,不改變指針。
做個簡單的Stack接口:
package com.wuwii.utils;
/**
* 堆棧
* @author Zhang Kai
* @version 1.0
* @since <pre>2017/12/14 22:51</pre>
*/
public interface Stack<E> {
/**
* 進棧
*
* @param element 進棧的元素
*/
void push(E element);
/**
* 彈出棧頂元素 ,並改變指針
*
* @return 棧頂元素
*/
E pop();
/**
* 返回棧頂元素 ,不改變指針
*
* @return 棧頂元素
*/
E topElement();
/**
* 判斷是否為空棧
*
* @return true為空棧
*/
Boolean isEmpty();
/**
* 清空棧
*/
void clear();
}
順序棧
就是符合LIFO運算規則的順序線性表。
package com.wuwii.utils;
/**
* 順序棧
* @author Zhang Kai
* @version 1.0
* @since <pre>2017/12/14 23:05</pre>
*/
public class ArrayStack<E> implements Stack<E> {
/**
* 初始化棧的默認大小
*/
private final int defaultSize = 10;
/**
* 棧的集合大小
*/
private int size;
/**
* 棧頂的位置
*/
private int top;
/**
* 元素存儲在數組
*/
private Object[] elements;
/**
* 初始化默認大小為10 的棧
*/
public ArrayStack() {
initStack(defaultSize);
}
/**
* 初始化指定大小的棧
* @param givenSize 指定棧大小
*/
public ArrayStack(Integer givenSize) {
initStack(givenSize);
}
/**
* 初始化棧
* @param givenSize 給定的棧大小
*/
private void initStack(Integer givenSize) {
size = givenSize;
top = 0;
elements = new Object[size];
}
/**
* 清空棧
*/
@Override
public void clear() {
top = 0;
}
/**
* 進棧
* @param element 進棧的元素
*/
@Override
public void push(E element) {
sizeCheckForPush();
elements[top++] = element;
}
/**
* 彈出棧頂元素 ,並改變指針
* @return 棧頂元素
*/
@Override
public E pop() {
sizeCheckForPop();
return (E) elements[--top];
}
/**
* 返回棧頂元素 ,不改變指針
* @return 棧頂元素
*/
@Override
public E topElement() {
sizeCheckForPush();
return (E) elements[top - 1];
}
/**
* 判斷是否為空棧
* @return true為空棧
*/
@Override
public Boolean isEmpty() {
return size == 0;
}
/**
* 在進棧的時候檢查
*/
private void sizeCheckForPush() {
if (top >= size) {
throw new RuntimeException("Stack overflow");
}
}
/**
* 退棧檢查
*/
private void sizeCheckForPop() {
if (isEmpty()) {
throw new RuntimeException("Stack is empty");
}
}
}
鏈式棧
符合LIFO運算規則的鏈式線性表。
package com.wuwii.utils;
/**
* @author Zhang Kai
* @version 1.0
* @since <pre>2017/12/15 12:58</pre>
*/
public class LinkStack<E> implements Stack<E> {
/**
* 鏈式單元
*/
private Node<E> top;
/**
* 初始化鏈式堆棧
*/
public LinkStack() {
initStack();
}
/**
* 初始化
*/
private void initStack() {
top = null;
}
/**
* 存儲單元
*/
private static class Node<E> {
E element;
Node<E> next;
Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
/**
* 進棧
*
* @param element 進棧的元素
*/
@Override
public void push(E element) {
top = new Node<E>(element, top);
}
/**
* 彈出棧頂元素 ,並改變指針
*
* @return 棧頂元素
*/
@Override
public E pop() {
checkEmpty();
E element = top.element;
top = top.next;
return element;
}
/**
* 返回棧頂元素 ,不改變指針
*
* @return 棧頂元素
*/
@Override
public E topElement() {
checkEmpty();
return top.element;
}
/**
* 判斷是否為空棧
*
* @return true為空棧
*/
@Override
public Boolean isEmpty() {
return top == null;
}
/**
* 清空棧
*/
@Override
public void clear() {
if (isEmpty()) {
return;
}
for (Node<E> x = top; x != null; ) {
Node<E> next = x.next;
x.element = null;
x.next = null;
x = next;
}
size = 0;
}
/**
* 檢查鏈式堆棧是否為空,為空拋出異常
*/
private void checkEmpty() {
if (isEmpty()) {
throw new RuntimeException("LinkStack is empty");
}
}
}
首先push 修改新產生的鏈表節點的next 域並指向棧頂,然後設置top 指向新的鏈表節點,pop則相反。
順序棧和鏈式棧的比較
實現鏈式棧和順序棧的操作都是需要常數時間,時間復雜度為O(1),主要從空間和時間復雜度考慮。
順序棧初始化的時候必須要給定指定大小,當堆棧不滿的時候,會造成一部分的空間浪費,鏈式棧變長,相對節約空間,但是增加了指針域,額外加大了數據結構的開銷。
當需要多個堆棧共享的時候,順序存儲中可以充分的利用順序棧的單向延伸,將一個數組可以存在兩個堆棧裏,每個堆棧從各自的棧頂出發延伸,這樣減少了空間的浪費。但只有兩個為堆棧的空間有相反的需求的時候才能使用。就是最好一個堆棧只能增加,一個只能減少。如果,兩個一起增加,可能造成堆棧的溢出。
如果在多個順序堆棧共享空間,一個堆棧滿了,其他可能沒滿,需要使用堆棧的LIFO 運算法則,將滿的堆棧元素向左或者右進行平移操作,這樣會造成大量的數據元素移動,使得時間的開銷增大。
相對來說,使用兩個堆棧共享一個空間是比較適宜的存儲方式,但是也增加了堆棧溢出的危險。
由於鏈式存儲結構的不連續性,什麽時候需要,就什麽時候去存儲,不存在溢出的問題,但是增加了結構的開銷,總體上來說浪費了空間,但是不需要堆棧共享,
Java的順序棧和鏈式棧