给定一个二叉树, 找到该树中两个指定节点p和q(数值唯一)的最近公共祖先

  1. 递归思想:
    判断p和q是否分别根结点的左右两侧,如果在左右两侧那么直接返回根结点即可,不失一般性,假设p和q分别均在根结点的左侧,那么按照分治的思想,此时继续往左子树找即可(问题规模已经缩小),那么依旧还是上面的操作划分,故可以采用递归的思想,后序遍历,如果在左子树找到了p,那么直接返回当前的root, 继续往root的右子树找,如果找到了q,说明root即是所找结点,如果未找到,将root返回给上一层继续搜索。

  2. 非递归思路
    通过后序遍历的思想,后序遍历存储了根结点到结点的所有祖先结点,用两个栈,假设p在q的左边,那么先找到p,栈A存储从根到p的所有祖先,栈B先拷贝栈A的数据,然后继续后序遍历,直至找到q,接着栈B开始出栈,与栈A进行比较,最先相等的结点就是最近公共祖先。

typedef struct BiTree{
    struct BiTree* lchild;
    struct BiTree* rchild;
    int data;
}BiTree;

typedef struct{
    BiTree* t;
    int tag;//tag=0  表示结点的左子女已被访问,tag=1表示结点的右子女已被访问
}Stack;

//递归创建树
void CreateTree(BiTree* &node,int &i,int treeData[]){
    int data = treeData[i];
    if (data != -1) {
        node = new BiTree();
        node->data = data;
        CreateTree(node->lchild,++i,treeData);
        CreateTree(node->rchild,++i,treeData);
    }
}

//寻找最近公共祖先结点
BiTree* CommonAncestor(BiTree* root,BiTree* p, BiTree* q){
    if (root == NULL || root->data == p->data || root->data == q->data) {
        return root;
    }
    BiTree* left = CommonAncestor(root->lchild, p, q);
    BiTree* right = CommonAncestor(root->rchild, p, q);
    if (left == NULL && right == NULL) {
        return NULL;
    }else if (left != NULL && right != NULL) {
        return  root;
    }else if(left != NULL){
        return left;
    }else{
        return right;
    }
}

//非递归寻找最近公共祖先
BiTree* Ancestor(BiTree* root,BiTree* p,BiTree* q)//求二叉树上结点p和q的最近的共同祖先结点r。
{
    cout<<"寻找p:"<<p->data<<"和q:"<<q->data<<"最近公共祖先"<<endl;
    int top = 0;
    int top1 = 0;
    BiTree* bt = root;
    Stack s[MaxSize],s1[MaxSize];//栈,容量够大
    while(bt != NULL || top > 0)
    {
        if (bt != NULL) {
            cout<<"data:"<<bt->data<<endl;
        }
        while(bt != NULL && bt->data != p->data && bt->data != q->data)              //结点入栈
        {
            s[++top].t = bt;
            s[top].tag = 0;
            bt = bt->lchild;
        } //沿左分枝向下
        
        if (bt!=NULL) {
            if(bt->data == p->data)  //不失一般性,假定p在q的左侧,遇结点p时,栈中元素均为p的祖先结点
            {
                for(int i = 1; i <= top; i++)
                    s1[i] = s[i];
                    top1 = top;
            }//将栈s的元素转入辅助栈s1 保存
        }
        
        if (bt!=NULL) {
            if(bt->data == q->data) {
                for(int i = top;i>0;i--)//;将栈中元素的树结点到s1去匹配
                {
                    BiTree* pp = s[i].t;
                    for (int j = top1;j>0;j--)
                        if(s1[j].t == pp) {
                            cout<<"p和q的最近共同的祖先已找到"<<endl;
                            return pp;
                        }
                }
            }
        }
        
        while(top!=0 && s[top].tag==1)
            top--; //退栈
        
        if (top!=0){
            s[top].tag=1;
            bt=s[top].t->rchild;
        } //沿右分枝向下遍历
        
    }
    return NULL;//q、p无公共祖先
}

int main(int argc, const char * argv[]) {
    BiTree* tree = NULL;
    int nodeTree[] = {1,2,3,-1,-1,4,-1,-1,5,-1,6,-1,-1};
    int i = 0;
    CreateTree(tree,i,nodeTree);

    BiTree* p = new BiTree();
    p->data = 3;
    p->lchild = NULL;
    p->rchild = NULL;
    
    BiTree* q = new BiTree();
    q->data = 5;
    q->lchild = NULL;
    q->rchild = NULL;
    
    BiTree* low =  CommonAncestor(tree, p, q);
    cout<<"lca: "<< low->data << endl;
    return 0;
}
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值