上一文中,我們學習了陣列和鍊錶,它們兩個是儲存資料的最底層結構,是功能完全的線性表。棧和佇列是受限的線性表,啥叫功能完全,功能受限呢?陣列和鍊錶,我們可以對裡面任意位置上的元素進行任意的操作,不受任何限制,而棧和佇列,其內部也是陣列或鍊錶實現,但是對外暴露的操作介面是有限的,棧只能在棧頂進行壓棧和出棧操作,佇列只能隊尾插入,隊頭出隊操作。
棧和佇列的結構示意
為啥有了功能全面的,更加靈活的陣列和鍊錶了,為啥還要搞功能受限的結構出來呢?
這是因為在特定的應用場景下, 棧和佇列用起來更加簡單,也跟貼近業務含義。
為了加深理解,我們通過陣列實現乙個簡單的棧。
/**
* 棧介面
*/public
inte***ce
mystack
基於陣列的棧實現:
/**
* 基於陣列實現的棧
*/public
class
arraystack
implements
mystack
public
arraystack
(int capacity)
@override
public
void
push
(item item)
private
void
needresize()
else
if(position < elements.length /
1.75
&& elements.length > capacity)
}/**
* 重置陣列大小
* @param newsize
*/private
void
resize
(int newsize)
elements = temp;
}@override
public item pop()
item topelement = elements[position]
; elements[position]
= null;
//清除引用,避免記憶體洩露。
position--
;//棧頂往下收縮
needresize()
;//是否需要縮容。
return topelement;
}@override
public
intsize()
@override
public
boolean
isempty()
public
static
void
main
(string[
] args)
}
有了以上棧的一些知識之後,我們來看下如何用棧來巧妙的解決括號匹配的問題,即判斷乙個字串,是否符合括號原則,比如 是符合的 } 不符合。
用棧來解決這個問題非常簡單,乙個乙個解析字串, 當發現是左括號之一時壓棧,當發現是右括號之一時,與棧頂元素匹配,如果是一對,則消掉,如果不匹配,則表示式不合法。你可以在本子上畫圖理解一下。
這個問題的**如下:
/**
* 符號匹配檢測工具
* 判斷乙個字串,是否符合括號原則,比如
* ok } not ok。
* * 思路,利用棧實現 o(n)
* 乙個乙個解析字串, 當發現是左括號之一時壓棧,
* 當發現是右括號之一時,與棧頂元素匹配,如果是一對,則消掉,如果不匹配,則表示式不合法。
* 整個字串解析完後,如果棧為空,合法;如果不為空,表名左括號多了,不合法。
*/public
class
symbolmatchtool
break
;case
'}':if(
!istopeleischaracter
(stack,
'break
;case
')':if(
!istopeleischaracter
(stack,
'(')
)break
;default
:break;}
}if(stack.
isempty()
)return
false;}
private
static
boolean
istopeleischaracter
(mystack
stack,character target)
return
true;}
public
static
void
main
(string[
] args)
]]";
system.out.
println
(symbolmatchtool.
ismatched
(s))
; s =
"[[sdfalajf()asdjf;asfdlkja;f2323]]"
; system.out.
println
(symbolmatchtool.
ismatched
(s))
; s=
"()((}"
; system.out.
println
(symbolmatchtool.
ismatched
(s));}
}
另外乙個使用棧的經典問題就是 字串表示式求值 ,比如給你乙個字串「10 + 23 * 5 - 4/8」 這樣乙個字串,你怎麼將它計算出來呢?
下面我給出下思路, 你可以花點時間自己實現一波。
* 表示式求值計算。
* 不考慮()的情況。只支援加減乘除操作
** 簡化版,如果只有一種優先順序操作,比如只有加減計算,那麼只需要乙個棧就可以實現。
* 乙個乙個解析字串的表示式,如果是符號,則壓棧;
* 如果是數字,判斷棧是否為空棧(為空表示第一次開始解析),為空壓棧,不為空則彈出兩個(乙個符號,乙個是前乙個運算元)
* 計算後壓棧回去。直到表示式被解析完成,結果也計算完成了。
** 加強版,如果操作符號有優先順序的情況,比如有加減乘除時,需要兩個棧才能實現。
* 思路:
* 乙個棧用於放運算元,乙個棧用於放操作符號。
* 乙個乙個解析字串的表示式,如果是數值,則在運算元棧壓棧;
* 如果是符號,那麼判斷符號的優先順序是不是高於 當前操作符號棧的棧頂符號優先順序,如果高於,則符號壓棧;
* 如果優先順序低於等於棧頂符號優先順序,則分別從兩個棧中彈出兩個運算元和乙個符號,計算後結果壓入運算元棧。
* 直到表示式被解析完成,此時需要判斷符號棧是否空(或者運算元棧多餘1個元素),
* 如果不為空,則重複分別從兩個棧中彈出兩個運算元和乙個符號,計算後結果壓入運算元棧,知道沒有符號為止。
*
思考題,如何設計乙個瀏覽器的前進和後退功能?
提示,用兩個棧。(最好是畫圖理解)
棧的特點是先進先出即first in first out (fifo),就好比我們排隊出站,先排的先出,後排的後出,非常好理解。
同樣,使用陣列來簡單實現乙個佇列,需要注意的是,陣列實現的佇列,入隊的時候陣列下標不能無限的往後加吧,因此需要通過控制,迴圈的使用前面已經出隊的空間,同時也可以控制佇列的容量。
/**
* 基於陣列實現的迴圈佇列
* @param */
public
class
arrayqueue
implements
myqueue
@override
public
void
put(item item)
@override
public item pop()
item item = data[head]
; head =
(head+1)
% cap;
//下標對映
return item;
}@override
public
boolean
isempty()
@override
public
intsize()
public
static
void
main
(string[
] args)
}
佇列的應用就非常廣泛了,小到我們自己內部應用的佇列使用,比如執行緒池中的阻塞佇列;大到訊息中介軟體,如rabbitmq,rockmq,kafka等,都是使用了佇列的思想。
那麼,你能使用鍊錶來實現乙個自己的佇列嗎?它的實現比陣列簡單些。
js資料結構和演算法(二)棧和佇列
棧和佇列都是動態的集合,在棧中,可以去掉的元素是最近插入的哪乙個。棧實現了後進先出。在佇列中,可以去掉的元素總是在集合中存在的時間最長的那乙個。佇列實現了先進先出的策略。棧的官方定義 棧 stack 是乙個後進先出 last in first out,lifo 的線性表,它要求只在表尾進行刪除和插入...
資料結構與演算法 棧和佇列
棧 是限制在表的一端進行插入和刪除運算的線性表。棧又稱後進先出簡稱lifo表 佇列 也是一種運算受限的線性表。它只允許在標的一端進行插入,而在另一端進行刪除。佇列亦稱先進先出fifo表 1.棧與佇列的區別 1 佇列先進先出,棧先進後出。2 對插入和刪除操作的 限定 棧是限定只能在表的一端進行插入和刪...
資料結構與演算法 棧和佇列
棧 stack 有些地方稱為堆疊,是一種容器,可存入資料元素 訪問元素 刪除元素,它的特點在於只能允許在容器的一端 稱為棧頂端指標,英語 top 進行加入資料 英語 push 和輸出資料 英語 pop 的運算。沒有了位置概念,保證任何時候可以訪問 刪除的元素都是此前最後存入的那個元素,確定了一種預設...