通俗理解二叉树重建

 

今天分享一个关于重建二叉树的算法。

 

首先简单介绍下二叉树的概念,树是计算机中的一种数据结构,二叉树又是一种特殊的树,每个节点至多只有两颗子树的树被称为二叉树。

 

二叉树的子树被称作左右子树,在节点左侧的被称作左子树,在节点右侧的被称作右子树,二叉树主要有三种遍历方式,分别是前序(也称先序)、中序、后序遍历,前序是指从根节点开始遍历,再到左子树,再到右子树,即:根 -> 左 -> 右,中序和后序遍历顺序分别为: 左 -> 根 -> 右,左 -> 右 -> 根。下面的图分别展示了二叉树的三种不同遍历方式。

 

 

回到今天要讨论的问题,给出一个二叉树的前序遍历和中序遍历列表,而且列表中的数字都不是重复的,现要求根据这两个遍历列表推导出该二叉树,前序遍历列表为 [1,2,4,7,3,5,6,8],中序遍历列表为 [4,7,2,1,5,3,8,6]。

 

开始分析,根据二叉树的遍历规则,我们第一步可以确定前序遍历列表的第一个元素(数字 1)是该二叉树的根节点,因为前序遍历是从二叉树的根节点开始遍历的,然后又得知该二叉树的节点的值都是不相同的,因此我们在中序遍历列表中找到根节点(数字 1)所在的位置,再结合中序遍历的规则,根节点左侧的为左子树,右侧的为右子树,即把中序遍历列表以根节点(数字 1)为界限分割成两部分,左右两部分分别为二叉树的左右子树。

 

经过上面的分析我们可以得出在中序遍历列表中左右子树的各节点值,左子树为 :[4,7,2],右子树为:[5,3,8,6],也就是说左子树有三个元素,右子树有四个元素,所以同样的,在前序遍历列表中根节点(数字 1)后面的三个数字是其左子树,再后面的就是其右子树了,分别为:[2,4,7] 和 [3,5,6,8]。

 

到现在我们其实已经知道了该二叉树左右子树的前序和中序遍历列表了,我们再以同样的方法来构造其左右子树,既然是做相同的事情,我们自然可以用递归来处理。

 

 

如果还没有完全理解的话,我们可以再尝试用上面的方法构造出下一个左右子树,上面得到的左子树的前序遍历列表为 [2,4,7],中序遍历列表为 [4,7,2],右子树的前序遍历列表为 [3,5,6,8],中序遍历列表为 [5,3,8,6],现在我们把每一个左右子树单独看成一个二叉树,同样从确定根节点开始构造其下面的左右子树,比如这个左子树前序遍历的第一个数字 2 为根节点,再看其中序遍历列表中发现只有左边有数字,右边没有数字,说明其没有右子树,只有左子树,而且数字 4 和 7 为其左子树的值,再结合前序遍历列表可推出数字 2 的左节点值为 4 。

 

说得好像越来越复杂了,其实并不难,一句话总结,把前序遍历列表中第一个数字作为根节点,再结合中序遍历列表确定其左右子树,然后再把上面得到的左右子树再采用相同的方法来构造其左右子树(这里其实就是递归了)。下图是重建后的二叉树。 

 

 

最后附上一份php实现的重建二叉树的完整代码。

 

//源码来自于公众号:谭某人

<?php

// 定义二叉树结构
function binaryTree($root){      

    $res = new stdclass();
    $res->root = $root;
    $res->left = NULL;
    $res->right = NULL;    
    return $res;
}

function main($pre, $vin){

    return reBuildBinaryTree($pre, $vin, 0, count($pre)-1, 0, count($vin)-1);
}

function reBuildBinaryTree($pre, $inorder, $pstart, $pend, $istart, $iend) {

    if ($pstart > $pend||$istart>$iend) return;
    $root = $pre[$pstart];

    for($find = $istart; $find<=$iend;$find++){
         if($root === $inorder[$find]){
            break;
         }
    }

    $len = $find-$istart;
    $res = binaryTree($inorder[$find]);    
    $res->left = reBuildBinaryTree($pre, $inorder, $pstart+1, $pstart+$len,$istart,$find-1);
    $res->right = reBuildBinaryTree($pre, $inorder ,$pstart+$len+1, $pend,$find+1, count($inorder)-1);
    return $res;
}


$pre = [1,2,4,7,3,5,6,8];
$vin = [4,7,2,1,5,3,8,6];

print_r(main($pre,$vin));

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值