5. 从遍历序列恢复二叉树

一,什么样的序列组合才能恢复一棵二叉树 ?

            1,前序+中序        2,后序+中序        3,特殊的层序遍历(需要记录空节点)
    
            前序遍历的结果 [根节点, [左子树], [右子树]]
            中序遍历的结果 [[左子树], 根节点, [右子树]]
            后序遍历的结果 [[左子树], [右子树], 根节点]
    
            所以若要恢复一棵二叉树,必须要包含 "中序遍历" 的结果,因为只有中序遍历中的左子树和右子树是分开的。
            同理,要想将中序遍历结果中左右子树分开,必须要找到根节点,所以还需要另外一个遍历结果。


二,需要注意的细节

        因为我们是根据 "根节点的值" 来将中序遍历结果进行划分,所以要确保该值是唯一的,即在左右子树中,不能出现与根节点值有相同值的节点。
        简而言之,被恢复的二叉树所有节点中,每个节点所包含的值都是唯一的。(也就是遍历的结果中,每个值都是不同的)

三,恢复的方法

        1,递归法 (该递归本质也算是一个前序遍历算法)
        2,迭代法 [还没明白,后续明白了再更新]

四,头文件

#ifndef __TREE_NODE_H__
#define __TREE_NODE_H__

#include <time.h>
#include <stdlib.h>
#include <vector>

#define NUM_SIZE 10

#define PRINT(vec) \
    do { \
	    for (auto it = (vec).begin(); it != (vec).end(); it++) { \
	        std::cout << *it << " "; \
	    } \
	    std::cout << std::endl << std::endl; \
    } while(0)

struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode() :val(0), left(nullptr), right(nullptr) {}
    TreeNode(int v) :val(v), left(nullptr), right(nullptr) {}
    TreeNode(int v, TreeNode* l, TreeNode* r) : val(v), left(l), right(r) {}
};

#define PRE_IN_BINARY_TREE 0
#define POST_IN_BINARY_TREE !PRE_IN_BINARY_TREE

static void preOrder(TreeNode* root, std::vector<int>& vec);	// 前序遍历
static void inOrder(TreeNode* root, std::vector<int>& vec);	    // 中序遍历
static void postOrder(TreeNode* root, std::vector<int>& vec);	// 后序遍历
static TreeNode* getRandomTree();	// 获取一棵随机二叉树

// 验证结果,若 flag == 0, 则验证 pre + in; 若 flag == 1, 则验证 post + in。
static bool verifyResult(TreeNode* root, std::vector<int> order, std::vector<int> inorder, int flag);

void preOrder(TreeNode* root, std::vector<int>& vec) {
    if (root) {
	    vec.emplace_back(root->val);
	    preOrder(root->left, vec);
	    preOrder(root->right, vec);
    }
}

void inOrder(TreeNode* root, std::vector<int>& vec) {
    if (root) {
	    inOrder(root->left, vec);
	    vec.emplace_back(root->val);
	    inOrder(root->right, vec);
    }
}

void postOrder(TreeNode* root, std::vector<int>& vec) {
    if (root) {
	    postOrder(root->left, vec);
	    postOrder(root->right, vec);
	    vec.emplace_back(root->val);
    }
}

bool verifyResult(TreeNode* root, std::vector<int> order, std::vector<int> inorder, int flag) {
    std::vector<int> ans;

    inOrder(root, ans);

    if (ans.size() != inorder.size()) {
	    return false;
    }

    for (int i = 0; i < ans.size(); i++) {
	    if (ans[i] != inorder[i]) {
	        return false;
	    }
    }

    ans.clear();

    if (flag == 0) {
	    preOrder(root, ans);
    }
    else {
	    postOrder(root, ans);
    }
	

    if (ans.size() != order.size()) {
	    return false;
    }

    for (int i = 0; i < ans.size(); i++) {
        if (ans[i] != order[i]) {
            return false;
        }
    }
    return true;
}

TreeNode* getRandomTree() {
    srand((unsigned)time(NULL));

    TreeNode* root = new TreeNode(0);
    TreeNode* tmp = root;

    int val = 1;
    for (int i = 0; i < NUM_SIZE; i++) {
        TreeNode* node = new TreeNode(val++);// 每次都要使用不同的 val,故让其递增即可

	    int seed = rand() % 2;
	    if (seed == 0) {
	        tmp->left = node;
	    }
	    else {
	        tmp->right = node;
	    }
	    tmp = node;
    }
    return root;
}
#endif // __TREE_NODE_H__

五,前序+中序 恢复一棵二叉树

#include "TreeNode.h"

#if PRE_IN_BINARY_TREE

#include <iostream>
using namespace std;

