zoukankan      html  css  js  c++  java
  • 剑指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);
    }
    

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

  • 相关阅读:
    判断数据库内容,在页面显示自定义数据case when
    其他窗体赋值给comboBox实现值的回显,并使赋的值处于选中状态(根据text获取selectedindex)
    DataGridView的按钮列的点击事件
    表单中各种input汇总
    块元素、内敛元素、行内块元素特点、相互转换、取消默认效果
    内联元素,取消间隙和默认效果
    margin-top塌陷
    linux下安装 配置 redis数据库
    redis数据库各种数据类型的增删改查命令
    python与mongodb的交互 增删改差
  • 原文地址:https://www.cnblogs.com/flyawayl/p/12502724.html
Copyright © 2011-2022 走看看