《剑指Offer》面试题:树中两结点最低公共祖先

来源:互联网 发布:mac不能玩qq游戏 编辑:程序博客网 时间:2024/06/04 23:33

题目描述:

给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先。

思路
这类的题目,方法蛮多的,思路也不难理解,基本都是各种遍历的变种,主要是写代码,尤其基于递归的代码。
首先如果是二叉排序树自然不用说了,判断的一句就是该节点的值是否位于输入的这两个节点之间,可以用前序遍历来做。
如果是普通的树或者二叉树,解题思路是一样的,可以考虑前序遍历,得到两个路径,用链表或数组保存起来,然后找出两条路径的最后一个公共节点即可。
也可以后序遍历的方式,遍历到输入的节点时,将该节点及其后面遍历到的节点都保存到一个链表或数组中,然后找出两条路径的第一个公共机节点即可。

下面利用前序遍历和后续遍历寻找节点在树中的路径,都进行了编码实现。

/*输入:输入可能包含多个测试样例。对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数。其中每个测试样例包括两行,第一行为一个二叉树的先序遍历序列,其中左右子树若为空则用0代替,其中二叉树的结点个数node_num<10000。第二行为树中的两个结点的值m1与m2(0<m1,m2<10000)。输出:对应每个测试案例,输出给定的树中两个结点的最低公共祖先结点的值,若两个给定结点无最低公共祖先,则输出“My God”。样例输入:21 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 06 81 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 06 12样例输出:2My God*/#include<stdio.h>#include<stdlib.h>struct TreeNode{    int mValue;    struct TreeNode *lchild;    struct TreeNode *rchild;};void createTree(TreeNode **ppNode){    int val;    scanf("%d",&val);    if(val==0){        *ppNode=NULL;    }    else{        //为新结点开辟空间。        (*ppNode)=(TreeNode *)malloc(sizeof(struct TreeNode));        if((*ppNode)==NULL){            exit(EXIT_FAILURE);        }         (*ppNode)->mValue=val;        (*ppNode)->lchild=NULL;        (*ppNode)->rchild=NULL;        createTree(&((*ppNode)->lchild));        createTree(&((*ppNode)->rchild));    }} //在树中找到data的路径并保存在path1数组中。/*函数参数的说明@param pRoot:节点的指针@param data:待查找的结点的value值@param index:数组的下标索引。即该节点将存储在数组的位置@param len:用来保存最终数组中存储的节点数目。 */ bool getNodePath(TreeNode *pRoot,int *path,int data,int index,int *len){    if(pRoot==NULL){        return false;    }     //利用先序遍历来寻找data存在节点的路径    //第一步:当前结点保存在数组中    path[index]=pRoot->mValue;     //第二步:检查当前结点是否是我们需找的节点。    if(data==path[index]){        *len=index+1;        return true;    }    else{        bool isExist=getNodePath(pRoot->lchild,path, data,index+1,len);        if(!isExist){            isExist=getNodePath(pRoot->rchild,path,data,index+1,len);        }        return isExist;    }} //返回的是第一个公共节点在数组中的下标。,由于公共节点在两个数组前几个相同的元素都为公共节点,因此每个公共节点在在两个数组中的下标是相同的。 int findPublicNode(int *path1,int *path2,int len1,int len2){    if(path1==NULL||path2==NULL||len1<1||len2<1){        return -1;    }    //以下思路与求链表的第一个公共节点比较类似    //第一步:将找出大树组、小数组    int *lengthPath=path1;    int *shortPath=path2;    int longthLen=len1;    int shortLen=len2;    if(len1<len2){        lengthPath=path2;        shortPath=path1;        longthLen=len2;        shortLen=len1;    }    //从后往前找,当然也可以从前往后找,一样。     for(int i=shortLen-1;i>=0;i--){        if(lengthPath[i]==shortPath[i]){            return i;        }    }    return -1; } void destoryTree(TreeNode **pRoot){    if(*pRoot==NULL)        return;    //按照后序遍历来进行销毁    destoryTree(&((*pRoot)->lchild));    destoryTree(&((*pRoot)->rchild));    if((*pRoot)->lchild==NULL&&(*pRoot)->rchild==NULL){        free(*pRoot);        (*pRoot)=NULL;    }}int main(void){    //借助两个数组来保存遍历的路径。     int path1[10000];    int path2[10000];    int n;//测试用例的个数     while(scanf("%d",&n)!=EOF&&n>0){        for(int i=0;i<n;i++){            TreeNode *pRoot;            //根据输入来创建一个树。             createTree(&pRoot);            //接收待查找的节点            int first,second;            scanf("%d%d",&first,&second);            //寻找第一个公共结点            int len1=0;            int len2=0;            bool isExist1=getNodePath(pRoot,path1,first,0,&len1);             bool isExist2=getNodePath(pRoot,path2,second,0,&len2);             if(!(isExist1&&isExist2)){                printf("My God\n");            }            else{                //在两个路径数组中找出公共结点的value值。                 int publicNodeIndex=findPublicNode(path1,path2,len1,len2);                if(publicNodeIndex!=-1){                    printf("%d\n",path1[publicNodeIndex]);                }                else{                    printf("My God\n");                }            }            //最后销毁树的内存空间            destoryTree(&pRoot);         }     }    return 0;}

运行截图如下:

上面是利用先序遍历来寻找节点在树中的路径,下面是利用后序遍历来做,这里*len的是最大的下标。

// bool getNodePath(TreeNode *pRoot,int *path,int data,int *len){    if(pRoot==NULL){        return false;    }     bool isExist=getNodePath(pRoot->lchild,path, data,len);    if(!isExist){        isExist=getNodePath(pRoot->rchild,path, data,len);    }    if(isExist){//如果其的子节点存在路径中,则此节点也肯定存在路径中。         path[++(*len)]=pRoot->mValue;    }    if(pRoot->mValue==data){        *len=0;        path[*len]=pRoot->mValue;        return true;    }    return isExist;} 
0 0