COMP9021 Principles of Programming WEEK9

1. Stack

stack的準(zhǔn)則:last in first out帮哈。創(chuàng)建stack的class如下:

class StackException(Exception):
    def __init__(self, message):
        self.message = message

class Stack:
    def __init__(self):
        self._data = []
    def peek_at_top(self):
    #因?yàn)閟tack是后入先出的膛檀,所以查看最頂上的元素是最后加入的
        if not self._data:
            raise StackException('Stack is empty')
        return self._data[-1]
    def pop(self):
        if not self._data:
            raise StackException('Stack is empty')
        return self._data.pop()
    def push(self, datum):
        return self._data.append(datum)
    def is_empty(self):
        return self._data == []

1.1 Stack Exercise1

檢查數(shù)學(xué)表達(dá)式的括號是否匹配。因?yàn)槔ㄌ柖际侵饘ζヅ淠锸蹋覂?nèi)側(cè)括號優(yōu)先級高于外側(cè)咖刃,后輸入的括號先匹配,所以適合用stack完成憾筏。

Stack的使用原則非常重要嚎杨,遇到實(shí)際問題時(shí),判斷問題的解決順訊氧腰,如果符合后進(jìn)先出枫浙,則可以使用Stack。

def check_well_balanced(expression):
    stack = Stack()
    parentheses = {')': '(', ']': '[', '}': '{'}
    #將需要匹配的括號創(chuàng)建一個(gè)key-value字典容贝,方便判斷
    for e in expression:
        if e in '([{':
            stack.push(e)
        #所有遇見的左括號都壓入棧中
        elif e in parentheses:
            try:
                if stack.pop() != parentheses[e]:
                    raise StackException('')
                #無論是否匹配自脯,stack.pop()都已經(jīng)彈出了棧中最外面的括號,不匹配自動(dòng)報(bào)錯(cuò)斤富,匹配則繼續(xù)
            except StackException as e:
                return False
                #如果不匹配膏潮,則返回False,并不拋出Exception的message
    return stack.is_empty()
    #最后還要判斷棧是否為空满力,如果不為空結(jié)果依然是False

1.2 Stack Exercise2

在進(jìn)行數(shù)學(xué)表達(dá)式運(yùn)算時(shí)焕参,括號的處理對計(jì)算機(jī)不友好轻纪,所以創(chuàng)建了一種postfix operation的方式來讓計(jì)算機(jī)清楚要進(jìn)行的運(yùn)算。例如:
(2 + 3) * 5叠纷,寫成postfix operation是2 3 + 5 *刻帚;2 * (3 + 5),寫成postfix operation是2 3 5 + *
因?yàn)槭呛笾眠\(yùn)算涩嚣,內(nèi)側(cè)括號優(yōu)先級高崇众,和Exercise1的分析一樣,依然適合stack的應(yīng)用航厚。

