1. 程式人生 > >演算法6:設計中常用的武器-棧

演算法6:設計中常用的武器-棧

演算法就是設計,而設計無處不在,故演算法無處不在,但這是廢話,關鍵還是,要把握設計的套路,如果能發明套路就更不得了。設計套路中,有一個套路,是定製一個數據結構,就相當於定製出一個專用的厲害武器,然後用於解決特定的問題。

為了解決不同的問題,你可以定製出不同的資料結構。什麼是資料結構?就是資料的組織結構,有特點的結構。在諸多資料結構中,有一些是通用的,也就是可以用於很多問題場景,因此它也是常用的。

常用的資料結構中,有一個結構,叫作“棧”。棧,能獨立作為一個數據結構,在於它有自己的特點--由此可見,有特點才能自立門派。

棧的特點,是操作都在同一個地方進行,這個地方叫棧頂。入棧、出棧,都在棧頂進行,最先被處理的是最後進棧的元素,“來得早不如來得巧”,這可不是排隊。

注意,資料結構的棧,跟c語言記憶體管理中的“棧”不是一個概念。

棧的封裝實現,哪一門語言可以做到。

為了簡化語言的細節與難度,這裡選擇python,只因為它的易用性。而簡化語言的難度,是為了把精力放在演算法或資料結構的設計上,而不是糾結於“記憶體怎麼釋放”、“怎麼自動檢測型別”之類的節外生枝的問題上,專注很重要!

(一)棧的實現

python實現棧,很簡單,用list(列表)即可。

入棧:s.append('hello stack')
出棧:s.pop()
棧是否為空:not s
偷看一下棧頂:s[-1]
棧的長度:len(s)

比如:

s=[]
s.append(8)
len(s)      #1
s.pop()     #返回8
not s       #True,為空
s.append('hello')   
s.append(False) 
s.append(8.9)
len(s)      #3
s[-1]       #8.9
len(s)      #3
s.pop()     #8.9
len(s)      #2
not s       #False

pop或append後,棧頂的位置都在變化。

(二)棧的應用

任何一個數據結構,都是因為有應用的場景才存在,也就是時勢造英雄。但基於這個資料結構,是可以創造出新的演算法的,英雄也可造時勢。

棧也不例外。

下面舉例說說它的應用場景。

(1)判斷左右符號是否配對

左符號:([{,右符號:)]},配對的情況如:'([]){()}',不配對的情況如:'[{]}]'。

用棧來理解就是:

遍歷所有字元。
遇左符號則入棧;遇右符號則出棧(如果為空則直接返回False),出棧返回的字元如果與右符號不配對則返回False,如果配對則繼續下一個字元。
所有字元遍歷完後,棧非空則返回False,否則返回True。
def sybol_match(str):
    L=['(','{','['];
    R=[')','}',']'];
    s=[]
    for c in str:
        if c in L:
            s.append(c)
        else:
            if not s:
                return False
            a=s.pop()
            lp = L.index(a)
            rp = R.index(c)
            if lp!=rp:
                return False
    return not s

(2)計算字尾表示式

計算一個表示式時,表示式可以以中綴或字尾的方式錄入,而後綴表示式由於不使用括號而簡化處理,所以是一個普遍的選擇。

補充一下知識點,中綴表示式是我們常用的表達方式,也就是把符號放在數字中間,比如3+4,而字首或字尾是計算機喜歡的表達方式,因為它更易於解析。字首就是把符號放在數字前面,比如+ 3 4,而後綴就是符號在數字後面,比如3 4 +。

再比如:

中綴:12*(2/2)  字尾:12 2 2 / * 
中綴:10-(2*3) 字尾:10 2 3 * -
中綴:(3-2)*(9/3)+5     字尾:3 2 - 9 3 / * 5 + 

用棧來理解就是:

遍歷所有分割項(以空格切分)。
遇到數字則入棧;遇到操作符出棧兩次(這裡簡化為都是二元操作,第一次出棧的為引數2,第二次為引數1),並進行運算,再把結果入棧。
遍歷完所有分割項後,返回棧中內容(只有一個值)。
operators = {
        '+' : lambda p1, p2: p1+p2,
        '-' : lambda p1, p2: p1-p2,
        '*' : lambda p1, p2: p1*p2,
        '/' : lambda p1, p2: p1/p2,
        }
def calc_postfix(str):
    expr = str.split()
    s = []
    for e in expr:
        if e.isdigit():
            s.append(int(e))
        else:
            f = operators[e]
            p2 = s.pop()
            p1 = s.pop()
            s.append(f(p1, p2))

    return s.pop()

