1. 程式人生 > >求二叉樹中兩個節點最遠的距離

求二叉樹中兩個節點最遠的距離

一說到二叉樹,就有很多題目,今天在程式設計之美中看到了二叉樹中兩個節點最遠的距離。所以給想借機寫一篇部落格。

在開始之前,我們先想想,兩個最常節點的最遠距離是怎麼樣的?

情況一:最大距離可能一個在左子樹,一個在右子樹中,通過根節點;


情況二:也可能出現在左/右子樹中,不經過根節點。


大體就是這樣,我們要如何來解決呢?

給大家看看《程式設計之美》中它是如何決解的:

<span style="font-size:24px;">// 資料結構定義
struct NODE
{
    NODE* pLeft;        // 左子樹
    NODE* pRight;       // 右子樹
    int nMaxLeft;       // 左子樹中的最長距離
    int nMaxRight;      // 右子樹中的最長距離
    char chValue;       // 該節點的值
};
 
int nMaxLen = 0;
 
// 尋找樹中最長的兩段距離
void FindMaxLen(NODE* pRoot)
{
    // 遍歷到葉子節點,返回
    if(pRoot == NULL)
    {
        return;
    }
 
    // 如果左子樹為空,那麼該節點的左邊最長距離為0
    if(pRoot -> pLeft == NULL)
    {
        pRoot -> nMaxLeft = 0; 
    }
 
    // 如果右子樹為空,那麼該節點的右邊最長距離為0
    if(pRoot -> pRight == NULL)
    {
        pRoot -> nMaxRight = 0;
    }
 
    // 如果左子樹不為空,遞迴尋找左子樹最長距離
    if(pRoot -> pLeft != NULL)
    {
        FindMaxLen(pRoot -> pLeft);
    }
 
    // 如果右子樹不為空,遞迴尋找右子樹最長距離
    if(pRoot -> pRight != NULL)
    {
        FindMaxLen(pRoot -> pRight);
    }
 
    // 計算左子樹最長節點距離
    if(pRoot -> pLeft != NULL)
    {
        int nTempMax = 0;
        if(pRoot -> pLeft -> nMaxLeft > pRoot -> pLeft -> nMaxRight)
        {
            nTempMax = pRoot -> pLeft -> nMaxLeft;
        }
        else
        {
            nTempMax = pRoot -> pLeft -> nMaxRight;
        }
        pRoot -> nMaxLeft = nTempMax + 1;
    }
 
    // 計算右子樹最長節點距離
    if(pRoot -> pRight != NULL)
    {
        int nTempMax = 0;
        if(pRoot -> pRight -> nMaxLeft > pRoot -> pRight -> nMaxRight)
        {
            nTempMax = pRoot -> pRight -> nMaxLeft;
        }
        else
        {
            nTempMax = pRoot -> pRight -> nMaxRight;
        }
        pRoot -> nMaxRight = nTempMax + 1;
    }
 
    // 更新最長距離
    if(pRoot -> nMaxLeft + pRoot -> nMaxRight > nMaxLen)
    {
        nMaxLen = pRoot -> nMaxLeft + pRoot -> nMaxRight;
    }
}</span>

這段程式碼有幾個缺點:
1.演算法加入了侵入式(intrusive)的資料nMaxLeft, nMaxRight
2.使用了全域性變數 nMaxLen。每次使用要額外初始化。而且就算是不同的獨立資料,也不能在多個執行緒使用這個函式
3.邏輯比較複雜,也有許多 NULL 相關的條件測試。

而,我想了想,寫出了這樣的程式碼:
<span style="font-size:24px;">void _FindMaxDis(BSTreeNode *pNode, int &deepth, int &maxdis)    //這裡的兩個數,我都用的是引用,讀者知道為什麼? 
{  
    if (pNode==NULL)  
    {  
        deepth=0;maxdis=0;  
        return;  
    } </span>
<span style="font-size:24px;">    int l_deepth=0,r_deepth=0;  
    int l_maxdis=0,r_maxdis=0;  
  
    if (pNode->m_pleft)  
        FindMaxDis(pNode->m_pleft,l_deepth,l_maxdis);  
    if (pNode->m_pright)  
        FindMaxDis(pNode->m_pright,r_deepth,r_maxdis);  
  
    deepth = (l_deepth > r_deepth ? l_deepth : r_deepth) + 1;  
  
    maxdis = l_maxdis > r_maxdis ? l_maxdis : r_maxdis ;  
  
    maxdis = (l_deepth+r_deepth) > maxdis ? (l_deepth+r_deepth) : maxdis;  
  
}</span>
<span style="font-size:24px;">  
int FindMaxDis(BSTreeNode *pNode)  
{  
    int deepth, maxdis;  
    _FindMaxDis(pNode,deepth,maxdis);  
  
    return maxdis;  
}  </span>
其實做做這個題,大體思路都是一致的。但是,我們要考慮很多。在這個方面,推薦讀者去看看《程式碼大全》

我來說一說這個程式碼的好處吧。

提高了可讀性,另一個優點是減少了 O(節點數目) 大小的侵入式資料,而改為使用 O(樹的最大深度) 大小的棧空間。

本博文只是對二叉樹中兩個節點最遠的距離的解法,做了粗略的概述,如有更好的解法或者不同的看法,請留言。