def evaluate_postfix_expression(expression):
    stack = Stack()
    operators = {'+': lambda x, y: x + y,
                         '-': lambda x, y: y - x,
                         '*': lambda x, y: x * y,
                         '/': lambda x, y: y // x}
    #創(chuàng)建operator的方法顷歌,key對應(yīng)的value是一個(gè)function,因?yàn)闂J呛筮M(jìn)先出幔睬,所以要注意減法除法的順序眯漩,同時(shí)除法要注意使用//,不然可能會(huì)把int轉(zhuǎn)變?yōu)閒loat麻顶。
    in_number = False
    #因?yàn)閑xpression是逐個(gè)讀取的赦抖,所以數(shù)字不能完整轉(zhuǎn)譯成需要的值,比如234辅肾,先讀入的是2队萤,然后是3,所以要判斷當(dāng)前數(shù)字是否在一個(gè)數(shù)字中宛瞄,default是不在
    for e in expression:
        if e.isdigit():
            if not in_number:
                stack.push(int(e))
                #因?yàn)楹罄m(xù)要做數(shù)字運(yùn)算浮禾,所以要不輸入的string類型轉(zhuǎn)變?yōu)閕nt
                in_number = True
            else:
                stack.push(stack.pop() * 10 + int(e))
                #如果前面已經(jīng)有數(shù)字,則把前面的數(shù)字從棧中推出份汗,10倍后加上當(dāng)前數(shù)字盈电,再壓入棧中。
        else:
            in_number = False
            if e in operators:
                try:
                    arg_1 = stack.pop()
                    arg_2 = stack.pop()
                    stack.push(operators[e](arg_1, arg_2))
                except StackException as e:
                    raise e
    try:
        value = stack.pop()
        if not stack.is_empty():
            raise StackExcpetion('Expression is not correct')
        return value
    except StackException as e:
        raise e

1.3 Stack Exercise3

算法中Depth First Search和stack非常契合杯活,優(yōu)先解決一條路徑上所有可能性匆帚,在當(dāng)下路徑下發(fā)現(xiàn)的新加入的節(jié)點(diǎn)會(huì)優(yōu)先搜索。

Tree

上圖的搜索流程如下旁钧,注意壓棧的順序是從右向左:

No.         1    2     3    4    5    6  ...
Stack       1    2     3    4    5    6 ...
                 4     4    5         11 ...
                 5     5              13 ...
Operation push1 pop1  pop2  pop3 pop4 pop5 ...
                push5 push3           push13
                push4                 push11
                push2                 push6

DFS的代碼實(shí)現(xiàn)基于Stack的方式如下:

T = {1: [2, 4, 5], 2: [3], 5: [6, 11, 13], 6: [7, 8, 10], 8: [9], 11: [12]}

def depth_fisrt_search():
    stack = Stack()
    stack.push([1])
    #將Tree root先壓入棧中
    while not stack.is_empty:
        path = stack.pop()
        #將一個(gè)節(jié)點(diǎn)的值從棧中取出并顯示
        print(path)
        if path[-1] in T:
            for child in reversed(T[path[-1]]):
                stack.push(list(path) + [child])
        #如果該節(jié)點(diǎn)有child吸重,則按照逆序方法把child壓入棧中,不同的是壓入的時(shí)候把前面做過的路徑也儲(chǔ)存了進(jìn)來
depth_first_exploration()
>>>
[1]
[1, 2]
[1, 2, 3]
[1, 4]
[1, 5]
[1, 5, 6]
[1, 5, 6, 7]
[1, 5, 6, 8]
[1, 5, 6, 8, 9]
[1, 5, 6, 10]
[1, 5, 11]
[1, 5, 11, 12]
[1, 5, 13]

上述代碼手動(dòng)輸入了一個(gè)tree T歪今,寫成自動(dòng)代碼是:

from collections import defaultdict

def tree():
    return defaultdict(tree)
#以tree作為type反復(fù)調(diào)用生成一個(gè)tree嚎幸,數(shù)據(jù)結(jié)構(gòu)是defaultdict

T = tree()
T[1][2][3] = None
T[1][4] = None
T[1][5][6][7] = None
T[1][5][6][8][9] = None
T[1][5][6][10] = None
T[1][5][11][12] = None
T[1][5][13] = None
#以每一條可以連通的路徑作為tree的生成過程,最終形成一個(gè)tree
T
>>>
defaultdict(<function __main__.tree>,
            {1: defaultdict(<function __main__.tree>,
                         {2: defaultdict(<function __main__.tree>, {3: None}),
                          4: None,
                          5: defaultdict(<function __main__.tree>,
                                      {6: defaultdict(<function __main__.tree>,
                                                   {7: None,
                                                    8: defaultdict(<function __main__.tree>,
                                                                {9: None}),
                                                    10: None}),
                                       11: defaultdict(<function __main__.tree>,
                                                   {12: None}),
                                       13: None})})})

基于tree的結(jié)構(gòu)update DFS的代碼如下:

def depth_first_exploration():
    stack = Stack()
    stack.push(([1], T[1]))
    #將tree root和后續(xù)的tree壓入棧中
    while not stack.is_empty():
        path, tree = stack.pop()
        #path指向當(dāng)前節(jié)點(diǎn)的值寄猩,tree指向當(dāng)前節(jié)點(diǎn)后續(xù)的tree
        print(path)
        if tree:
            for child in reversed(list(tree)):
                stack.push((list(path) + [child], tree[child]))
        #只要當(dāng)前節(jié)點(diǎn)后面還有枝葉嫉晶,則將其壓入棧中
        #壓入方法和前面一致,把concatenate之前path的child,和該節(jié)點(diǎn)后續(xù)的tree的defaultdict做成一個(gè)tuple壓入棧中替废。

2. Fractal(分形)

引用Wikipedia的解釋:“分形(英語:Fractal)箍铭,又稱碎形、殘形椎镣,通常被定義為“一個(gè)粗糙或零碎的幾何形狀诈火,可以分成數(shù)個(gè)部分,且每一部分都(至少近似地)是整體縮小后的形狀”状答,即具有自相似的性質(zhì)冷守。”
通過定義一個(gè)規(guī)則使得后續(xù)的復(fù)制能夠繼續(xù)剪况,自然界最常見的分形比如樹葉和海岸線教沾。
規(guī)則舉例:


Fractal

(1)原圖案各邊都縮小到原來的1/2
(2)以左下方為坐標(biāo)原點(diǎn),輸出第1個(gè)新圖案到和原圖案相同的坐標(biāo)點(diǎn)
(3)以左下方為坐標(biāo)原點(diǎn)译断,輸出第2個(gè)新圖案到橫坐標(biāo)的1/2縱坐標(biāo)不變的坐標(biāo)點(diǎn)
(4)以左下方為坐標(biāo)原點(diǎn),輸出第3個(gè)新圖案到橫坐標(biāo)的1/4縱坐標(biāo)向上移動(dòng)1/2的坐標(biāo)點(diǎn)
每次對圖中所有原圖案都執(zhí)行同樣的操作或悲,就形成了上述的圖片孙咪。
最終的圖案只體現(xiàn)規(guī)則,而不以最初的圖案改變巡语,比如同樣的規(guī)則下會(huì)形成下圖:


Fractal2

3. Queue

queue的準(zhǔn)則:first in first out翎蹈,順序與stack相比是反的。因?yàn)閝ueue只需要不斷改變兩頭的數(shù)據(jù)男公,所以linkedlist是適合queue的數(shù)據(jù)結(jié)構(gòu)荤堪,創(chuàng)建queue的class如下:

