【算法导论】 二叉树的重构
二叉树的重构是指通过二叉树的遍历结果得到二叉树的构造。
重构方案: 前序遍历( PreOrder) + 中序遍历(InOrder)
后序遍历(PostOrder) + 中序遍历(InOrder)
前序遍历( PreOrder) + 后续遍历(PostOrder)(只对真二叉树成立)
前序遍历+后序遍历:
对于一颗二叉树, 其前序遍历可表示为: Root + LeftTree + RightTree
中序遍历可表示为: LeftTree + Root + RightTree
前序遍历的第一个结果为根节点,通过查找可确定中序遍历中根节点的位置,则可确定左子树和右子树中序遍历下的结果。确定左子树和右子树节点数量,则可确定左子树和右子树前序遍历下的结果。得到左右子树中序和前序遍历结果,则可递归的构造出二叉树。
(下面程序以此作为实例对二叉树进行重构)
#include<stdio.h>
/** BinaryTree DS */
typedef struct BinaryTree
{
int key;
BinaryTree *lson;
BinaryTree *rson;
}BinaryTree;
/** find a key in array A */
int Array_Find( int *A, int begin, int end, int key)
{
for( int i=begin; i<end; i++) {
if( key == A[i]) return i;
}
return -1;
}
/** 重构二叉树, 参数: PreOrder[pbegin ... pend]
* InOrder[ibegin ... iend]
*/
BinaryTree *GetBinaryTree( int *PreOrder, int pbegin, int pend,
int *InOrder, int ibegin, int iend)
{
if( pbegin>=pend || ibegin>=iend) return NULL; // 出口, 无节点情况
BinaryTree *Root = new BinaryTree; // 分配在堆上, 函数结束时不会消失
*Root = { PreOrder[pbegin], NULL, NULL}; // 构造根节点
int r = Array_Find( InOrder, ibegin, iend, PreOrder[pbegin]); // root's position in InOrder
// 确定左子树、右子树 前序、中序遍历结果, 递归求解 */
Root->lson = GetBinaryTree( PreOrder, pbegin+1, (r-ibegin)+pbegin+1, InOrder, ibegin, r);
Root->rson = GetBinaryTree( PreOrder, (r-ibegin)+pbegin+1, pend, InOrder, r+1, iend);
return Root;
}
/** 中序遍历 */
void Tree_Print( BinaryTree *Root)
{
if( Root==NULL) return;
printf( "key: %d\n", Root->key); //visit tree
if( Root->lson !=NULL) printf( "lson: %d\n", Root->lson->key);
if( Root->rson !=NULL) printf( "rson: %d\n", Root->rson->key);
printf( "\n\n");
Tree_Print( Root->lson);
Tree_Print( Root->rson);
}
int main()
{
int PreOrder[] = { 43, 12, 8, 32, 16, 35, 78, 56, 88, 83, 121, 97};
int InOrder[] = { 8, 12, 16, 32, 35, 43, 56, 78, 83, 88, 97, 121};
BinaryTree *Root = GetBinaryTree( PreOrder, 0, 12, InOrder, 0, 12);
Tree_Print( Root);
return 0;
}
前序遍历+后序遍历:
真二叉树( Proper Binary Tree) : 所有节点出度为0或2的二叉树,即左子树右子树皆空或皆非空的二叉树。
真二叉树定义来自邓俊辉教授的慕课,国外称为 满二叉树(Full Binary Tree),注意和国内教材中的满二叉树区别。
因为当某节点只有一颗子树时,前序遍历结果为 Root + SonTree,后序遍历结果为 SonTree + Root。通过前序遍历和后序遍历无法确定SonTree为左子树和右子树,因此不能还原这种二叉树的结构。
对于真二叉树来说,前序遍历可表示为: Root + LeftTreeRoot + LeftTree + RightTreeRoot + RightTree
后续遍历可表示为: LeftTree + LeftTreeRoot + RightTree + RightTreeRoot + Root
可通过前序遍历确定左子树根节点,通过后序遍历确定右子树根节点,从而确定左右子树前序、后续遍历结果,递归求得二叉树结构。
根据前序、后序判断是否为真二叉树:
若节点左子树为空,前序遍历前两个节点为 根节点 + 左子树根节点;后序遍历后两个节点为 根节点 + 左子树根节点;两者结果相同,右子树同上。若节点左右子树全为空 或 全不为空,都不会出现节点逆序相同情况。则可通过递归依次判断节点是否为真二叉树。
下面code忽略了判断是否为真二叉树,当节点只有一颗子树时,都当做左子树进行处理。
/** 重构二叉树, 参数: PreOrder[pbegin ... pend]
* PostOrder[tbegin ... tend]
*/
BinaryTree *GetBinaryTree( int *PreOrder, int pbegin, int pend,
int *PostOrder, int tbegin, int tend)
{
if( pbegin>=pend || tbegin>=tend) return NULL; // 出口, 无节点情况
BinaryTree *Root = new BinaryTree; // 分配在堆上, 函数结束时不会消失
*Root = { PreOrder[pbegin], NULL, NULL}; // 构造根节点
if( pbegin+1==pend) return Root; // 不存在左右子树情况
int lr = Array_Find( PostOrder, tbegin, tend, PreOrder[pbegin+1]); // leftsonRoot position in PostOrder
// 确定左子树、右子树 前序、中序遍历结果, 递归求解 */
Root->lson = GetBinaryTree( PreOrder, pbegin+1, (lr+1-tbegin)+pbegin+1, PostOrder, tbegin, lr+1);
Root->rson = GetBinaryTree( PreOrder, (lr+1-tbegin)+pbegin+1, pend, PostOrder, lr+1, tend-1);
return Root;
}
/** 中序遍历 */
void Tree_Print( BinaryTree *Root)
{
if( Root==NULL) return;
printf( "key: %d\n", Root->key); //visit tree
if( Root->lson !=NULL) printf( "lson: %d\n", Root->lson->key);
if( Root->rson !=NULL) printf( "rson: %d\n", Root->rson->key);
printf( "\n\n");
Tree_Print( Root->lson);
Tree_Print( Root->rson);
}