各位同學寫作業一辛苦了.

45  Download (0)

Full text

(1)

QUEUE & STACK

Michael Tsai

2010/10/8 作業二今天會放

上網站.

各位同學寫作業一辛苦了.

請發表對這次作業的感想.

(2)

今日菜單

複雜的KMP例子

上次沒有講完的sparse matrix

Stack

Queue

計算機

迷宮

(3)

Failure function

另外一個定義(recursive)

1, 0

1 1 where is the least int for which 1, if there is no satisfying the above

a b c a b d a b c a b c

f

a b c a b d a b c a b c

(4)

朋友三: 矩陣

27 3 4 6 82 2 109 64 11

怎麼存起來?

用二維陣列, a[row][col] 來存

加法乘法都很容易

(5)

很鬆的矩陣

15

11 3 22 15 6

91

28

浪費很多空間在存T_T

有沒有什麼節省空間的方法可以存?

(6)

很鬆的矩陣

15

11 3 22 15 6

91

28

存法:

index 0 1 2 3 4 5 6 7 8

Row 6 0 0 0 1 1 2 4 5

Col 6 0 3 5 1 2 3 0 2

value 8 15 22 ‐15 11 3 ‐6 91 28

(7)

空間省了, 但是…

如果要做transpose…   

直接把row & col的值交換? 

這樣就沒有照順序了 (以row為主)

index 0 1 2 3 4 5 6 7 8

Row 6 0 3 5 1 2 3 0 2

Col 6 0 0 0 1 1 2 4 5

15

11 3 22 15

6 91

28

15

113

91

22 6 28

15

(8)

<動腦時間>

怎麼用O(columns+elements)的時間做完transpose?

當不鬆的時候, 就變成O(columns+elements)

=O(columns+columns*rows)=O(columns*rows)

怎麼做? 當做回家習題 請看program 2.9 (p.78)

(9)

那麼, 矩陣乘法呢?

不順 (希望同樣column的會放在一起)

所以可以先Transpose

15

11 3 22 15

6 91

28

15 11 3 22 15

6 91

28

(10)

實際乘乘看

類似之前的”鬆多項式”加法

比較兩邊的row & col數

a<b  a移到下一項

a==b 可以相乘, 乘起來以後的值存起來

a>bb移到下一項

15

11 3 22 15

6 91

28

15 11 3 22 15

6 91

28

(11)

看看程式

program 2.10和2.11 (p.81)

這個程式比較複雜, 如果看不懂回家請再多花時間 

Time complexity = O(??)

複雜度也很複雜, 請也多花時間

(12)

Stack

Stack是蝦密碗糕?

一串有”順序”的列表

又叫Ordered List

有什麼特別的地方?

永遠從頂部拿與放

拿: Pop

放: Push

看一個例子

所以先放進去的, 會??出來?  

1 0

頂(Top) 底(Bottom) 4

1 0 5 4 1 0 4 1 0 3 4 1 0

(13)

Stack

所以是一個先進後出(First‐In‐Last‐Out)的資料結構

那麼, 支援那些動作呢? 各動作又需要什麼參數?

請同學們列舉:

做一個Stack

放(Push)

拿(Pop)

是不是空的?

是不是滿了?

(14)

例子一: 系統堆疊

被程式拿來儲存呼叫function的相 關資訊

放什麼? 叫做activation record或 stack frame

return address: 呼叫function的下一個 指令應該去的地方

previous frame pointer: 指到前一個 stack frame在stack裡面的位置(底)

local variables

看一個例子.

適不適用recursive call?

Stack overflow?

Previous frame pointer Return address Previous frame pointer

Return address Local variables Previous frame pointer

Return address sp

fp

(15)

怎麼implement一個stack?

請同學上來舉例說明

提示: Array這個好朋友又出現了

除了array以外, 還需要哪些東西?

紀錄頂部的index

怎麼implement四個operations?

Push

Pop

是不是空?

是不是滿?

(16)

滿了怎麼辦?

int stack[MAX_SIZE];

如果裡面有超過MAX_SIZE元素, 就滿了. T_T

如果一開始我用stack=(int*)malloc(sizeof(int)*MAX_SIZE);

如何把stack變大?

複習: realloc做什麼用的?

realloc相關決定:

要realloc成多大?

realloc完畢有沒有什麼要改的地方?

(17)

來一點複雜度分析

如果每次都realloc成原本的兩倍大

capacity 存現在的stack大小

realloc(stack, 2*capacity*sizeof(int));

capacity*=2;

問題: push n 次的時間複雜度為多少?

(18)

來一點複雜度分析

問題: push n 次的時間複雜度為多少?

假設: 

Push一個element是O(1)

Allocate一個element是O(1)

移動一個element是O(1)

