棧(stack)又名堆棧,它是一種運算受限的線性表。限定僅在表尾進行插入和刪除操作的線性表。這一端被稱爲棧頂,相對地,把另一端稱爲棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成爲新的棧頂元素;從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成爲新的棧頂元素。棧最大的特點就是先進後出
上面是棧存取數據的示意圖,棧主要分爲兩種結構:基於數組實現的數組棧和基於鏈表實現的鏈棧,總的來說都是棧結構,實現的都是棧的基本操作,而棧的操作需要具體的結構去實現,因此數組棧和鏈棧都是實現的棧接口,我們先定義一個棧接口:
interface Stack {
public abstract void push(int e); //入棧操作
public abstract int pop(); //彈棧
public abstract int length(); //有效元素個數
public abstract boolean isEmpty(); //判空操作
}
數組棧/順序棧:由數組實現的,但是不提供角標的訪問,只在棧頂進行操作。
/*
基於數組實現的棧
*/
class ArrayStack implements Stack{
private static int DEFAULT_SIZE=10; //默認容量
private int top; //棧頂指針
private int[] data; //存儲元素的容器用數組存儲
//創建一個棧使用默認容量
public ArrayStack(){
this(DEFAULT_SIZE);
}
//創建一個棧使用用戶指定容量capacity
public ArrayStack(int capacity){
super();
this.top=-1; //初始化棧頂元素
this.data=new int[capacity]; //初始化容量
}
//入棧一個元素e
public void push(int e){
if(top==data.length-1){ //入棧前要判滿,如果棧頂指針達到容量最大,表示無法入棧
System.out.println("棧已滿!無法入棧元素");
}
data[++top]=e; //否則先將棧頂指針先向上移動一個,將元素添加到該指針位置
}
//出棧一個元素
public int pop(){
if(isEmpty()){ //出棧前先判空
System.out.println("棧已空!無法出棧元素"); //棧內爲空,沒有元素
return -1;//特殊含義 表示錯誤
}
return data[top--]; //棧不爲空,先將元素彈棧,再將棧頂指針下移
}
//判斷棧是否爲空
public boolean isEmpty(){
return this.top==-1; //如果當前棧頂元素爲空,爲true否則爲false
}
//獲取有效元素的個數
public int length(){
return this.top+1; //獲取當前棧頂指針加一,因爲是用數組實現的
}
public String toString(){ //以字符串形式打印當前棧內元素
StringBuilder sb=new StringBuilder();
sb.append('[');
for(int i=0;i<=top;i++){
sb.append(data[i]);
if(i==top)
return sb.append(']').toString();
sb.append(',');
sb.append(' ');
}
return "[]";
}
//獲取當前棧的總容量
public int getCapacity(){
return data.length; //總容量就是數組的大小
}
}
鏈棧,是由鏈表實現的,鏈表由一系列結點(鏈表中每一個元素稱爲結點)組成,每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。 相比於線性表順序結構,操作複雜。由於不必須按順序存儲,鏈表在插入的時候可以達到O(1)的複雜度,比另一種線性表順序錶快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而線性表和順序表相應的時間複雜度分別是O(logn)和O(1)。這裏是鏈棧結構,則存取完全可以根據棧結構對頭結點進行操作。
class LinkedStack extends Stack{ //單向鏈表
private Node head; //鏈表的頭結點
private int size; //鏈表長度
public LinkedStack(){
this.head=new Node();
this.size=0;
}
//加入結點是不用判滿的,因爲鏈表沒有容量限制
public void push(int e){ //在頭結點後添加一個新的元素結點
Node n=new Node(); //新創建一個結點
n.data=e; //將創建的結點中的數據域用來存放傳入的元素
n.next=head.next; //將創建的結點中的指針域用來存放當前頭結點的下一個結點的地址
head.next=n; //然後將新創建的結點的地址賦給頭結點的指針域
//head.next=new Node(e,head.next); 上面三行代碼可簡化成這一句
size++; //鏈表長度加一
}
public int pop(){ //元素出棧
if(isEmpty()){ //出棧判空
System.out.println("棧已空!不能出棧元素!");
return -1;
}
Node n=head.next; //元素不爲空,將頭元素的下一個元素提取出來
head.next=n.next; //將頭元素下一個結點的下一個結點的地址賦給頭元素的指針域
n.next=null; //將頭元素的下一個結點的指針域置爲null,與原鏈表斷離
size--; //相應的鏈表長度減一
return n.data; //將刪除的結點的數據返回
}
public int length(){ //獲取鏈表的長度
return size; //直接返回size,因爲這個變量記錄了鏈表的長度
}
public boolean isEmpty(){ //判空
return size==0&&head.next==null; //如果鏈表長度爲0和頭結點的後面沒有其他結點,則爲空
}
public String toString(){ //獲取鏈表得字符串形式輸出
if(isEmpty()){
return "[]";
}
Node p=head.next; //定義一個結點變量存放頭結點的下一個結點的地址
StringBuilder sb=new StringBuilder();
sb.append('[');
while(true){
sb.append(p.data); //將該結點的數據域添加到字符串中
if(p.next==null){
sb.append(']');
break;
}else{
sb.append(", ");
p=p.next; //再將下一個結點的地址賦給當前結點變量
}
}
return sb.toString();
}
//內部類:在類裏面定義的類
private class Node{ //結點類
int data; //數據域
Node next; //指針域
Node(){
this(0,null);
}
Node(int data,Node next){ //帶參的構造函數
this.data=data;
this.next=next;
}
}
}