(3)揹包問題

有若干個物品,每一個物品都有重量。揹包有最大容量限制,求剛好裝滿揹包最大容量的所有解。

比如:

物品名稱    重量
物品0     1kg
物品1     8kg
物品2     4kg
物品3     3kg
物品4     5kg
物品5     2kg

揹包最大可以裝10kg,那可能的解是:[0,2,3,5]、[1,5]等。

用棧來理解就是:

盡情地裝(按物品順序,只要能裝得下就裝),如果剩餘容量剛好為0或者之後的各個物品都裝不下了,則出棧,即拿掉最後的一個物品k,再繼續從k+1個物品開始裝。
棧為空而且填裝的物品的索引已經超出範圍,則結束迴圈。
由於,總會一直出棧到一個物品都沒有,再從下一個物品開始填裝,所以一定會出現棧為空且沒有物品可裝的情況。
def knapsack(w, ws):
    """
    w --揹包容量
    ws --物品重量列表 [1, 3, ..]
    """
    ret = []
    s = []
    i = 0
    cnt = len(ws)
    rest = w
    while s or i < cnt:  # 棧為空或者還有得裝
        while i < cnt and rest > 0:  # 還有得裝且還有容量
            if rest >= ws[i]:  # 裝得下就裝
                s.append(i)
                rest -= ws[i]
            i += 1   # 不管當前的是否裝得下,都要嘗試下一個
        if rest == 0:
            ret.append(s[:])  # one solution
        i = s.pop()
        rest += ws[i]
        i += 1
    return ret
        
if __name__ == '__main__':
    # print(sybol_match('[{()}]{}[()]()'))
    # print(sybol_match('[({}])'))
    # print(calc_postfix('12 2 2 / *'))
    # print(calc_postfix('3 2 - 9 3 / * 5 +'))
    ret = knapsack(10, [1, 8, 4, 3, 5, 2]) 
    print(ret)

(三)總結

工具的價值在於恰當的使用。棧,一個簡單的工具,可用於某類問題的解決。

但是,這裡的重點不是怎麼設計棧這個資料結構,而是,你要有一個意識,就是對於特定的問題,有可能要設計一個專用的資料結構並使用它,當然,通用的資料結構也是一個辦法。


相關推薦

演算法6設計常用武器-

演算法就是設計,而設計無處不在,故演算法無處不在,但這是廢話,關鍵還是,要把握設計的套路,如果能發明套路就更不得了。設計套路中,有一個套路,是定製一個數據結構,就相當於定製出一個專用的厲害武器,然後用於解決特定的問題。 為了解決不同的問題,你可以定製出不同的資料結構。什麼是資料結構?就是資料的組織結構,有特點

演算法設計常用的規律性資料型別轉換

案例一: 1/2 錯誤寫法: double a= 1/2  正確寫法: double a=1/2*1.0;    答案為0.5   案例二: 字元轉int    &nbs

java(二)工作常用到的工具類

java 工具類 工作中大家要用到很多工具類,第三方的jar中有很多現成的工具類符合自己的項目需要,這個時候就不需要去重復造輪子了,從而節省了很多時間,大家可以利用這些時間去做其它重要的事情,如果沒有符合自己的工具類,這個時候就要寫自己的工具類了,下面列舉一些工作中常用的工具類。1、

SQLmapper常用方法

resultMap 中定義實體類的名稱 方法名 方法 引數 返回值 分頁查詢 findByPage 實體類物件 List

LeetCode演算法6java Z 字形變換

題目: 將一個給定字串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。 比如輸入字串為 “LEETCODEISHIRING” 行數為 3 時,排列如下: L C I R E T O E S I I G E D H N 之後,你的輸出需要從左往右逐行讀取,產生出一個新的

文章6Nginx的Epoll事件處理機制

0.序      在Linux下,Nginx預設的事件處理機制是Epoll事件處理機制。當然Nginx也可以使用select等事件處理機制,因此Nginx為了支援和開發具體的I/O模型,Nginx將事件處理機制抽象化。      在ngx_epoll_module.c中,可以看到。 1.Epoll事件初

Java併發指南16JUC常用的Unsafe和Locksupport

最近在看Java併發包的原始碼,發現了神奇的Unsafe類,仔細研究了一下,在這裡跟大家分享一下。Unsafe類是在sun.misc包下,不屬於Java標準。但是很多Java的基礎類庫,包括一些被廣泛使用的高效能開發庫都是基於Unsafe類開發的,比如Netty、Cassan

