棧是一種先進後出的數據結構,咱們把容許插入和刪除的一端稱爲棧頂,另外一端稱爲棧底,不含任何元素的棧稱爲空棧。node
一、棧的操做端一般被稱爲棧頂,另外一端被稱爲棧底。 二、棧的插入操做稱爲進棧(壓棧|push);棧刪除操做稱爲出棧(彈棧|pop)。面試
根據棧的存儲方式,棧能夠分爲靜態棧(數組實現)和動態棧(鏈表實現)。算法
對於靜態棧,咱們通常經過數組來實現的。實現一個棧,裏面主要涉及到 2 種狀態和 2 種操做。數組
Java代碼實現靜態棧bash
首先咱們須要使用到一個數組來存儲數據,其次還須要一個變量用於棧的容量大小,還有一個變量來指示棧頂的數據。所以棧的內部數據結構以下所示:數據結構
private T data[];// 用數組表示棧元素
private int maxSize;// 棧空間大小(常量)
private int top;// 棧頂指針(指向棧頂元素)
複製代碼
對於棧的構造函數,咱們能夠以下表示:函數
public LJStack(int maxSize) {
this.maxSize = maxSize;
this.top = -1;
this.data = (T[]) new Object[maxSize];
}
複製代碼
對於棧空的判斷很簡單,只須要判斷 top 是否等於-1便可:測試
/**
* 判斷棧是否爲空
*
* @return
*/
public boolean isEmpty() {
return this.top == (-1) ? true : false;
}
複製代碼
對於棧滿的狀態判斷也很簡單,經過 top 的值是否等於棧的容量大小 -1 即:ui
/**
* 判斷棧是否爲滿了
*
* @return
*/
public boolean isFull() {
return this.top == this.maxSize - 1 ? true : false;
}
複製代碼
兩種狀態已經實現了,接下來咱們來看下兩種操做,對於入棧操做,核心就是數組的增長而已:this
/**
* 壓棧操做,將數據存入到棧中
*
* @param value
*/
public boolean push(T value) {
if (isFull()) {
return false;
} else {
data[++top] = value;
return true;
}
}
複製代碼
對於出棧來講就是將取出數組最後一個值:
/**
* 出棧操做
* @return
*/
public T pop() {
if (isEmpty()) {
return null;
} else {
return data[top--];
}
}
複製代碼
以上即是用數組來實現的靜態棧,咱們能夠進行簡單的測試一下:
LJStack<String> stack = new LJStack<String>(10);
System.out.println("是否棧空:" + stack.isEmpty());
stack.push("AAAA");
stack.push("BBBB");
stack.push("CCCC");
stack.push("DDDD");
stack.push("1111");
stack.push("2222");
stack.push("3333");
stack.push("4444");
stack.push("5555");
stack.push("6666");
stack.push("7777");
stack.push("8888");
System.out.println("是否棧滿:" + stack.isFull());
複製代碼
打印出結果是:
咱們觀察結果會發現,打印棧裏面的結果發現,居然沒有「7777」,「8888」,那是由於棧滿了,因此沒法入棧了。所以能夠判斷,上面咱們寫的代碼是正確的。
接着咱們來討論一下鏈式棧的實現。其實鏈式棧是經過鏈表實現棧,咱們能夠想象有個棧頂節點,當入棧的時候,原先的棧頂節點成爲新的節點後繼節點,新的節點成爲新的棧頂節點。同理,出棧的時候,將棧頂節點彈出,第二個節點成爲新的棧頂節點。
Java代碼實現鏈式棧
既然鏈式棧是經過鏈表來實現的,首先咱們須要構造一個鏈表的節點LJLinkNode,
public class LJLinkNode <T>{
private T data;//數據域
private LJLinkNode<T> next;//指針域
public LJLinkNode() {
this.data=null;
this.next=null;
}
public LJLinkNode(T data) {
this.data=data;
this.next=null;
}
public void setData(T data) {
this.data=data;
}
public T getData() {
return this.data;
}
public void setNext(LJLinkNode<T> next) {
this.next=next;
}
public LJLinkNode<T> getNext(){
return this.next;
}
}
複製代碼
具體實現以下:
private LJLinkNode<T> topLinkNode;// 棧頂節點
/**
* 初始化
*/
public LJLinkStack() {
this.topLinkNode = new LJLinkNode<T>();
}
/**
* 初始化
*/
public void initLinkStack() {
this.topLinkNode.setData(null);
this.topLinkNode.setNext(null);
}
複製代碼
判斷棧空狀態:
/**
* 判斷是否棧空
*
* @return
*/
public boolean isEmpty() {
return this.topLinkNode.getNext() == null;
}
複製代碼
壓棧操做:
/**
* 壓棧
* 當入棧的時候,原先的棧頂節點成爲新的節點後繼節點,新的節點成爲新的棧頂節點。
*
* @param node
*/
public void push(LJLinkNode<T> node) {
if (isEmpty()) {
this.topLinkNode.setNext(node);
} else {
node.setNext(this.topLinkNode.getNext());
this.topLinkNode.setNext(node);
}
}
複製代碼
出棧操做:
/**
* 出棧
* 出棧的時候,將棧頂節點彈出,第二個節點成爲新的棧頂節點。
* @return
*/
public LJLinkNode<T> pop() {
if (isEmpty()) {
// 棧空沒法彈棧
return null;
} else {
LJLinkNode<T> delNode = this.topLinkNode.getNext();// 取出刪除節點
this.topLinkNode.setNext(this.topLinkNode.getNext().getNext());// 刪除節點
return delNode;
}
}
複製代碼
咱們來測試一下寫的代碼:
LJLinkStack<String> linkStack = new LJLinkStack<String>();
System.out.println("棧是否爲空:" + linkStack.isEmpty());
linkStack.push(new LJLinkNode<String>("AAAAA"));
linkStack.push(new LJLinkNode<String>("BBBBB"));
linkStack.push(new LJLinkNode<String>("CCCCC"));
// 依次彈棧
System.out.println("彈棧順序:");
System.out.println(linkStack.pop().getData());
System.out.println(linkStack.pop().getData());
System.out.println(linkStack.pop().getData());
複製代碼
打印結果以下:
以上即是棧的靜態及動態實現方式。那麼這兩種方式有什麼具體運用呢?接着咱們來經過具體的面試題目來運用棧。
題目:匹配有效的括號
給定一個只包括 '(',')','{','}','[',']' 的字符串,判斷字符串是否有效。
有效字符串需知足:
左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
注意空字符串可被認爲是有效字符串。
示例 1:
輸入: "()"
輸出: true
示例 2:
輸入: "()[]{}"
輸出: true
示例 3:
輸入: "(]"
輸出: false
示例 4:
輸入: "([)]"
輸出: false
示例 5:
輸入: "{[]}"
輸出: true
複製代碼
來源:力扣(LeetCode) 連接:leetcode-cn.com/problems/va…