/*
    该递归函数的定义 : 通过前序遍历和中序遍历的指定区间,恢复并返回二叉树的根节点
    该递归的本质也是一个前序遍历算法
*/
TreeNode* build(vector<int>& preorder, vector<int>& inorder, int preStart, int preEnd, int inStart, int inEnd) {
    if (preStart > preEnd || inStart > inEnd) {		// 若越界,则直接返回空节点
	    return nullptr;
    }

    TreeNode* root = new TreeNode(preorder[preStart]);// 前序遍历的起始节点就是根节点

    int offset = 0;    // offset 为偏移量,为了在 中序遍历结果 中找到根节点,从而将左右子树分开
    while (inorder[inStart + offset] != preorder[preStart]) {		// 这一步可以优化,因为每个值都是唯一的,可以使用 map 将值与 index 相绑定
	    offset++;
    }

    /*
        对于 前序遍历 [根,左,右] ,左子树的起点就是 preStart+1, 终点就是 preStart+左子树节点个数
	对于 中序遍历 [左, 根, 右] ,我们通过 offset 找到了 根节点,故左子树的节点个数就是 offset。

		preStart		                      preEnd
		 |				                      |
		 v				                      v
pre		[根, [左1, ...... , 左n], [右1, ......, 右n]]

		inStart		                         inEnd
		  |				                        |
		  v			                            v
in		[[左1, ...... , 左n], 根, [右1, ......, 右n]]
		  ^		            ^
		  |		            |
		offset = 0          offset

	*/
    root->left = build(preorder, inorder, preStart + 1, preStart + offset, inStart, inStart + offset - 1);
    root->right = build(preorder, inorder, preStart + offset + 1, preEnd, inStart + offset + 1, inEnd);
    return root;
}

/*
    采用递归法恢复二叉树
*/
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
}

int main() {
    TreeNode* root = getRandomTree();

    vector<int> preorder;
    vector<int> inorder;

    preOrder(root, preorder);
    cout << "before preorder : " << endl;
    PRINT(preorder);

    inOrder(root, inorder);
    cout << "before inorder : " << endl;
    PRINT(inorder);

    root = buildTree(preorder, inorder);

    bool ret = verifyResult(root, preorder, inorder, 0);
    cout << "ret : " << ret << endl;

    return 0;
}

#endif // PRE_IN_BINARY_TREE

六,后序+中序 恢复一棵二叉树

#include "TreeNode.h"

#if POST_IN_BINARY_TREE

#include <iostream>
using namespace std;

TreeNode* build(vector<int>& postorder, vector<int>& inorder, int postStart, int postEnd, int inStart, int inEnd) {
    if (postStart > postEnd || inStart > inEnd) {
	    return nullptr;
    }

    TreeNode* root = new TreeNode(postorder[postEnd]);

    int offset = 0;
    while (inorder[inStart + offset] != postorder[postEnd]) {
	    offset++;
    }

    root->left = build(postorder, inorder, postStart, postStart + offset - 1, inStart, inStart + offset - 1);
    root->right = build(postorder, inorder, postStart + offset, postEnd - 1, inStart + offset + 1, inEnd);
    return root;
}

TreeNode* buildTree(vector<int>& postorder, vector<int>& inorder) {
    return build(postorder, inorder, 0, postorder.size() - 1, 0, inorder.size() - 1);
}


int main() {
    TreeNode* root = getRandomTree();

    vector<int> postorder;
    vector<int> inorder;

    postOrder(root, postorder);
    cout << "before postorder : " << endl;
    PRINT(postorder);

    inOrder(root, inorder);
    cout << "before inorder : " << endl;
    PRINT(inorder);

    root = buildTree(postorder, inorder);

    bool ret = verifyResult(root, postorder, inorder, 1);
    cout << "ret : " << ret << endl;

    return 0;
}
#endif // POST_IN_BINARY_TREE

七,特殊的层序遍历 恢复一棵二叉树

/*
    特殊的层序遍历,需要记录非空节点的两个孩子,即使非空节点的孩子是空节点,也要占位。

        1
       /  \
      2    3
     / \  / \
    #  4  #  #
      / \
     #  #

    层序遍历需要记录的结果是 [ 1, 2, 3, #, 4, #, #, #, #]

    则我们可以通过上述的遍历结果,将该二叉树恢复。(该方法也可用于二叉树的序列化与反序列化)

*/

/*  NULL_NUM 表示 #,就是一个与二叉树中所有节点值都不相同的一个数字 */
void levelOrder(TreeNode* root, vector<int>& vec, int NULL_NUM) {
    if (root) {
        queue<TreeNode*> queue;
        queue.emplace(root);

        while (!queue.empty()) {
            TreeNode* front = (TreeNode*)queue.front();
            queue.pop();     // 弹出该节点
            
            if (front == nullptr) {    // 因为有可能将空节点加入,所以需要判空
                vec.emplace_back(NULL_NUM);    // 若是空节点,则加入特殊标志的数字
            }
            else {
                vec.emplace_back(front->val);

                queue.emplace(front->left);    // 注意,此处与正常的层序遍历不同,因为需要将非空节点的两个孩子都记录,所以即使是空孩子,也要加入
                queue.emplace(front->right);
            }
        }
    }
}

/*
    如何通过上述的序列恢复二叉树呢 ?
        恢复的方法与遍历的方法一致,也是需要通过队列。
*/

TreeNode* buildTree(vector<int> vec, int NULL_NUM) {
    if (vec.size() == 0)    return nullptr;
    
    TreeNode* root = new TreeNode(vec[0]);
    queue<TreeNode*> queue;
    queue.emplace(root);

    for (int i=1; i<vec.size(); i++) {
        TreeNode* front = (TreeNode*)queue.front();
        queue.pop();

        if (vec[i] != NULL_NUM) {
            TreeNode* left = new TreeNode(vec[i]);
            front->left = left;
            queue.emplace(left);    // 需要将非空节点加入队列
        }

        if (vec[++i] != NULL_NUM) {    // 需要 ++i 
            TreeNode* right = new TreeNode(vec[i]);
            front->right = right;
            queue.emplace(right);
        }
    }

    return root;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值