資料結構和演算法之二 棧和佇列

2021-10-06 13:07:39 字數 4609 閱讀 7754

上一文中,我們學習了陣列和鍊錶,它們兩個是儲存資料的最底層結構,是功能完全的線性表。棧和佇列是受限的線性表,啥叫功能完全,功能受限呢?陣列和鍊錶,我們可以對裡面任意位置上的元素進行任意的操作,不受任何限制,而棧和佇列,其內部也是陣列或鍊錶實現,但是對外暴露的操作介面是有限的,棧只能在棧頂進行壓棧和出棧操作,佇列只能隊尾插入,隊頭出隊操作。

棧和佇列的結構示意

為啥有了功能全面的,更加靈活的陣列和鍊錶了,為啥還要搞功能受限的結構出來呢?

這是因為在特定的應用場景下, 棧和佇列用起來更加簡單,也跟貼近業務含義。

為了加深理解,我們通過陣列實現乙個簡單的棧。

/**

* 棧介面

*/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 的運算。沒有了位置概念,保證任何時候可以訪問 刪除的元素都是此前最後存入的那個元素,確定了一種預設...