二叉樹平衡檢查(遞歸思想分析)

二叉樹平衡檢查

題目描述

實現一個函數,檢查二叉樹是否平衡,平衡的定義以下,對於樹中任意一個結點,兩顆子樹的高度差不超過1。給定指向樹根結點的指針TreeNode* root,請返回一個bool,表明這棵樹是否平衡。函數

解題思路

樹結構自身就是遞歸定義,不少問題均可以利用遞歸巧妙地實現,對於這道題,關鍵點有兩處:post

  1. 求結點左右子樹高度差
  2. 遍歷樹中全部結點

以前咱們作過樹的最大深度問題以及樹的遍歷問題,將二者結合起來,即可以解決這兩個關鍵點。
解題思路爲:ui

  1. 若根結點爲空,則二叉樹平衡
  2. 調用Depth函數求結點高度
  3. 分別求出結點左右子樹高度並將高度差賦給differ,判斷是否知足平衡二叉樹的條件
  4. 遞歸遍歷左子樹和右子樹,重複以上操做

源代碼

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/

class Balance {
public:
    bool isBalance(TreeNode* root) {
        if (!root)
            return true;
        int differ=Depth(root->left)-Depth(root->right);
        if (differ>1||differ<-1)
            return false;
        return isBalance(root->left)&& isBalance(root->right);       //遞歸遍歷樹中全部結點
    }
    //計算結點高度
    int Depth(TreeNode *root) {
        if (!root)
            return 0;
        int L_high=Depth(root->left);
        int R_high=Depth(root->right);
        return (left>right) ? (left+1) : (right+1);
    }
};

遞歸思想分析

這道題中值得一提的是其中體現出的遞歸思想源代碼中有兩處遞歸調用,而若是咱們去觀察的話,會發現它們的形式是不一樣的。isBalance函數中,問題的解決遞歸調用函數以前,而在Depth函數中,遞歸調用函數問題的解決以前。可是,這又有什麼不一樣呢?指針

咱們知道遞歸是一個調用並返回的過程,它要求待解決的問題能夠拆分爲不少個解法相同或相似的小問題,經過的過程由近及遠,到達臨界點(也就是結束條件)而後再開始,咱們常常搞不清楚的是在這樣一個過程當中,問題是如何一步步被解決的?code

以前看了一篇關於大腦理解遞歸的文章,裏面對於這一點講得很好,對於我理解遞歸起到了很大的幫助。
遞歸過程有不一樣的模式,而它們都有三個共同的要素:遞+結束條件+歸遞歸

模式1

function(大問題)  {
    if (end_condition)  {             //結束條件
        return ;
    }
    else  {
        //先將一個大問題拆分爲小問題,當「遞」到「結束條件」返回的過程當中,再依次解決問題
        recursive(小問題);          // 遞  =>  遞歸調用函數
        solve questions;           // 歸  =>  問題的解決
    }
}

這種模式就對應於源代碼中的Depth函數,假如咱們給定一棵以下結構的二叉樹,get


咱們去分析Depth函數的遞歸流程,以下:it


從圖中能夠看出,程序的執行流程是遞歸調用函數(遞)→返回(結束條件)→解決問題(歸),問題是在遞歸調用返回的過程當中一步步被解決的。io

模式2

function(大問題)  {
    if (end_condition)  {          //結束條件
        return ;
    }
    else  {
        //在將大問題逐步拆分爲小問題的同時去解決問題
        solve questions;            // 遞  => 問題的解決
        recursive(小問題);          // 遞  => 遞歸調用函數
    }
}

這種模式就對應於源代碼中的isBalance函數,依舊利用上面說起的二叉樹結構,去分析isBalance函數的遞歸流程,以下:function


從圖中能夠看出,程序的執行流程是解決問題(遞)→遞歸調用函數(遞)→返回(結束條件)→一步步返回上層(歸),每一次問題的解決都是在遞歸調用函數的過程當中()便已經被解決,而不是在返回()的過程當中。

對於遞歸思想的理解,有人曾作了一個形象的比喻:

你打開面前這扇門,看到屋裏面還有一扇門(這門可能跟前面打開的門同樣大小(靜),也可能門小了些(動)),你走過去,發現手中的鑰匙還能夠打開它,你推開門,發現裏面還有一扇門,你繼續打開,。。。, 若干次以後,你打開面前一扇門,發現只有一間屋子,沒有門了。 你開始原路返回,每走回一間屋子,你數一次,走到入口的時候,你能夠回答出你到底用這鑰匙開了幾扇門。

這種說法其實並不全面,它對應了模式1,在的過程當中一步步解決問題。
或許咱們能夠對其稍加修改,以對應模式2

你打開面前這扇門,看到屋裏面還有一扇門(這門可能跟前面打開的門同樣大小(靜),也可能門小了些(動)),你走過去,發現手中的鑰匙還能夠打開它,你推開門,發現裏面還有一扇門,你繼續打開。。。而在打開門的過程當中,每走到一間屋子,你數一次, 若干次以後,你打開面前一扇門,發現只有一間屋子,沒有門了。 你開始原路返回,走到入口的時候,你能夠回答出你到底用這鑰匙開了幾扇門。

這兩種形式都有調用並返回(遞歸)的過程,不一樣點在於問題被解決的時機不一樣,有多是在的過程當中就已經被解決,也多是在的過程當中被解決。

以上是我對於遞歸的一些理解,可能會有不當之處,望指出Thanks♪(・ω・)ノ

相關文章
相關標籤/搜索