class Node:
    def __init__(self, value):
        self.value = value
        self.next_node = None

class QueueException(Exception):
    def __init__(self, message):
        self.message = message

class Queue:
    def __init__(self):
        self.head = None
        self.tail = None
    #Queue只對頭尾操作,所以initiation只需要定義head和tail
    
    def enqueue(self, value):
        new_node = Node(value)
        if not self.tail:
            self.tail = new_node
            self.head = self.tail
            return
        #判斷如果queue為空枢赔,tail指向新加入的點(diǎn)澄阳,head與其相同
        self.tail.next_node = new_node
        self.tail = new_node
        #在linkedlist尾端增加一個(gè)點(diǎn),比如a - > b踏拜,加入新點(diǎn)c碎赢,這個(gè)時(shí)候先讓b的next等于c,然后tail指向c

    def dequeue(self):
        if not self.head:
            raise QueueException('Queue is empty)
        value = self.head.value
        self.head = self.head.next_node
        if not self.head:
            self.tail = None
        #每次從隊(duì)列中取走一個(gè)點(diǎn)速梗,head后移一位肮塞,如果head是None,那么tail是None姻锁,否則tail的指向不變
        return value

    def is_empty(self):
        return self.head is None

3.1 Queue Exercise

廣度優(yōu)先搜索是先進(jìn)入的先處理枕赵,符合queue的思路。


Tree

上圖的搜索流程如下位隶,注意壓棧的順序是從右向左:

No.         1    2     3    4    5    6  ...
Queue       1    2     4    5    3    6 ...
                 4     5    3    6      11 ...
                 5     3         11    13 ...
                                 13
Operation push1 pop1  pop2  pop4 pop5 pop3 ...
                push2 push3      push6    
                push4            push11 
                push5            push13        
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拷窜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌装黑,老刑警劉巖副瀑,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異恋谭,居然都是意外死亡糠睡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門疚颊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狈孔,“玉大人,你說我怎么就攤上這事材义【椋” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵其掂,是天一觀的道長油挥。 經(jīng)常有香客問我,道長款熬,這世上最難降的妖魔是什么深寥? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮贤牛,結(jié)果婚禮上惋鹅,老公的妹妹穿的比我還像新娘。我一直安慰自己殉簸,他們只是感情好闰集,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著般卑,像睡著了一般武鲁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上椭微,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天洞坑,我揣著相機(jī)與錄音,去河邊找鬼蝇率。 笑死迟杂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的本慕。 我是一名探鬼主播排拷,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锅尘!你這毒婦竟也來了监氢?” 一聲冷哼從身側(cè)響起布蔗,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浪腐,沒想到半個(gè)月后纵揍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡议街,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年泽谨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片特漩。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吧雹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涂身,到底是詐尸還是另有隱情雄卷,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布蛤售,位于F島的核電站丁鹉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏悍抑。R本人自食惡果不足惜鳄炉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搜骡。 院中可真熱鬧,春花似錦佑女、人聲如沸记靡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摸吠。三九已至,卻和暖如春嚎花,著一層夾襖步出監(jiān)牢的瞬間寸痢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工紊选, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啼止,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓兵罢,卻偏偏與公主長得像献烦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子卖词,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容