面试题五 重建二叉树

题目

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

分析

  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. 留意函数参数的传递方式是值传递,尤其是在传递的参数为指针时。必要时采用引用可以避免繁琐的多级指针

posted on 2014-03-18 16:16  空山悟  阅读(233)  评论(0编辑  收藏  举报

导航