二叉树非递归遍历,重建二叉树等面试题

二叉树非递归遍历

这里写图片描述

先序

头结点入栈,每次循环顺着左路一直找到底,先打印结点,遇右压栈就可以了。

void PreOrderNorOP(pBTNode pRoot)
{
    Stack s;
    init(&s);
    pushBack(&s, pRoot);
    while (!isEmpty(&s)) {
        pBTNode pCur = top(&s);
        pop(&s);
        while (pCur) {
            printf("%c ", pCur->_data);
            if (pCur->_pRight) {
                pushBack(&s, pCur->_pRight);
            }
            pCur = pCur->_pLeft;
        }
    }
}

解法二:类似于层序遍历的写法

void PreOrderNor(pBTNode pRoot)
{
    Stack s;
    init(&s);
    pushBack(&s, pRoot);
    while (!isEmpty(&s)) {
        pBTNode pCur = top(&s);
        pop(&s);
        printf("%c ", pCur->_data);
        if (pCur->_pRight)
            pushBack(&s, pCur->_pRight);
        if (pCur->_pLeft)
            pushBack(&s, pCur->_pLeft);
    }
}

中序

用pCur指向当前处理的结点,向左找到底,压栈保存沿途路径。

void InOrderNor(pBTNode pRoot)
{
    if (pRoot) {
        Stack s;
        init(&s);
        pBTNode pCur = pRoot;
        while (!isEmpty(&s) || pCur) {
            //找到最左结点
            while (pCur) {
                pushBack(&s, pCur);
                pCur = pCur->_pLeft;
            }
            //无左结点,打印出栈
            pCur = top(&s);
            printf("%c ", pCur->_data);
            pop(&s);
            pCur = pCur->_pRight;
        }
    }
}

后序

后序就比较麻烦了。只用一个pCur完成不了任务了,因为如果像中序最后一句返回当前栈顶元素的右孩子,而下次循环还需要判断栈顶(父节点)和右孩子的关系,就会造成死循环。

所以用三个指针。

pCur:迭代临时指针,每次找到左路最底的NULL。仅在遇右子树时改变。

pTop: 代码围绕栈顶元素来写。

pPre:记录上一个打印的结点。例如在C-》F的时候需要判断右子树走过没,如果打印过F了,就记录下来。
这里写图片描述

void PostOrderNor(pBTNode pRoot)
{
    if (pRoot) {
        Stack s;
        init(&s);
        pBTNode pCur = pRoot;
        pBTNode pPre = NULL;
        pBTNode pTop = NULL;
        while (!isEmpty(&s) || pCur) {
            while (pCur) {
                pushBack(&s, pCur);
                pCur = pCur->_pLeft;
            }
            pTop = top(&s);
            //判断如果栈顶结点有右结点且没走过,遍历右子树
            if (pTop->_pRight && pPre != pTop->_pRight) {
                pCur = pTop->_pRight;
            }
            else {
                //左右子树都遍历过了,打印根节点
                pPre = pTop;
                printf("%c ", pTop->_data);
                pop(&s);
                //pCur = top(&s)->_pRight;右子树退回来死循环
            }
        }
    }
}

判断是否完全二叉树——层序

找到第一个不饱和结点(无右,或者无左无右),找到后置标志,队列中其后结点不能有子节点。

int IsComTree(pBTNode pRoot)
{
    LQueue q;
    InitQueue(&q);
    PushQueue(&q, pRoot);
    int flag = 0;
    while (!QueueEmpyt(&q)) {
        pBTNode pCur = QueueFront(&q);
        PopQueue(&q);

        if (flag == 1) {
            if (pCur->_pLeft || pCur->_pRight) {
                return 0;
            }
        }
        if (pCur->_pLeft && pCur->_pRight) {
            //正常的双子结点
            PushQueue(&q, pCur->_pLeft);
            PushQueue(&q, pCur->_pRight);
        }
        else if (pCur->_pLeft) {
            //第一个不饱和结点
            flag = 1;
            PushQueue(&q, pCur->_pLeft);
        }
        else if ((NULL == pCur->_pLeft && NULL == pCur->_pRight)) {
            //也不饱和,但子节点不入队列
            flag = 1;
        }
        else if(pCur->_pRight){
            //没有左子树,有右子数
            return 0;
        }
    }
    return 1;
}

