樹的遍歷和迭代的一些總結

weixin_33978044發表於2017-12-27

一直在寫effective stl的總結,今天換個主題,我們來看看演算法,刷leetcode看到了樹的遍歷的問題和一些迭代的問題,順便過來總結一番.

  1. 首先我們來看看mergeTree,是leetcode上面的merge two binary trees:


    5550522-15270030d9136e25.png
    image.png

    題目如上圖所示,我們下面來分析一下我們的思路,首先肯定是需要理解題目要幹什麼,題目的意思就是讓我們把兩個樹"加"起來,類似於去兩個樹的並集.
    接下來,我們肯定要想到迭代啊,因為這種問題都是由一個小的部分可以放大到一個大的部分,針對這個題就是說我們如果我們融合了這兩顆的子樹,那麼我們只需要將根節點相加就完成了,所以以遞迴的思想來看就是,我們不斷的對根節點的子節點呼叫mergeTrees,這樣最終會讓所有子樹都完成相"加",這樣一層一層擴充就完成了整個樹的相"加".
    然後就是要考慮迭代函式結束的時候了,因為迭代從大的樹的根節點到越來越小的樹的根節點,最終肯定要結束的,不然迭代就結束不了了.對於我們來說,這個迭代函式的結束情況很簡單,如果兩個節點有任意一個為空,那麼迭代就結束,直接返回另外一個節點即可(這時我們不用考慮另外一個節點是否為空,原因很簡單).
    這個時候,我們程式的框架就完成了.

    /**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if (!t1) return t2;
        if (!t2) return t1;

        TreeNode* node = new TreeNode(t1->val + t2->val);
        node->left = mergeTrees(t1->left, t2->left);
        node->right = mergeTrees(t1->right, t2->right);
        return node;
    }
};
理解起來應該並不讓人覺得困難,主要是我們要弄清楚迭代的思路.
  1. 接下來說的也是一個迭代的例子,題目也是leetcode上的flatten-binary-tree-to-linked-list:


    5550522-06d647799ebe6cb8.png
    image.png

    這個題目乍一看不算簡單,需要我們的思考才能解決問題,這個問題依然是用遞迴來解決的,原因還是和上面一樣.不過這次的迭代函式需要我們來深思一下了,因為遞迴沒有那麼容易了.
    首先,我們考慮下迭代函式是在做什麼.對於一個最簡單的樹,如果我們呼叫迭代函式,要想將樹"flatten",那麼我們先要將根節點的右節點連到左節點的右子節點上,然後在將左子樹連到根節點的右子節點上.
    我畫了個圖,大家看看:


    5550522-0323c8a4433ea308.jpg
    379895094.jpg

    手動畫圖,有點粗糙,不過畫了圖我們要做的事情就變得明朗起來.
    迭代函式的引數是root節點,要做的就是將樹"flatten",返回值的話可以是void也可以是root節點,如果是void的話,我們就要加入一個額外的變數來儲存節點.我們這裡假設返回值是void吧(因為leetcode上給的解題模板就是函式返回值為空,其實返回一個指標也OK),加入一個額外的TreeNode指標prev.迭代函式內部要完成我在圖中畫的三步,第一步之前,先令prev為右子節點的指標(也就是指向3的TreeNode指標).第一步到第二步的過程中,讓左子節點的right等於prev,再讓root的右子節點為空(root->right=NULL),用prev來儲存左子節點指標(也就是指向2的TreeNode指標).第二步到第三步的過程,我們令root的右子節點為prev(也就是指向2的TreeNode指標),然後再令左子節點為空,最後令prev為root節點就OK了.
    這個時候,規律已經很明顯了,程式碼如下:

    /**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    void flatten(TreeNode* root) {
        if(root == NULL)
            return;
         flatten(root->right);
        flatten(root->left);
       
        root->right=prev;
        root->left = NULL;
        prev = root;
    }
    private:
    TreeNode *prev = NULL;
};
  1. 我們稍稍總結一下迭代這一部分,迭代的問題都是有規律的,也就是說對於一個大的問題,它的解法實際上是和小的問題是類似的,譬如我們上面的樹的例子,都是對根節點進行操作,然後呢,對左右子節點呼叫同樣的函式(因為子節點仍然還是樹,函式依然是正確的),當節點已經沒有子節點時,迭代就結束了,然後迭代會不斷返回,最終一顆大樹的問題也解決了.

相關文章