二叉树问题总结(三)

问题11:求二叉树的镜像
递归解法:
(1)如果二叉树为空,返回空
(2)如果二叉树非空,分别求左子树和右子树的镜像,然后交换左右子树

BinaryTreeNode* Mirror(BinaryTreeNode* pRoot)
{
    if(pRoot==NULL)
        return NULL;
    BinaryTreeNode* pLeft=Mirror(pRoot->m_pLeft);//求左子树镜像
    BinaryTreeNode* pRight=Mirror(pRoot->m_pRight);//求右子树镜像
    //交换左右子树
    pRoot->m_pLeft=pRight;
    pRoot->m_pRight=pLeft;
    return pRoot;
}

问题12:求二叉树中两个节点的最低公共祖先
递归解法:
(1)如果两个节点分别在左子树和右子树,返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树

bool FindNode(BinaryTreeNode* pRoot,BinaryTreeNode* pNode)
{
    if(pRoot==NULL ||pNode==NULL)
        return false;
    if(pRoot==pNode)
        return true;
    bool Found=FindNode(pRoot->m_pLeft,pNode);
    if(!Found)
        Found=FindNode(pRoot->m_pRight,pNode);
    return Found;
}

BinaryTreeNode* GetLastCommonParent(BianryTreeNode* pRoot,BinaryTreeNode* pNode1,BinaryTreeNode* pNode2)
{
    if(FindNode(pRoot->m_pLeft,pNode1))
    {
        if(FindNode(pRoot->m_pRight,pNode2))
            return pRoot;
        else
        {
            return GetLastCommonParent(pRoot->m_pLeft,pNode1,pNode2);
        }
    }
    else
    {
        if(FindeNode(pRoot->m_pLeft,pNode2))
            return pRoot;
        else
        {
            return GetLastCommonParent(pRoot->m_pRight,pNode1,pNode2);
        }
    }
}

递归解法效率低,有很多重复的遍历。
非递归解法:
首先求根节点到两个节点的路径,然后比较两个路径中的节点,
最后一个相同的节点就是二叉树中两个节点的最低公共祖先

bool FindPath(BinaryTreeNode* pRoot,BinaryTreeNode* pNode,list<BinaryTreeNode*> &path)
{
    if(pRoot==NULL)
        return false;
    if(pRoot==pNode)
    {
        path.push_back(pRoot);
        return true;
    }
    path.push(pRoot);
    bool Found=false;
    Found=FindPath(pRoot->m_pLeft,pNode,path);
    if(!Found)
        Found=FindPath(pRoot->m_pRight,pNode,path);
    if(!Found)
        path.pop_back();
    return Found;
}

BinaryTreeNode* GetLastCommonParent(BinaryTreeNode* pRoot,BinaryTreeNode* pNode1,BianryTreeNode* pNode2)
{

    if(pRoot==NULL||pNode1==NULL||pNode2==NULL)
        return NULL:
    list<BinaryTreeNode*> path1,path2;
    bool FoundLeft=FindPath(pRoot,pNode1,path1);
    bool FoundRight=FindPath(pRoot,pNode2,path2);
    BinaryTreeNode* pLast=NULL;
    if(FondLeft && FoundRight)
    {
        list<BinaryTreeNode*>::const_iterator iter1=path1.begin();
        list<BinaryTreeNode*>::const_iterator iter2=path2.begin();
        while(iter1!=path1.end() &&iter2!=path2.end())
        {
            if(*iter1==*iter2)
            {
                pNode=*iter;
                iter1++;
                iter2++;
            }
            else
                break;
        }
        return pNode;
    }
    return NULL;
}

问题13:求二叉树节点的最大距离
即二叉树中相距最远的两个节点之间的距离
递归解法:
(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(maxLeft,maxRight),maxDistLeft+maxDistRight);
}

问题14:由前序遍历和中序遍历重建二叉树

二叉树的谦虚遍历中,第一元素总是树的根节点的值。中序遍历序列中,位于根节点左边的数为树的左子树节点,
位于根节点的右边的数为树的右子树节点
递归解法:
(1)如果前序遍历或者中序遍历为空,或者节点个数小于等于0,返回空
(2)创建根节点。前序遍历中的第一个数为根节点,在中序遍历中找到根节点的位置,可分别得到左子树和右子树的前序遍历和中序遍历,重建左右子树

BinaryTreeNode* ReBuildBinaryTree(int *pPreOrder,int *pInOrder,int NodeNum)
{
    if(pPreOrder==NULL||pInOrder==NULL || NodeNum<=0)
        return NULL;
    BinaryTreeNode* pRoot=new BinaryTreeNode;
    pRoot->m_nValue=pPreOrder[0];
    pRoot->m_pLeft=NULL;
    pRoot->m_pRight=NULL;

    //在中序遍历序列中找到根节点的位子,根节点左边的节点为左子树,右边的数为右子树
    int rootPoistionInOrder=-1;
    for(int i=0;i<NodeNum;i++)
    {
        if(pPreOreder[0]==pInOreder[i])
            {
                rootPositionInOrder=i;
                break;
        }
    }
    if(rootPostionInOrder==-1)
        throw std::exception("Invalid input");
    //重建左子树
    int nodeNumLeft=rootPositionInOrder;
    int* pPreOrderLeft=pPreOrder+1;
    int* pInOrderLeft=pInOrder;
    pRoot->m_pLeft=ReBuildBinaryTree(pPreOrderLeft,pInOrderLeft,nodeNumLeft);

    //重建右子树
    int nodeNumRight=NodeNum-rootPositionInOrder-1;
    int* pPreOrderRight=pPreOrder+rootPositionInOrder+1;
    int* pInOrderRight=pInOrder+rootPositionInOrder+1;
    pRoot->m_Right=ReBuildBinaryTree(pPreOrderRight,pInOrderRight,nodeNumRight);

    return pRoot;
}

问题15:判断二叉树是不是完全二叉树 ( 有点难)
若二叉树的深度为h,则除第h层外,其他层(1~h-1)的节点都达到了最大个数
在第h层,所有的节点都连续集中在最左边,这就是完全二叉树

如下算法:按层遍历(从上到下,从左到右)二叉树,若遇到二叉树左子树为空,则右子树必须为空,如后续遍历左右子树不为空,则不是完全二叉树

bool IsCompleteBinaryTree(BinaryTreeNode* pRoot)
{
    if(pRoot==NULL)
        return false;
    queue<BinaryTreeNode*> q;
    q.push(pRoot);
    bool mustHaveNoChild=false;
    bool result=true;
    while(!q.empty())
    {
        BinaryTreeNode* pNode=q.front();
        q.pop();
        if(!mustHaveNoChild)//已经出现了空子树节点,后面出现的必须为叶子节点(左右子树为空)
        {
            if(pNode->m_pLeft!=NULL &&pNode->m_pRight!=NULL)
            {
                result=false;
                break;
            }
        }
        else
        {
            if(pNode->m_pLeft!=NULL && pNode->m_pRight!=NULL)
            {
                q.push(pNode->m_pLeft);
                q.push(pNode->m_pRight);
            }
            else if(pNode->m_pLeft!=NULL &&pNode->m_pRight==NULL)
            {
                mustHaveNoChild=true;
                q.push(pNode->m_pLeft);
            }
            else if(pNode->m_pLeft==NULL && pNode->m_pRight!=NULL)
            {
                result=false;
                break;
            }
            else
            {
                mustHaveNoChild=true;
            }
        }

    }
    return result;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值