判断结点是否在树里

1.传值

pBTNode IsDataInTree(pBTNode pRoot, BTDataType data)
{
    if (pRoot) {

        if (pRoot->_data == data) {
            return pRoot;
        }
        pBTNode pRet = NULL;
        if (pRet = IsDataInTree(pRoot->_pLeft, data)) {
            return pRet;
        }
        if (pRet = IsDataInTree(pRoot->_pRight, data)) {
            return pRet;
        }

    }
    return NULL;
}

2.传值

int IsNodeInTree(pBTNode pRoot, pBTNode node)
{
    if (pRoot) {

        if (pRoot == node) {
            return 1;
        }
        if (IsNodeInTree(pRoot->_pLeft, node)) {
            return 1;
        }
        if (IsNodeInTree(pRoot->_pRight, node)) {
            return 1;
        }

    }
    return 0;
}

求两个结点的最低公共祖先

普通树

/*递归求两个结点最低公共祖先
若两个结点有一个是根,则根节点是公共祖先。
若两个结点都在左子树,则在左子树找,同理在右子树找。若一左一右,则root是公共祖先
*/
pBTNode ComAscNodeR(pBTNode root, pBTNode n1, pBTNode n2)
{
    if (root == NULL) {
        return NULL;
    }
    if (root == n1 || root == n2) {
        return root;
    }
    if (IsNodeInTree(root->_pLeft, n1) && IsNodeInTree(root->_pLeft, n2)) {
        return ComAscNodeR(root->_pLeft, n1, n2);
    }
    else if(IsNodeInTree(root->_pRight, n1) && IsNodeInTree(root->_pRight, n2)){
        return ComAscNodeR(root->_pRight, n1, n2);
    }
    else {
        return root;
    }
}
/*
非递归求两个结点最低公共祖先
记录从根节点到n1的路径,n2的路径。先出栈路径较长的至两个路径等长,之后同时出栈,第一个相等则为祖先
*/
pBTNode ComAscNodeNor(pBTNode root, pBTNode n1, pBTNode n2)
{
    Stack s1;
    Stack s2;
    init(&s1);
    init(&s2);
    GetPath(&s1, root, n1);
    GetPath(&s2, root, n2);
    int size1 = stackSize(&s1);
    int size2 = stackSize(&s2);
    while (size1 > size2) {
        pop(&s1);
        size1--;
    }
    while (size1 < size2) {
        pop(&s2);
        size2--;
    }
    while (!isEmpty(&s1) && top(&s1)!=top(&s2)) {
        pop(&s1);
        pop(&s2);
    }
    return top(&s1);
}
int GetPath(Stack* s, pBTNode root, pBTNode node) {
    if (root) {
        pushBack(s, root);
        if (root == node) {
            return 1;
        }
        if (GetPath(s, root->_pLeft, node) || GetPath(s, root->_pRight, node)) {
            return 1;
        }
        pop(s);
    }
    return 0;
}

二叉搜索树

/*
二叉搜索树找最低公共祖先
如果有一个结点值等于根,则root是祖先。若两个都比根小则祖先在左子树,若一个大一个小,则是root。
*/
pBTNode ComAscNodeSearch(pBTNode root, pBTNode n1, pBTNode n2)
{
    if (NULL == root) {
        return NULL;
    }
    if (n1->_data == root->_data || n2->_data == root->_data) {
        return root;
    }
    if (n1->_data < root->_data && n2->_data < root->_data) {
        return ComAscNodeR(root->_pLeft, n1, n2);
    }
    else if (n1->_data > root->_data && n2->_data > root->_data) {
        return ComAscNodeR(root->_pRight, n1, n2);
    }
    else {
        return root;
    }
}

