题目
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
分析
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. 留意函数参数的传递方式是值传递,尤其是在传递的参数为指针时。必要时采用引用可以避免繁琐的多级指针。