1. 程式人生 > >二叉樹系列——二叉樹的最大距離(即相距最遠的兩個葉子節點,程式設計之美,百度面試題)

二叉樹系列——二叉樹的最大距離(即相距最遠的兩個葉子節點,程式設計之美,百度面試題)

來自於程式設計之美3.8。

題目:如果我們把二叉樹看做圖,父子節點之間的連線看成是雙向的,我們姑且定義“距離”為兩個節點之間邊的個數。寫一個程式求一棵二叉樹中相距最遠的兩個節點之間的距離。

如下圖所示,樹中相距最遠的兩個節點為A,B,最大距離為6。

書上對這個問題的分析是很清楚的,計算一個二叉樹的最大距離有兩個情況:

情況A: 路徑經過左子樹的最深節點,通過根節點,再到右子樹的最深節點。
情況B: 路徑不穿過根節點,而是左子樹或右子樹的最大距離路徑,取其大者

對於情況A來說,只需要知道左右子樹的深度,然後加起來即可。

對於情況B來說,需要知道左子樹的最遠距離,右子樹的最遠距離。

只需要計算這兩種情況的路徑距離,並取其最大值,就是該二叉樹的最大距離。

           

             情況A                                                        情況B

下面是書上的原始碼:

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)
	{
		pRoot->nMaxLeft = ((pRoot->pLeft->nMaxLeft > pRoot->pLeft->nMaxRight) ? pRoot->pLeft->nMaxLeft : pRoot->pLeft->nMaxRight) + 1;
	}

	// 計算右子樹最長節點距離
	if (pRoot->pRight != NULL)
	{
		pRoot->nMaxRight = ((pRoot->pRight->nMaxLeft > pRoot->pRight->nMaxRight) ? pRoot->pRight->nMaxLeft : pRoot->pRight->nMaxRight)+1;
	}

	// 更新最長距離
	if (pRoot->nMaxLeft + pRoot->nMaxRight > nMaxLen)
	{
		nMaxLen = pRoot->nMaxLeft + pRoot->nMaxRight;
	}
}

以上程式碼得新定義一個Node型別,且程式碼比較複雜!

下面是精簡版:

首先我們知道求二叉樹的深度的程式碼是比較簡單的,程式碼如下:

int DepthOfBinaryTree(BinaryTreeNode*pNode){
	if (pNode==NULL)
	{
		return 0;
	}
	else{  //遞迴
		return DepthOfBinaryTree(pNode->m_pLeft) > DepthOfBinaryTree(pNode->m_pRight) ?
			   DepthOfBinaryTree(pNode->m_pLeft) + 1 : DepthOfBinaryTree(pNode->m_pRight) + 1;
	}
}

而我們要求的二叉樹的最大距離其實就是求:肯定是某個節點左子樹的高度加上右子樹的高度加2,所以求出每個節點左子樹和右子樹的高度,取左右子樹高度之和加2的最大值即可,假設空節點的高度為-1

程式碼如下:

//改進的版本
int HeightOfBinaryTree(BinaryTreeNode*pNode, int&nMaxDistance){
	if (pNode == NULL)
		return -1;   //空節點的高度為-1
	//遞迴
	int nHeightOfLeftTree = HeightOfBinaryTree(pNode->m_pLeft, nMaxDistance) + 1;   //左子樹的的高度加1
	int nHeightOfRightTree = HeightOfBinaryTree(pNode->m_pRight, nMaxDistance) + 1;   //右子樹的高度加1
	int nDistance = nHeightOfLeftTree + nHeightOfRightTree;    //距離等於左子樹的高度加上右子樹的高度+2
	nMaxDistance = nMaxDistance > nDistance ? nMaxDistance : nDistance;            //得到距離的最大值
	return nHeightOfLeftTree > nHeightOfRightTree ? nHeightOfLeftTree : nHeightOfRightTree;
}


上面的函式的引數nMaxDistance返回的就是最大的距離,以下圖作為測試。

                                                               

輸出如下:


注意:在資料結構與演算法分析這本書上面,樹的深度是不包括根節點的,樹的深度就等於樹的高度,所以上面的函式的返回值是能夠代表樹的深度,也就是高度的!所以在判斷pNode==NULL的時候返回-1。

但是在劍指offer:面試題39,樹的深度包括了根節點,所以在判斷pNode==NULL的時候返回0。

如上圖所示:按劍指offer上面的,深度為4,但是按資料結構與演算法分析,深度則為3!這個需要特別注意。