面試知識點6MySQLInnoDB的一級索引、二級索引

每個InnoDB表具有一個特殊的索引稱為聚簇索引(也叫聚集索引,聚類索引,簇集索引)。如果表上定義有主鍵,該主鍵索引就是聚簇索引。如果未定義主鍵,MySQL取第一個唯一索引(unique)而且只含非空列(NOT NULL)作為主鍵,InnoDB使用它作為聚簇索引。如果沒有這樣

經典演算法6貪心演算法之最小生成樹

 1、問題描述      設G =(V,E)是無向連通帶權圖,即一個網路。E中每條邊(v,w)的權為c[v][w]。如果G的子圖G’是一棵包含G的所有頂點的樹,則稱G’為G的生成樹。生成樹上各邊權的總和稱為該生成樹的耗費。在G的所有生成樹中,耗費最小的生成樹稱為G的最小

【啊哈!演算法演算法6只有五行的Floyd最短路演算法

        我們來想一想,根據我們以往的經驗,如果要讓任意兩點(例如從頂點a點到頂點b)之間的路程變短,只能引入第三個點(頂點k),並通過這個頂點k中轉即a->k->b,才可能縮短原來從頂點a點到頂點b的路程。那麼這個中轉的頂點k是1~n中的哪個點呢?甚至有時候不只通過一個點,而是經過兩個點

常用演算法設計窮舉法、分治法、動態規劃、貪心法、回溯法和分支限界法

演算法設計之六種常用演算法設計方法 1.直接遍歷態(窮舉法)        程式執行狀態是可以遍歷的,遍歷演算法執行每一個狀態,最終會找到一個最優的可行解;適用於解決極小規模或者複雜度線性增長,而線

學習用Node.js和Elasticsearch構建搜索引擎(6實際項目常用命令使用記錄

nds 黃色 ati cat htm action last shard open 1、檢測集群是否健康。 curl -XGET ‘localhost:9200/_cat/health?v‘#後面加一個v表示讓輸出內容表格顯示表頭 綠色表示一切正常,黃色表示所有

西南交通大學計算機專業考研真題答案詳解62012年演算法設計

一、考研真題 1、下面是求兩個集合A和B的並集(AUB)的演算法,集合A和集合B分別用單鏈表La和Lb的帶頭結點的單鏈表表示(連結串列中的資料按升序排序),其並集用單鏈表Lc表示(帶頭結點,其資料也按升序排列),請填空完善演算法。(每空2分)。 2、對給定的帶頭結點的單鏈表L,結點值得型

Spring常用設計模式原型模式

文章目錄 定義 模式分析 模式優缺點分析 原型模式的實際應用案例 定義 原型模式:使用原型例項指定待建立物件的型別,並且通過複製這個原型來建立新的物件。 模式分析 在原型模式結構中定義了一個抽象原型

Spring常用設計模式策略模式

在閻巨集博士的《JAVA與模式》一書中開頭是這樣描述策略(Strategy)模式的:     策略模式屬於物件的行為模式。其用意是針對一組演算法,將每一個演算法封裝到具有共同介面的獨立的類中,從而使得它們可以相互替換。策略模式使得演算法可以在不影響到客戶端的情況下發生變化。

Spring常用設計模式委派模式

Spring MVC框架中的DispatcherServlet其實就用了委派模式,也有人稱為是代理模式和策略模式的組合。 代理模式 使用代理物件來執行目標物件的方法並在代理物件中增強目標物件方法。 策略模式 策略模式是針對一組演算法,將每一個演算法封裝到具有共同介面的獨立的

Spring常用設計模式單例模式

在Spring中,Bean可以被定義為兩種模式:prototype(原型)和singleton(單例)。 singleton(單例) 只有一個共享的例項存在,所有對這個Bean的請求都會返回這個唯一的例項。 prototype(原型) 對這個Bean的每次請求都會建立一個新

Spring常用設計模式工廠模式

文章目錄 工廠模式 例項 1. 先實現簡單工廠 2. 工廠類(修改) 3. 增加配置檔案 4. 測試類 5. 總結 工廠模式 Beanfactory是一

Spring常用設計模式代理模式

文章目錄 代理模式 靜態代理 動態代理 Spring AOP的代理模式 代理模式 本質:控制物件訪問 代理模式具有可擴充套件性: 中介隔離作用。 開閉原則,增加

Spring常用設計模式模板模式

文章目錄 定義 例項分析 定義 在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但呼叫將以抽象類中定義的方式進行。這種型