网易面试题之求出树的直径

来源:互联网 发布:注册网络教育公司 编辑:程序博客网 时间:2024/05/18 01:21

给定一颗二叉树,求出树中的任意两个节点之间的最大距离,即树的直径。

        从这个题目可以看到,树中任意两个节点的最大距离,显然应该是不同叶子间的最大距离。我们可以根据树的节点分布情况分情况考虑如何求树的直径。

       情况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:12 5 10 11 36 12




原创粉丝点击