zoukankan      html  css  js  c++  java
  • 面试题五 重建二叉树

    题目

      输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。

    分析

      1. 取先序遍历的第一个值为树的根结点

      2. 在中序遍历中找到1中的那个结点

      3. 中序遍历中,处于该结点左侧的所有结点,是该树左子树的中序遍历;处于该结点右侧的所有结点,是该树右子树的中序遍历。

      4. 前序遍历中,左右子树的前序遍历也是分开放的。因此根据3中获得的中序遍历的长度,可以将前序遍历分割为左右子树的前序遍历。

      根据这个思路,就可以写出递归算法/程序了。

    代码实现( 含测试 )

      1 #include <iostream>
      2 
      3 using namespace std;
      4 
      5 // 定义二叉树结点类型
      6 struct BinaryTreeNode {
      7     int value;
      8     BinaryTreeNode * left;
      9     BinaryTreeNode * right;
     10 };
     11 
     12 // 构建二叉树函数
     13 BinaryTreeNode * construct( BinaryTreeNode * &T, int *pre, int *in, int len );
     14 
     15 // 遍历函数
     16 void preOrderPrint( BinaryTreeNode *T );
     17 void inOrderPrint( BinaryTreeNode *T );
     18 void postOrderPrint( BinaryTreeNode *T );
     19 
     20 // 定义最大处理结点数
     21 const int MAX = 1000;
     22 
     23 // 定义合法标志
     24 int TAG=0; 
     25 
     26 int main()
     27 {
     28     int preOrder[MAX];
     29     int inOrder[MAX];
     30 
     31     /*
     32      * 获取前序和中序序列。
     33     */
     34     int n;
     35     cout << "请输入树的结点数( 前/中序遍历的长度 ):" << endl;
     36     cin >> n;
     37     if (n >= MAX) {
     38         cout << "结点数过大!" << endl;
     39         return 1;
     40     }
     41     if (n <= 0) {
     42         cout << "输入结点数异常,无法构造树。" << endl;
     43         return 1;
     44     }
     45 
     46     cout << "请输入前序遍历序列:" << endl;
     47     for (int i=0; i<n; i++) {
     48         cin >> preOrder[i];
     49     }
     50 
     51     cout << "请输入中序遍历序列:" << endl;
     52     for (int i=0; i<n; i++) {
     53         cin >> inOrder[i];
     54     }
     55 
     56     // 初始化根结点,前序遍历起点,中序遍历起点。
     57     BinaryTreeNode * T=NULL;
     58     int *pre = preOrder;
     59     int *in = inOrder;
     60 
     61     // 调用构建函数构建二叉树
     62     T = construct( T, preOrder, inOrder, n );
     63 
     64     // 如果输入的前序和中序序列不匹配。
     65     if (TAG == 1) {
     66         cout << "前序和中序序列不匹配,构造失败。" << endl;
     67         return 1;
     68     }
     69 
     70     // 构建成功,打印此二叉树。
     71     cout << endl << "构建成功,下面输出此二叉树:" << endl;
     72     cout << "前序:" << endl;
     73     preOrderPrint(T);
     74     cout << endl;
     75     cout << "中序:" << endl;
     76     inOrderPrint(T);
     77     cout << endl;
     78     cout << "后序:" << endl;
     79     postOrderPrint(T);
     80     cout << endl;
     81 
     82     return 0;
     83 }
     84 
     85 // 第一个参数务必要声明为指针的引用类型
     86 BinaryTreeNode * construct( BinaryTreeNode * &T, int *pre, int *in, int len )
     87 {
     88     // 如果输入的前序和中序序列不匹配
     89     if (TAG == 1) return NULL;
     90 
     91     // 如果为空树
     92     if (len == 0) {
     93         return NULL;
     94     }
     95 
     96     // 构造新结点
     97     T = new BinaryTreeNode();
     98     T->value = pre[0];
     99 
    100     // 找到中序遍历数组中左右子树的" 分割点 "
    101     int i=0;
    102     while (in[i] != pre[0]) {
    103         i++;
    104     }
    105 
    106     // 如果输入的前序和中序序列不匹配
    107     if (i >= len) {
    108         TAG = 1;
    109         return NULL;
    110     }
    111 
    112     // a, b为左,右子树的前序遍历在pre的起始位置
    113     int a,b;
    114     // p, q为左,右子树的中序遍历在in中的起始位置
    115     int p,q;
    116     p = 0;
    117     q = i+1;
    118     a = 1;
    119     b = q;
    120 
    121     // 递归构建左子树
    122     T->left = construct(T->left, &pre[a], &in[p], i);
    123     // 递归构建右子树
    124     T->right = construct(T->right, &pre[b], &in[q], len-i-1);
    125 
    126     return T;
    127 }
    128 
    129 /*
    130  * 三种测试用的遍历函数
    131 */
    132 // 先序 
    133 void preOrderPrint( BinaryTreeNode *T ) {
    134     if (!T) return;
    135     cout << T->value << " ";
    136     preOrderPrint(T->left);
    137     preOrderPrint(T->right);
    138 }
    139 
    140 // 中序 
    141 void inOrderPrint( BinaryTreeNode *T ) {
    142     if (!T) return;
    143     inOrderPrint(T->left);
    144     cout << T->value << " ";
    145     inOrderPrint(T->right);
    146 }
    147 
    148 // 后序
    149 void postOrderPrint( BinaryTreeNode *T ) {
    150     if (!T) return;
    151     postOrderPrint(T->left);
    152     postOrderPrint(T->right);
    153     cout << T->value << " ";
    154 } 

    运行测试

      正常输入:

      

      异常输入( 结点数为0或负数 ):

      

      异常输入( 前序,中序序列不匹配 ):

      

     小结

      1. 对于一些较为复杂的问题,需要通过画图来细致分析,从而发现规律

      2. 分析问题要全面,应当在考虑到一般输入后,思考特殊输入的问题。

      3. 递归思想可以解决很多经典的问题。

      4. 留意函数参数的传递方式是值传递,尤其是在传递的参数为指针时。必要时采用引用可以避免繁琐的多级指针

  • 相关阅读:
    SpringBoot笔记十三:引入webjar资源和国际化处理
    Mybatis笔记二:接口式编程
    Mybatis笔记一:写一个demo
    SpringBoot笔记十一:html通过Ajax获取后端数据
    MarkDown语法
    Spring Boot笔记十:IOC控制反转
    USB2.0学习笔记连载(三):通用USB驱动程序解析
    《FPGA全程进阶---实战演练》第四章之Quartus II使用技巧
    摄像头模组基础扫盲
    USB2.0学习笔记连载(二):USB基础知识简介
  • 原文地址:https://www.cnblogs.com/scut-fm/p/3606924.html
Copyright © 2011-2022 走看看