剑指offer 重建二叉树

问题描述

面试题07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

问题分析

首先明确二叉树的前序遍历和中序遍历的定义。

  • 前序遍历:先访问根结点,再访问根结点的左子树,最后访问根结点的右子树;
  • 中序遍历:先访问根结点的左子树,再访问根结点,最后访问根结点的右子树。

可以自己试一试写出上面的二叉树的前序和中序,这样做有助于接下来的理解。
根据定义可以总结出一些性质:

  • 前序遍历的性质:访问的第一个节点必然是根结点,即3一定是根结点;
  • 中序遍历的性质:根结点分隔左右子树,即通过先序已经知道了根结点是3,则[9]一定是3的左子树,[15,20,7]一定是3的右子树。

根据以上两条性质,就可以确定一个节点以及它的左右子树。
问题解决的步骤:
(1)根据当前子树(子树也是二叉树)的前序遍历的第一个节点确定当前子树的根结点;
(2)根据当前子树(子树也是二叉树)的中序遍历确定根结点的左右子树;
(3)对左右子树再分别执行步骤(1)和(2);
(4)如果只剩余一个节点,那么这就是叶结点。无需再进行划分。

代码实现

#include <stdio.h>
#include <stdlib.h>

// Definition for a binary tree node.
struct TreeNode {
  int val;
  struct TreeNode *left;
  struct TreeNode *right;
};

typedef struct TreeNode TNode;

// create a new node
TNode *newTreeNode(int val) {
  TNode *tnode = (TNode *)malloc(sizeof(TNode));
  tnode->val = val;
  return tnode;
}

TNode *rebuild(int *preorder, int pl, int pr, int *inorder, int inl, int inr) {
  if(pl > pr) return NULL;
  TNode *tnode = newTreeNode(preorder[pl]);
  if (pl <= pr) {
    int mid = inl;
    for (; mid <= inr; mid++) {
      if (inorder[mid] == preorder[pl]) {
        break;
      }
    }
    // 左子树节点的数量
    int leftTreeSize = mid - inl;
    tnode->left = rebuild(preorder, pl+1, pl + leftTreeSize, inorder, inl, mid-1);
    tnode->right = rebuild(preorder, pl+leftTreeSize+1, pr, inorder, mid+1, inr);
  }

  return tnode;
}

struct TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
  return rebuild(preorder, 0, preorderSize-1, inorder, 0, inorderSize-1);
}

// 先序遍历 测试一下
void preOrderTest(TNode *root) {
  printf("%d  ", root->val);
  if(root->left != NULL) preOrderTest(root->left);
  if(root->right != NULL) preOrderTest(root->right);
}

int main() {
  int preorder[] = {3,9,20,15,7};
  int inorder[] = {9,3,15,20,7};
  TNode* treeRoot = buildTree(preorder, 5, inorder, 5);
  preOrderTest(treeRoot);
  return 0;
}

特殊情况

(1)二叉树为空
(2)二叉树存在重复元素。

第(1)个情况很好解决,但是第二个就要考虑一下了。比如下面这种情况:

(0)既有左孩子又有右孩子
  3
 / \
3   3

前序序列:[3,3,3]
中序序列:[3,3,3]

根据前序和中序能否还原二叉树呢?看下面这两棵树:

(1)只有左孩子
    3
   /
  3
 /
3

(2)只有右孩子
3
 \
  3
   \
    3

因为上图这(0)(1)(2)三棵树的前序和后序相同,所以不能确定唯一的二叉树。
是否包含重复元素的所有二叉树都无法被还原呢?考虑下面这种情况:

(4)包含相同元素的二叉树
  3
 / \
2   3
前序序列:[3,2,3]
中序序列:[2,3,3]

似乎这种情况是可以唯一确定二叉树的。也就是说在有重复元素的情况下,有可能是可以唯一确定二叉树的。幸好这个题目指明了没有重复元素,否则代码需要做一些调整。

测试

直接输出重建的二叉树的前序和中序,与原本的序列对照即可。

// 先序遍历 测试一下
void preOrderTest(TNode *root) {
  printf("%d  ", root->val);
  if(root->left != NULL) preOrderTest(root->left);
  if(root->right != NULL) preOrderTest(root->right);
}

如有不当之处,欢迎指出!

posted @ 2020-03-16 11:32  flyawayl  阅读(145)  评论(0编辑  收藏  举报