1. 程式人生 > >二叉樹中任意兩節點的最低共同父節點

二叉樹中任意兩節點的最低共同父節點

通過兩個和二叉樹相關的演算法題來看看和遞迴在二叉樹中的應用

輸入二叉樹中的兩個結點,輸出這兩個結點在數中最低的共同父結點。

思路: 如果這兩個節點不在同一個子樹下面,那麼這棵樹的根節點就是他們的共同最低父節點。

如果兩個都在右子樹,那麼以右子樹的最上面的那個節點作為根節點,重新進行判斷,遞迴呼叫。

同理兩個都在左子樹,則方法同上。  也就是說,最終的結果分別只有三種情況,一個節點在右子樹,一個節點在左子樹。兩個節點都在右子樹,兩個節點都在左子樹。

如果是第一種情況,那麼當前的節點就是他們最低的公共節點,左右都在左子樹,或者在右子樹,那麼就遞迴呼叫。

BinTree* GetParent(BinTree* root,BinTree* first,BinTree* second)
{
         if(root == NULL)
           return ;
         if(root == first || second == second)
         {
           return root;        
         }
         BinTree* Low_left = GetParent(root->left,first,second);
         BinTree* Low_right = GetParent(root->right,first,second);
         /*說明在左子樹中沒有找到和first/second中任何一個相同的節點  公共父節點右子樹 而且第一個就是*/  
         if(Low_left == NULL)  
           return Low_right;
         /*在右子樹中沒有找到和first/second中任何一個相同的節點 公共父節點在左子樹 而且第一個就是最低父節點*/
         else if(Low_right == NULL)
           return Low_left;
         else
            return root;
 } 
在左子樹中沒找到任何一個節點,那麼就在右子樹中找,在右子樹中沒找到任何一個節點,那麼共同父節點肯定在左子樹。如果在左右子樹中各找到一個節點(注意並不可能同時找到兩個節點的),那麼當前節點就是最低共同父節點。

遞迴解法效率很低,有很多重複的遍歷,下面看一下非遞迴解法。
非遞迴解法:
先求從根節點到兩個節點的路徑,然後再比較對應路徑的節點就行,最後一個相同的節點也就是他們在二叉樹中的最低公共祖先節點
參考程式碼如下:

    bool GetNodePath(BinaryTreeNode * pRoot, BinaryTreeNode * pNode,   
                     list<BinaryTreeNode *> & path)  
    {  
        if(pRoot == pNode)  
        {     
            path.push_back(pRoot);  
            return true;  
        }  
        if(pRoot == NULL)  
            return false;  
        path.push_back(pRoot);  
        bool found = false;  
        found = GetNodePath(pRoot->m_pLeft, pNode, path);  
        if(!found)  
            found = GetNodePath(pRoot->m_pRight, pNode, path);  
        if(!found)  
            path.pop_back();  
        return found;  
    }  
    BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot, BinaryTreeNode * pNode1, BinaryTreeNode * pNode2)  
    {  
        if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)  
            return NULL;  
        list<BinaryTreeNode*> path1;  
        bool bResult1 = GetNodePath(pRoot, pNode1, path1);  
        list<BinaryTreeNode*> path2;  
        bool bResult2 = GetNodePath(pRoot, pNode2, path2);  
        if(!bResult1 || !bResult2)   
            return NULL;  
        BinaryTreeNode * pLast = NULL;  
        list<BinaryTreeNode*>::const_iterator iter1 = path1.begin();  
        list<BinaryTreeNode*>::const_iterator iter2 = path2.begin();  
        while(iter1 != path1.end() && iter2 != path2.end())  
        {  
            if(*iter1 == *iter2)  
                pLast = *iter1;  
            else  
                break;  
            iter1++;  
            iter2++;  
        }  
        return pLast;  
    }  


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

這裡的距離就是邊的個數。距離就是邊的個數

我們也同樣可以使用遞迴來解決。所有的情況就是,最遠的兩個節點一個在左子樹,一個在右子樹,那麼就是左子樹中最遠節點到當前節點的距離加上右子樹中最遠節點到當前節點的距離,也就是說最遠距離跨越了根節點。第二種情況就是最遠距離的兩個節點都在右子樹,那麼我們就需要看右字數中任意兩點的最遠距離,第三種情況就是最遠距離的兩個節點都在左子樹,那麼我們就需要計算左子樹中最遠距離。然後比較三種情況中最遠的距離即可

/*
  distance 是以root為根的的樹的深度  一個樹的深度比邊要多1.這一點一定要明確  下面那個例子也是這樣
  函式的返回值就是以root為跟的樹 任意兩點的最遠距離 函式內部進行了比較選擇
*/
int Distance(BinTree* root,int& depth)
{
    if( root == NULL)
    {
        depth = 0;
        return ; 
    }
    int left,right;
    int left_dis = Distance(root->left,left);
    int right_dis = Distance(root->right,right);
    depth = (left > right? left :right)+1;
    
    return max(left_dis,max(right_max,left+right));
} 

遞迴解法:
(1)如果二叉樹為空,返回0,同時記錄左子樹和右子樹的深度,都為0

