• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tbwshc

            tbw

              C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              95 Posts :: 8 Stories :: 3 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(4)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            二叉樹(Binary Tree)的前序、中序和后續(xù)遍歷是算法和數(shù)據(jù)結構中的基本問題,基于遞歸的二叉樹遍歷算法更是遞歸的經(jīng)典應用。

            假設二叉樹結點定義如下:

            1. // C++ 
            2. struct Node { 
            3.     int value; 
            4.     Node *left; 
            5.     Node *right; 

            中序遞歸遍歷算法:

            1. // C++ 
            2. void inorder_traverse(Node *node) { 
            3.     if (NULL != node->left) { 
            4.         inorder_traverse(node->left); 
            5.     } 
            6.     do_something(node); 
            7.     if (NULL != node->right) { 
            8.         inorder_traverse(node->right); 
            9.     } 

            前序和后序遍歷算法類似。

            但是,僅有遍歷算法是不夠的,在許多應用中,我們還需要對遍歷本身進行抽象。假如有一個求和的函數(shù)sum,我們希望它能應用于鏈表,數(shù)組,二叉樹等等不同的數(shù)據(jù)結構。這時,我們可以抽象出迭代器(Iterator)的概念,通過迭代器把算法和數(shù)據(jù)結構解耦了,使得通用算法能應用于不同類型的數(shù)據(jù)結構。我們可以把sum函數(shù)定義為:

            1. int sum(Iterator it) 

            鏈表作為一種線性結構,它的迭代器實現(xiàn)非常簡單和直觀,而二叉樹的迭代器實現(xiàn)則不那么容易,我們不能直接將遞歸遍歷轉(zhuǎn)換為迭代器。究其原因,這是因為二叉 樹遞歸遍歷過程是編譯器在調(diào)用棧上自動進行的,程序員對這個過程缺乏足夠的tb控制。既然如此,那么我們?nèi)绻梢宰约簛砜刂普麄€調(diào)用棧的進棧和出棧不是就達到 控制的目的了嗎?我們先來看看二叉樹遍歷的非遞歸算法:

            1. // C++ 
            2. void inorder_traverse_nonrecursive(Node *node) { 
            3.     Stack stack; 
            4.     do { 
            5.         // node代表當前準備處理的子樹,層層向下把左孩子壓棧,對應遞歸算法的左子樹遞歸 
            6.         while (NULL != node) { 
            7.             stack.push(node); 
            8.             node = node->left; 
            9.         } 
            10.         do { 
            11.             Node *top = stack.top(); 
            12.             stack.pop(); //彈出棧頂,對應遞歸算法的函數(shù)返回 
            13.             do_something(top); 
            14.             if (NULL != top->right) { 
            15.                 node = top->right; //將當前子樹置為剛剛遍歷過的結點的右孩子,對應遞歸算法的右子樹遞歸 
            16.                 break
            17.             } 
            18.         } 
            19.         while (!stack.empty()); 
            20.     } 
            21.     while (!stack.empty()); 

            通過基于棧的非遞歸算法我們獲得了對于遍歷過程的控制,下面我們考慮如何將其封裝為迭代器呢? 這里關鍵在于理解遍歷的過程是由棧的狀態(tài)來表示的,所以顯然迭代器內(nèi)部應該包含一個棧結構,每次迭代的過程就是對棧的操作。假設tbw迭代器的接口為:

            1. // C++ 
            2. class Iterator { 
            3.     public
            4.         virtual Node* next() = 0; 
            5. }; 

            下面是一個二叉樹中序遍歷迭代器的實現(xiàn):

            1. //C++ 
            2. class InorderIterator : public Iterator { 
            3.     public
            4.         InorderIterator(Node *node) { 
            5.             Node *current = node; 
            6.             while (NULL != current) { 
            7.                 mStack.push(current); 
            8.                 current = current->left; 
            9.             } 
            10.         } 
            11.         virtual Node* next() { 
            12.             if (mStack.empty()) { 
            13.                 return NULL; 
            14.             } 
            15.             Node *top = mStack.top(); 
            16.             mStack.pop(); 
            17.             if (NULL != top->right) { 
            18.                 Node *current = top->right; 
            19.                 while (NULL != current) { 
            20.                     mStack.push(current); 
            21.                     current = current->left; 
            22.                 } 
            23.             } 
            24.             return top; 
            25.          } 
            26.     private
            27.         std::stack<Node*> mStack; 
            28. }; 

            下面我們再來考察一下這個迭代器實現(xiàn)的時間和空間復雜度。很顯然,由于棧中最多需要保存所有的結點,所以其空間復雜度是O(n)的。那么時間復雜度 呢?一次next()調(diào)用也最多會進行n次棧操作,而整個遍歷過程需要調(diào)用n次next(),那么是不是整個迭代器的時間復雜度就是O(n^2)呢?答案 是否定的!因為每個結點只會進棧和出棧一次,所以整個迭代過程的時間復雜度依然為O(n)。其實,這和遞歸遍歷的時空復雜度完全一樣。

            除了上面顯式利用??刂拼a執(zhí)行順序外,在支持yield語義的語言(C#, Python等)中,還有更為直接的做法。下面基于yield的二叉樹中序遍歷的Python實現(xiàn):

            1. // Python 
            2. def inorder(t): 
            3.     if t: 
            4.         for x in inorder(t.left): 
            5.             yield x 
            6.         yield t.label 
            7.         for x in inorder(t.right): 
            8.             yield x 

            yield與return區(qū)別的一種通俗解釋是yield返回時系統(tǒng)會保留函數(shù)調(diào)用的狀態(tài),下次該函數(shù)被調(diào)用時會接著從上次的執(zhí)行點繼續(xù)執(zhí)行,這是一種與 棧語義所完全不同的流程控制語義。我們知道Python的解釋器是C寫的,但是C并不支持yield語義,那么解釋器是如何做到對yield的支持的呢? 有了上面把遞歸遍歷變換為迭代遍歷的經(jīng)驗,相信你已經(jīng)猜到Python解釋器一定是對yield代碼進行了某種變換。如果你已經(jīng)能夠?qū)崿F(xiàn)遞歸變非遞歸,不 妨嘗試一下能否寫一段編譯程序?qū)ield代碼變換為非yield代碼。

            posted on 2013-07-17 17:09 tbwshc 閱讀(157) 評論(0)  編輯 收藏 引用
            久久中文字幕一区二区| 国产精品久久亚洲不卡动漫| 国产日韩久久久精品影院首页| 91久久精品国产成人久久| 亚洲精品无码久久久| 漂亮人妻被黑人久久精品| 久久精品99无色码中文字幕| 思思久久99热只有频精品66| 久久国产欧美日韩精品| 久久五月精品中文字幕| 国产高潮国产高潮久久久| 亚洲伊人久久成综合人影院 | 久久精品国产亚洲AV无码娇色| 久久综合九色综合网站| 久久精品亚洲日本波多野结衣 | 狠狠色综合网站久久久久久久高清| 国产精品久久婷婷六月丁香| 狠狠色丁香久久综合五月| 一本久久免费视频| 久久国产午夜精品一区二区三区| 亚洲AV无码久久寂寞少妇| 亚洲精品NV久久久久久久久久| 久久狠狠高潮亚洲精品| 久久久噜噜噜久久中文字幕色伊伊 | 精品久久国产一区二区三区香蕉| 亚洲中文字幕久久精品无码APP| 久久精品亚洲欧美日韩久久| 亚洲欧美精品伊人久久| 久久ZYZ资源站无码中文动漫| 精品国产99久久久久久麻豆| 久久男人AV资源网站| 国产精品无码久久久久| 久久这里只有精品首页| 久久国产精品成人片免费| 欧美黑人又粗又大久久久| 日韩精品久久无码中文字幕| 精品久久久久久久久免费影院 | 久久无码人妻精品一区二区三区 | 国内精品伊人久久久久妇| 欧美亚洲国产精品久久| 亚洲午夜精品久久久久久浪潮|