第一小問題: 如果目前capacity是2 , 花在realloc的時間是多 少?

第一小問題的子問題: realloc是O(?)

Worst case: Allocate 2*capacity個element的位置, 然後移動 capacity個element的資料到新地方

答案??

(19)

來一點複雜度分析

第一小問題: 如果目前capacity是2 , 花在realloc的時間是多 少?

第一小問題的子問題: realloc是O(capacity)

那麼花在realloc的時間就是 ∑ 2 2 2

(20)

來一點複雜度分析

第二小問題: 會使得目前的stack大小為capacity=2 , 應該已 經push了幾次?

答案: 至少已經 2 次了.

2 2 4 2 Ο

喔耶.

<動腦時間: 回家作業> 如果每次增加為原本capacity的c倍,  c>1, 那麼複雜度會有變化嗎? 為什麼?

(21)

Queue

Queue是蝦密咚咚?

也是Ordered List

和Stack有什麼不一樣?

從尾(Rear)放, 從頭(Front)拿

拿: Delete

放: Add

所以先放進去的, 會??出來?  

5 4 1

0 頭(Front)

尾(Rear)

(22)

Queue

先放進去的, 會先出來

所以是First‐In‐First‐Out (FIFO)

那麼, 支援那些動作呢? 各動作又需要什麼參數?

請同學們列舉:

做一個Queue

放(Add)

拿(Delete)

是不是空的?

是不是滿了?

(23)

舉一個例子

Job scheduling

如果沒有使用priority的概念的話

先來的job就先做  FIFO的概念

問題:程式要求作業系統做一個job, 作業系統要照來的順序 決定誰先使用資源.

作法: 

程式發出要求的時候, 就放入queue中

資源空出來了, 就從queue拿出一個job來做

結果: FIFO

<動腦時間> 如果放入一個stack會怎麼樣?

(24)

那麼, 怎麼implement queue呢?

一樣是array的朋友

要記得頭跟尾的在array裡面的位置(index)

但是這次有點問題

滿了! 但是前面的空間沒有用到!

怎麼解決?

1 2 0 ‐5

頭 尾

1 2 0 ‐5 3

1 2 0

(25)

解決方式: 頭尾相接

課本叫它circular queue

這樣就可以保證queue儲存的element數目最多可以跟array大小一

什麼時候是空?

什麼時候是滿?

(能不能分辨空和滿?)

2 0 ‐5

頭 尾

0 ‐5 3

2 0

2 00 ‐5

5 0 ‐5 3

(26)

那麼, 如果要使用動態大小呢?

一樣使用realloc來要到一塊更大的記憶體

然後還有什麼工作要做?

realloc之後的樣子: 看起來不太對

請一位同學上來講解怎麼修正

5 0 ‐5 3

尾 頭

(27)

問題: 很多stack用一個array來存

問題: 如果現在我們有一個array, 但是我希望用這個array存 兩個stack, 請問要怎麼implement?

什麼時候stack滿了? 

push & pop的動作怎麼做?

問題: 那麼, 如果有n個stack, 又要怎麼存在一個array?

什麼時候stack滿了?

真的有滿嗎?

(28)

接下來講一些應用:

計算機問題

迷宮問題

(29)

應用一: 計算機

不是普通的計算機

題目: 如果打入一串如1+2*3‐5/(4+5)/5的算式,  請寫一個演算法來算出這串算式的結果.

怎麼寫呢?  好複雜T_T

(30)

先來看看算式長什麼樣子

1+2*3‐5/(4+5)/5

裡面有:

Operand – 1, 2, 3, 5, 4, 5, etc.

Operator – + * ‐ /

括號– (,)

特色1: 左到右(left‐to‐right associativity)

特色2:一般這種寫法叫做infix (operator夾在operand中間)

先後順序要operator去”比大小”

例如乘除是大, 加減是小, 那麼就先乘除後加減

(31)

別種寫法: postfix

把operator放到兩個operands的後面

例如 2+3*4 2 3 4 * +

a*b+5  ?

(1+2)*7  ?

a*b/c ?

(a/(b‐c+d))*(e‐a)*c?

a/b‐c+d*?

e‐a*c?

(32)

Postfix有什麼好處?

沒有括號

用stack幫忙就可以很容易地算出結果!

例子: 6 2 / 3 – 4 2 * +

從左邊讀過去

讀到6, 放6進stack (stack: 6)

讀到2, 放2進stack (stack: 6 2)

讀到/, 取兩個operands (6和2), 算6/2, 然後答案放回去stack (stack: 

3)

讀到3, 放3進stack (stack: 3 3)

讀到‐, 取兩個operands(3和3), 算3‐3, 然後答案放回去stack (stack: 0)

依此類推….(請同學上來繼續完成)

(33)