(2)如果二叉樹不為空,最大距離要麼是左子樹中的最大距離,要麼是右子樹中的最大距離,要麼是左子樹節點中到根節點的最大距離+右子樹節點中到根節點的最大距離,同時記錄左子樹和右子樹節點中到根節點的最大距離。

    int GetMaxDistance(BinaryTreeNode * pRoot, int & maxLeft, int & maxRight)  
    {  
        // maxLeft, 左子樹中的節點距離根節點的最遠距離  
        // maxRight, 右子樹中的節點距離根節點的最遠距離  
        if(pRoot == NULL)  
        {  
            maxLeft = 0;  
            maxRight = 0;  
            return 0;  
        }  
        int maxLL, maxLR, maxRL, maxRR;  
        int maxDistLeft, maxDistRight;  
        if(pRoot->m_pLeft != NULL)  
        {  
            maxDistLeft = GetMaxDistance(pRoot->m_pLeft, maxLL, maxLR);  
            maxLeft = max(maxLL, maxLR) + 1;  
        }  
        else  
        {  
            maxDistLeft = 0;  
            maxLeft = 0;  
        }  
        if(pRoot->m_pRight != NULL)  
        {  
            maxDistRight = GetMaxDistance(pRoot->m_pRight, maxRL, maxRR);  
            maxRight = max(maxRL, maxRR) + 1;  
        }  
        else  
        {  
            maxDistRight = 0;  
            maxRight = 0;  
        }  
        return max(max(maxDistLeft, maxDistRight), maxLeft+maxRight);  
    }  


參考程式碼如下:

int MaxDepth(BinTree* root)
{
    int depth = 0;
    if(root != NULL)
    {
       int left_depth = MaxDepth(root->left);
       int right_depth = MaxDepth(root->right);
       depth = left_depth > right_depty? left_depth : right_depth;
       depth++; 
    }
    return depth;
    }

int MaxDistance(BinTree* root)
{
    int maxdis = 0;
    if(root != NULL)
    {
        maxdis = MaxDepth(root->right) +MaxDepth(root->left);
        int left_dis = MaxDistance(root->left);
        int right_dis = MaxDistance(root->right);
        int temp = left_dis >  right_dis? left_dis:right_dis;
        maxdis = temp>maxdis ? temp :maxdis;
    }
    return maxdis;
    }

其他和二叉樹相關的幾種操作

/*
二叉樹的深度 
*/
int Depth(BinTree* root)
{
	if(root == NULL)
		return 0;
	int left = Depth(root->left);
	int right = Depth(root->right);
	return (left > right?left:right)+1;
}

/*
二叉樹的寬度  二叉樹的寬度就是節點最多的那層中節點數目 
*/ 
void Width(BinTree* root,int& width)
{
	if(root == NULL)
		return;
	deque<BinTree*> dequ;
	dequ.push_back(root);
	BinTree* current;
	int tempWidth = dequ.size();
	while(tempWidth)
	{
		if(tempWidth > width)
		 width = tempWidth;
		while(tempWidth)
		{
			current = dequ.front();
			dequ.pop_front();
			cout<<current->value<<endl;
			if(current->left != NULL)
				dequ.push_back(current->left);
			if(current->right != NULL)
				dequ.push_back(current->right);
			tempWidth--;
		}
	tempWidth = dequ.size();
	}
}

/*二叉樹到葉子節點的路徑*/
void Routh(BinTree* root,vector<BinTree*>& vec)
{
	if(root == NULL)
		return;
	vec.push_back(root);
	if(root->left == NULL && root->right == NULL)
	{
		vector<BinTree*>::iterator itr = vec.begin();
		for(;itr!=vec.end();itr++)
		{
			cout<<(*itr)->value<<endl;
		}
		cout<<"-----"<<endl;
	} 
 	Routh(root->left,vec);
 	Routh(root->right,vec);
 	vec.pop_back();
}
/*
判斷兩棵二叉樹是否結構相同
*/

bool JudeSame(BinTree* first,BinTree* second)
{
	if(first == NULL && second == NULL)
		return true;
	if((first==NULL&&second!=NULL)|| (first!=NULL&&second==NULL))
		return false;
	if(first->value != second->value)
		return false;
	return (JudeSame(first->left,second->left))&&(JudeSame(first->right,second->right));
} 

/*
二叉樹的映象 
*/
void Reverse(BinTree* root)
{
	if(root == NULL)
		return;
	BinTree* temp=NULL;
	temp = root->right;
	root->right = root->left;
	root->left = temp;
	Reverse(root->left);
	Reverse(root->right);
}

/*
  將一個二叉查詢樹變成一個雙向連結串列 
*/
BinTree* phead = NULL;
BinTree* pcurrent = NULL;
void Link(BinTree* node)
{
     if(phead == NULL)
       phead = node;
     if(pcurrent != NULL)     
     {
         pcurrent->right = node;
         node->left = pcurrent;
     }
     pcurrent = node;
     }
void Link_BinTree(BinTree* root)
{
    if(root == NULL)
     return ;     
     Link_BinTree(root->left);
     Link(root);
     Link_BinTree(root->right);
}