對大多數程式設計師來說,遞迴演算法都不算陌生。甚至很多時候,使用遞迴演算法解決一些簡單問題是基礎教材的起步課程。但是,當遞迴演算法遇到稍微複雜的資料結構,如二叉搜尋樹的時候。別說讓你直接寫出演算法,就是把標準答案擺在你的面前讓你理解,我們恐怕都會抓耳撓腮。如果你也有類似的困擾請不要灰心,因為包括我在內幾乎我遇到的每一個碼農在這些方面都顯得一籌莫展。究其原因,我認為這其實是認知心理的一種表現——我們無法勝任同時正向思考與反向記憶——大多數的時候,我們可以輕鬆應付函式的一層層遞進,卻很難記住每一層的返回值。終於我發現通過一種簡單而有效的圖形結構可以讓我們理解各類遞迴演算法,下面進入正題。
定義在一個標準的二叉搜尋樹演算法中包含一個方法以獲取小於某一個鍵值Key的最大節點物件:
/** * Node包含一個鍵值對<Key extends Comparable<Key>, Value> * 以及一棵左子樹和一棵右子樹Node left, right * * class Node { * private Key key; * private Value val; * private Node left, right; * } */ Node floor(Node n, Key key) { if (n == null) { return null; } int cmp = key.compareTo(n.key); if (cmp == 0) { return n; } else if (cmp < 0) { return floor(n.left, key); } else { Node r = floor(n.right, key); if (r != null) { return r; } else { return n; } } }
演算法不復雜,對二叉搜尋樹不太瞭解的可以先去熟悉一下資料結構。可是想讀懂這幾行程式碼確實令我頭疼,畢竟如果讀不懂就不可能記住更不可能在需要的時候自己寫出來。接下來就是見證奇蹟的時刻:
首先,任何一種遞迴演算法都必然包含四種要素:輸入,輸出,遞迴條件和終止條件。floor方法含有兩個遞迴條件cmp<0和cmp>0以及三個終止條件n==null, cmp==0和r==null。
其次,我們將要通過簡單的圖形來代表層層的遞迴順序,並且幫助我們在達到終止條件的時候記錄返回值。如果在稿紙上繪製甚至還可以更簡潔一些。
以下是一個完整的演示: Node(H) floor( Node(S), Key(J) )
跟著floor方法繪製遞迴圖你大概應該獲得和我類似的結果:
是不是感覺瞬間思路清晰了!如果你也覺得這樣的方法對你有所幫助或者還有更好的建議不妨在留言中分享出來。