剩下來的”小問題”: infix to postfix

“小問題”: 怎麼把infix expression轉成postfix expression?

第一種方法: (適合紙上談兵)

1. 把整個expression

2. 把所有的operator都移到operand的後面去, 方便去除所有 括號

3. 去除所有括號

但是實作上要怎麼做呢? (不希望做兩次)

(34)

又是一個可以利用stack解決的問題

方法:

1. 從左至右讀取expression

2. 碰到operand就直接輸出

3. 碰到operator時比較stack頂上的operator和目前讀到的 operator哪一個比較”大” (precedence)

如果目前讀到的operator比較大, 就把operator放到stack裡

如果一樣大或者現在讀到的operator比較小, 就一直把stack裡面的 operator拿出來印出來, 一直到stack是空的或者現在的operator比 stack頂的operator大或者一樣

為什麼可以這樣做?

裡面括號需要先印出, 但是卻是後讀到

比較外面的先用stack記起來, FILO, 由內而外

(35)

例子一

請一位同學來解說 怎麼把a+b*c利用前述方法轉換成postfix 

那麼, 如果碰到括號怎麼辦?

(36)

括號

括號內的有優先性

因此當碰到右括號的時候, 就立刻把stack中的東西一直拿出來直 到碰到左括號為止

舉例: a*(b+c)

輸出的內容, stack內容(最右邊為top)

a

a, *

a, *(

ab, *(

ab, *(+

abc, *(+

abc+*

(37)

還有一些小問題

小問題1: 左括號的”大小”到底是多少?

碰到左括號一定要放進去stack 此時要是最大的

左括號的下一個operator一定要可以放進去此時要是最小 的

結論:

左括號有兩種”大小”

在stack內的時候優先性為最小

在stack內的時候優先性為最大

其他operator的優先性則不管在stack內外都一樣

(38)

還有一些小問題

Q: 如何確保stack空的時候, 第一次碰到的operator可放進去?

A: 在stack底部放入一個虛擬operator帶有最低的優先性

Q: 如何確保讀到expression最後的時候, 可以把stack裡面未 取出的operator都拿出來?

A: 字串最後可以加一個優先性最低的虛擬operator

(39)

例題

a+(b*c/(d‐f)+e)

用白板解說 (請同學? :P)

<回家作業> 請各位看一下page 137的program 3.15 以了解怎 麼寫infix prefix的轉換程式

作業預告: 會跟”prefix”相關

例子:   a*b+c  +*abc

(40)

最後來一個有趣的迷宮問題吧

迷宮: 0是路, 1是牆壁. 每一部可以往上、下、左、右和四個 斜角方向走一步.

問題: 怎麼找出一條路從(0,0)走到(7,7) ? (不一定要最短)

提示: 跟stack是朋友

0 1 2 3 4 5 6 7

0 0 1 1 1 1 0 1 1

1 0 0 0 0 0 0 0 1

2 1 1 1 1 1 1 1 0

3 1 0 0 0 0 0 0 1

4 0 1 1 1 1 1 1 1

5 0 0 0 1 1 0 1 0

6 0 1 0 1 0 1 0 1

(41)

走迷宮的時候, 人要怎麼走?

最重要的時候, 是碰到岔路的時候

先記起來, 選其中一條走走看

如果碰壁了 (一直沒有走到終點), 就退回最後一次碰到的岔 路, 換另外一條岔路

關鍵字: 最後一次碰到的岔路 (不是最先碰到的)

所以是先進後出使用stack

關鍵字: 換另外一條岔路

要記得上一次走過哪一條路了

(42)

一些細節

Q: 那麼, stack裡面要存什麼呢?

A: 

“岔路”的地方的

座標, 也就是(row, col)

試過那些岔路了(試到八個方向的哪個方向了)

Q: 要怎麼預防繞圈圈? (永遠出不來)

A: 標示所有已經走過的地方, 走過就不用再走了

<注意> 這是因為不用找”最短”的路

(43)

讓我們來寫algorithm

把(row_start, col_start, 第一個方向) 放入stack

while(stack不是空的) {

從stack拿出一組岔路點(row, col, dir)

while(還有別的dir還沒試) {

將(row, col)往dir方向移動, 得到(row_n, col_n, dir).

如果(row_n, col_n)就是終點, 則結束

如果(row_n, col_n)不是牆壁且沒有來過 {

標示(row_n, col_n)來過了

把(row, col, dir的下一個方向)放入stack

row=row_n; col=col_n; dir=第一個方向;

}

}

}

(44)

休息時間

(45)

周末快樂~

作業二今天會出

參考同學們的意見, 這次份量會減少

請及早開始 (程式作業不簡單)

Michael 辦公室在這一棟

Figure

Updating...

References

Related subjects :