给定一颗二叉树,求出树中的任意两个节点之间的最大距离,即树的直径。
从这个题目可以看到,树中任意两个节点的最大距离,显然应该是不同叶子间的最大距离。我们可以根据树的节点分布情况分情况考虑如何求树的直径。
情况1:对于一般的树,根结点的左子树与右子树平衡,如图1。左子树与右子树的叶子节点的最大距离,应该为左子树的深度加上右子树的深度,然后加1,即为树的直径。
情况2:根据上面的分析,我们可以明显感觉到,如果该树为一颗倾斜树,即节点的左子树与右子树不对称,那么根结点的左子树与右子树的高度相差较大,同时衍生道以根结点的左(或右)子树作为根结点的左子树和右子树高非对称,那么根据情况1来求树的直径是完全错误的。
根据上面的分析,我们需要求出以树的根结点为根结点的左右子树的高度之和diameterOfRoot之后,与以树的根结点的左(右)孩子为根结点的左右子树的高度之和diameterOflChild(diameterOfrChild)进行比较,如果diameterOfRoot最大,那么diameterOfRoot,即为所求,否则diameterOflChild或者diameterOfrChild即为所求。当前这只是一次比较的节点。这个值还需要与树的根结点的左孩子的后代,以及右孩子的后代的不同叶子间的最大距离之和进行比较。
因此,对树求出直径就是一个不断递归,不断比较的过程。
它的伪代码为:
1.输入为根结点,输出为树的直径大小,初始化树的直径大小为0,以及pNode=root。
2.如果结点pNode为null,返回;否则,根据pNode递归求出左右子树的高度,以及递归2步骤,但是其输入为pNode->lChild、pNode->rChild。比较三者pNode递归求出左右子树的高度、pNode->lChild和pNode->rChild各自返回的左孩子和右孩子为根结点的树的直径。最终,三者的最大值即为树的直径。
c++代码为:
int DiameterPathOfTree(Node *root,int& length)
{
if(root==NULL){
return -1;
}
root->MaxLeft=1+DiameterPathOfTree(root->lChild, length);//求出左孩子的高度,以及以左孩子为根结点的2叉树直径保存在length中
root->MaxRight=1+DiameterPathOfTree(root->rChild, length);//求出右孩子的高度,以右孩子为根结点的2叉树的直径保存在length中
if(root->MaxLeft+root->MaxRight>length) length=root->MaxLeft+root->MaxRight+1;//孩子的为根结点的2叉树的直径与(左子树+右子树+1)进行对比,更新直径
return (root->MaxLeft>root->MaxRight)?root->MaxLeft:root->MaxRight;//返回树的高度
};
求出了树的直径大小,那么如果确定树的直径上面的节点呢?
要打印路径采用前序遍历来找到路径的根节点,根节点必须是满足:root->MaxLeft+root->MaxRight==length-1;
然后从跟节点遍历深度为root->MaxLeft-1的左子树路径和root->MaxRight-1的右子树路径。
//采用递归的方式来打印直径上的结点
void PrintDepthPathOfTree(Node* root,int depth)
{
if(depth==-1) return;
cout<<root->data<<"\t";
if(root->MaxLeft==depth)
PrintDepthPathOfTree(root->lChild,depth-1);
else if(root->MaxRight==depth)
PrintDepthPathOfTree(root->rChild, depth-1);
else
return;
};
//采用循环的方式来打印直径上的结点
void PrintDiameterPathOfTree(Node *root,int length)
{
Node* pNode=root;
stack<Node*> s;
while (pNode!=NULL||s.size()!=0) {
while (pNode!=NULL) {
if(pNode->MaxLeft+pNode->MaxRight==length-1){
cout<<pNode->data<<"\t";
PrintDepthPathOfTree(pNode->lChild,pNode->MaxLeft-1);
PrintDepthPathOfTree(pNode->rChild,pNode->MaxRight-1);
return;
}
s.push(pNode);
pNode=pNode->lChild;
}
if(s.size()!=0){
//cout<<"s.szie()==="<<s.size()<<endl;
pNode=s.top();
s.pop();
pNode=pNode->rChild;
}
}
};
最后,测试程序代码有:
typedef struct Node
{
int data;
Node* rChild;
Node* lChild;
int MaxLeft;
int MaxRight;
Node(){
rChild=NULL;
lChild=NULL;
MaxLeft=-1;
MaxRight=-1;
}
} *BinTree;
int array[31]={1,2,4,8,0,0,9,0,0,5,10,11,0,0,0,0,3,6,12,0,0,13,0,0,7,14,0,0,15,0,0};
void CreateBinTree(BinTree* T)
{
// 构造二叉链表。 T 是指向根指针的指针,故修改 *T 就修改了实参 ( 根指针 ) 本身
int ch;
static int i=0;
ch=array[i++];
if(ch==0)
T=NULL ;// 读人空格,将相应指针置空
else{ // 读人非空格
*T=(BinTree)malloc(sizeof(Node)); // 生成结点
(*T)->data=ch ;
CreateBinTree(&(*T)->lChild) ; // 构造左子树
CreateBinTree(&(*T)->rChild) ; // 构造右子树
}
}
int main (int argc, const char * argv[])
{
// insert code here...
BinTree root;
CreateBinTree(&root);
int length=0;
cout<<"the Tree's Depth is:"<<DiameterPathOfTree(root, length)<<endl;
cout<<"the Tree's Diameter is:"<<length<<endl;
PrintDiameterPathOfTree(root,length);
return 0;
}
输出结果为:
the Tree's Depth is:4
the Tree's Diameter is:8
the Nodes of Diameter are:1 2 5 10 11 3 6 12