由前序遍历和中序遍历创建二叉树

点:二叉树的理解,代码能力


题意:由前序遍历序列和中序遍历的序列,创建对应的二叉树。

    如前序序列:16, 7, 6, 3, 14, 12, 8, 15, 28, 23, 27, 41, 30, 29, 55

    和中序序列:3, 6, 7, 8, 12, 14, 15, 16, 23, 27, 28, 29, 30, 41, 55

剑指offer面试题6


思路:1、前序遍历序列的第一个元素就是根节点,这和后序遍历整好相反,后续遍历序列是最后一个元素是根节点

    2、对于典型的二叉树,即“左节点比父节点的值小,右节点比父节点的值大”这样的二叉树,根本无需中序序列,仅前序或后序即可唯一确定一个二叉树。

    3、但这类题中想要得出的二叉树是不用遵循“左节点比父节点的值小,右节点比父节点的值大”的规则的,这时单独的前序遍历或后序遍历包括按层遍历,其序列无法对应一个唯一二叉树。必须有中序遍历序列。

  原因非常简单:前序和后序在不遵循左小右大规则时,无法确定叶子节点是其父节点的左孩子还是右孩子。如父节点A有一个子节点B,无论左孩子是B还是右孩子是B,前序遍历序列结果永远是AB,后序遍历序列结果也永远是BA,这时,即便同时给定前序遍历序列和后序遍历序列,依然不知道到底B是A的左子节点还是右子节点。

然而无论是前序还是后序,若再给定中序遍历序列,那么就可以确定B是A的左子节点还是右子节点,因为中序本身就是给定了子节点是父节点的左孩子还是右孩子。


    4、基于上面的1-3,创建方式如下:
1、前序遍历序列:第一个是根节点,由中序序列确定左子树的部分,右子树的部分

2、左子树第一个节点,左子树的根节点,然后如法炮制第1步,右子树亦然,这些典型使用递归

中序的左右界定的功能:有效的区分了左右子树以及左右叶子节点,这是这类题的关键

3、递归创建节点,注意递归结束条件即边界条件


后序遍历序列 + 中序遍历序列、按层遍历序列 + 中序遍历序列,都可以类似的道理生成唯一二叉树,其他都不行,原因就是不能确定孩子是左还是右。


代码:


#include <iostream>

template<class T> struct Node {
    T val;
    Node<T> *lchild;
    Node<T> *rchild;
    Node(T _val):val(_val), lchild(nullptr), rchild(nullptr) {}
};

template<class T> class Btree {
    Node<T> *root;

public:
    Btree():root(nullptr) {}
    void Free (Node<T> *cur) {
        if (cur) {
            Free(cur->lchild);
            Free(cur->rchild);
            delete cur;
            cur = nullptr;
        }
    }
    ~Btree () {
        Free(root);
    }

    void Add (T val) {
        if (!root) {
            root = new Node<T>(val);
        } else {
            Node<T> *cur = root;
            while (cur) {
                if (cur->val > val) {
                    if (cur->lchild) {
                        cur = cur->lchild;
                    } else {
                        cur->lchild = new Node<T>(val);
                        break;
                    }
                } else if (cur->val < val) {
                    if (cur->rchild) {
                        cur = cur->rchild;
                    } else {
                        cur->rchild = new Node<T>(val);
                        break;
                    }
                } else {
                    break;
                }
            }
        }
    }

    void Pre (Node<T> *cur) {
        if (cur) {
            std::cout << cur->val << "\t";
            Pre(cur->lchild);
            Pre(cur->rchild);
        }
    }
    
    void pre () {
        Pre(root);
        std::cout << std::endl;
    }

    void Mid (Node<T> *cur) {
        if (cur) {
            Mid(cur->lchild);
            std::cout << cur->val << "\t";
            Mid(cur->rchild);
        }
    }
    
    void mid () {
        Mid(root);
        std::cout << std::endl;
    }
};

void recreate (int *pre, int *mid, const int size, int prestart, int preend, int midstart, int midend, Btree<int> &bt) {
    if (preend - prestart != midend - midstart) {
        return;
    }

    if (prestart > preend || midstart > midend || prestart >= size || preend >= size || !pre || !mid) {
        return;
    }

    std::cout << "inesrt node idx " << prestart << "(" << pre[prestart] << ")" << std::endl;
    bt.Add(pre[prestart]);
    if (prestart == preend && midstart == midend) {
        return;
    }
    
    int mididx = -1, count = 0;
    for (int i = midstart; i <= midend; i++) {
        if (pre[prestart] == mid[i]) {
            mididx = i;
            break;
        } else {
            ++count;
        }
    }

    std::cout << "left(pre; mid): " << (prestart + 1) << ", " << prestart + count << "; " << midstart << ", " << (mididx - 1) << std::endl;
    std::cout << "right(pre; mid): " << (prestart + count + 1) << ", " << preend << "; " << (mididx + 1) << ", " << midend << std::endl << "---------------------" << std::endl;
    if (mididx > 0) {
        recreate(pre, mid, size, prestart + 1, prestart + count, midstart, mididx - 1, bt);
        recreate(pre, mid, size, prestart + count + 1, preend, mididx + 1, midend, bt);
    }
}

void recreate_btree_by_pre_and_mid (int *pre, int *mid, int size) {
    Btree<int> bt;
    recreate(pre, mid, size, 0, size - 1, 0, size - 1, bt);
    bt.pre();
    bt.mid();
}

int main () {
    int pre[] = {16, 7, 6, 3, 14, 12, 8, 15, 28, 23, 27, 41, 30, 29, 55};
    int mid[] = {3, 6, 7, 8, 12, 14, 15, 16, 23, 27, 28, 29, 30, 41, 55};
    recreate_btree_by_pre_and_mid(pre, mid, sizeof(pre)/sizeof(pre[0]));
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值