三叉树,每个结点包含parent指针。

转化为从node1,node2到root结点的两条链表求交点的问题。求出len1,len2,较长的先走几步至两个链表同长度,若结点相同则为交点,不相同则两链表指针各前进一步。

判断一棵二叉树是否是平衡二叉树

//含有重复递归
int IsBalance(pBTNode root)
{
    if (root == NULL) {
        return 1;
    }
    int left = Height(root->_pLeft);
    int right = Height(root->_pRight);
    int diff = left - right;
    if (diff > 1 && diff < -1) {
        return 0;
    }
    return IsBalance(root->_pLeft)&&IsBalance(root->_pRight);
}

//后序思想,精髓在于每次向下递之前用变量记录归回时的值。
int IsBalanceOP(pBTNode root, int* depth)
{
    if (root == NULL) {
        *depth = 0;
        return 1;
    }
    int left, right;
    if (IsBalanceOP(root->_pLeft, &left) && IsBalanceOP(root->_pRight, &right)) {
        int diff = left - right;
        if (diff <= 1 && diff >= -1) {
            *depth = left > right ? left + 1 : right + 1;
            return 1;
        }
    }
    return 0;
}

前序遍历和中序遍历重建二叉树

//此题过程复杂,必须画图仔细掌握一次递归的细节过程
pBTNode _RebuildTree(BTDataType* preStart, BTDataType* preEnd, BTDataType* inStart, BTDataType* inEnd);

pBTNode RebuildTree(BTDataType* preOrder, BTDataType* inOrder, int length)
{
    if (preOrder == NULL || inOrder == NULL ) {
        return NULL;
    }
    return _RebuildTree(preOrder, preOrder + length - 1, inOrder, inOrder + length - 1);
}

pBTNode _RebuildTree(BTDataType* preStart, BTDataType* preEnd, BTDataType* inStart, BTDataType* inEnd)
{
    //创建当前根节点
    pBTNode pNew = BuyBinTreeNode(preStart[0]);

    //在中序里找到根节点
    BTDataType* pRoot = inStart;
    while (pRoot != inEnd && *pRoot != *preStart ) {
        pRoot++;
    }
    if (pRoot == inEnd && *pRoot != *preStart) {
        exit(1);
    }
    //计算中序左子树节点数
    int leftLength = pRoot - inStart;
    //计算前序左子树节点数
    BTDataType* preLeft = preStart + leftLength;
    if (leftLength > 0) {
        //当前根节点还有左子树
        pNew->_pLeft = _RebuildTree(preStart + 1, preLeft, inStart, pRoot - 1);
    }
    //重点!当上一个根节点左子树的节点数  >  当前根节点左子树节点,证明当前根节点有右子树
    if (preEnd - preStart > leftLength) {
        pNew->_pRight = _RebuildTree(preLeft + 1, preEnd, pRoot + 1, inEnd);
    }
    return pNew;
}

c++版本思路

/* 先序遍历第一个位置肯定是根节点node,

中序遍历的根节点位置在中间p,在p左边的肯定是node的左子树的中序数组,p右边的肯定是node的右子树的中序数组

另一方面,先序遍历的第二个位置到p,也是node左子树的先序子数组,剩下p右边的就是node的右子树的先序子数组

把四个数组找出来,分左右递归调用即可

struct TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> in){
    //获得序列的长度
    //分别存储先序序列的左子树,先序序列的右子树,中序序列的左子树,中序序列的右子树

    //先序遍历第一个位置肯定是根节点node,取其值
    //新建一个树结点,并传入结点值
    //p用于存储中序序列中根结点的位置


    //建立中序序列的左子树和前序序列的左子树
    //前序第一个为根节点,+1从下一个开始记录
    //建立中序序列的右子树和前序序列的右子树

    //取出前序和中序遍历根节点左边和右边的子树
    //递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
    }
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>