PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次)实例详解


Posted in PHP onApril 20, 2018

本文实例讲述了PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次)。分享给大家供大家参考,具体如下:

前言:

深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历。具体说明如下:

前序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后序遍历:左子树->右子树->根节点

广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。

例如对于一下这棵树:

PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次)实例详解

深度优先遍历:

前序遍历:10 8 7 9 12 11 13
中序遍历:7 8 9 10 11 12 13
后序遍历:7 9 8 11 13 12 10

广度优先遍历:

层次遍历:10 8 12 7 9 11 13

二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。

深度优先遍历:

1、前序遍历:

/**
* 前序遍历(递归方法)
*/
private function pre_order1($root)
{
    if (!is_null($root)) {
      //这里用到常量__FUNCTION__,获取当前函数名,好处是假如修改函数名的时候,里面的实现不用修改
      $function = __FUNCTION__;
      echo $root->key . " ";
      $this->$function($root->left);
      $this->$function($root->right);
    }
}
/**
* 前序遍历(非递归方法)
* 因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。
*/
private function pre_order2($root)
{
    //    $stack = new splstack();
    //    $stack->push($root);
    //    while(!$stack->isEmpty()){
    //      $node = $stack->pop();
    //      echo $node->key.' ';
    //      if(!is_null($node->right)){
    //        $stack->push($node->right);
    //      }
    //      if(!is_null($node->left)){
    //        $stack->push($node->left);
    //      }
    //    }
    if (is_null($root)) {
      return;
    }
    $stack = new splstack();
    $node = $root;
    while (!is_null($node) || !$stack->isEmpty()) {
      while (!is_null($node)) {
        //只要结点不为空就应该入栈保存,与其左右结点无关
        $stack->push($node);
        echo $node->key . ' ';
        $node = $node->left;
      }
      $node = $stack->pop();
      $node = $node->right;
    }
}
//前序遍历
public function PreOrder()
{
    // 所在对象中的tree属性保存了一个树的引用
    //   $this->pre_order1($this->tree->root);
    $this->pre_order2($this->tree->root);
}

说明:1、我将所有的遍历方法都封装在一个类 traverse 里面了。2、pre_order2方法中,在使用栈的过程中,我使用的是PHP标准库SPL提供的splstack,如果你们习惯使用数组的话,可以使用 array_push() array_pop() 模拟实现。

2、中序遍历:

/**
* 中序遍历(递归方法)
*/
private function mid_order1($root)
{
    if (!is_null($root)) {
      $function = __FUNCTION__;
      $this->$function($root->left);
      echo $root->key . " ";
      $this->$function($root->right);
    }
}
/**
* 中序遍历(非递归方法)
* 因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。
*/
private function mid_order2($root)
{
    if (is_null($root)) {
      return;
    }
    $stack = new splstack();
    $node = $root;
    while (!is_null($node) || !$stack->isEmpty()) {
      while (!is_null($node)) {
        $stack->push($node);
        $node = $node->left;
      }
      $node = $stack->pop();
      echo $node->key . ' ';
      $node = $node->right;
    }
}
//中序遍历
public function MidOrder()
{
    //    $this->mid_order1($this->tree->root);
    $this->mid_order2($this->tree->root);
}

3、后序遍历:

/**
* 后序遍历(递归方法)
*/
private function post_order1($root)
{
    if (!is_null($root)) {
      $function = __FUNCTION__;
      $this->$function($root->left);
      $this->$function($root->right);
      echo $root->key . " ";
    }
}
/**
* 后序遍历(非递归方法)
* 因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。
* 由于在访问了左子节点后怎么跳到右子节点是难点,这里使用一个标识lastVisited来标识上一次访问的结点
*/
private function post_order2($root)
{
    if (is_null($root)) {
      return;
    }
    $node = $root;
    $stack = new splstack();
    //保存上一次访问的结点引用
    $lastVisited = NULL;
    $stack->push($node);
    while(!$stack->isEmpty()){
      $node = $stack->top();//获取栈顶元素但不弹出
      if(($node->left == NULL && $node->right == NULL) || ($node->right == NULL && $lastVisited == $node->left) || ($lastVisited == $node->right)){
        echo $node->key.' ';
        $lastVisited = $node;
        $stack->pop();
      }else{
        if($node->right){
          $stack->push($node->right);
        }
        if($node->left){
          $stack->push($node->left);
        }
      }
    }
}
//后序遍历
public function PostOrder()
{
    //    $this->post_order1($this->tree->root);
    $this->post_order2($this->tree->root);
}

广度优先遍历:

1、层次遍历:

/**
* 层次遍历(递归方法)
* 由于是按层逐层遍历,因此传递树的层数
*/
private function level_order1($root,$level){
    if($root == NULL || $level < 1){
      return;
    }
    if($level == 1){
      echo $root->key.' ';
      return;
    }
    if(!is_null($root->left)){
      $this->level_order1($root->left,$level - 1);
    }
    if(!is_null($root->right)){
      $this->level_order1($root->right,$level - 1);
    }
}
/**
* 层次遍历(非递归方法)
* 每一层从左向右输出
元素需要储存有先进先出的特性,所以选用队列存储。
*/
private function level_order2($root){
    if(is_null($root)){
      return;
    }
    $node = $root;
    //利用队列实现
//    $queue = array();
//    array_push($queue,$node);
//
//    while(!is_null($node = array_shift($queue))){
//      echo $node->key.' ';
//      if(!is_null($node->left)){
//        array_push($queue,$node->left);
//      }
//      if(!is_null($node->right)){
//        array_push($queue,$node->right);
//      }
//    }
    $queue = new splqueue();
    $queue->enqueue($node);
    while(!$queue->isEmpty()){
      $node = $queue->dequeue();
      echo $node->key.' ';
      if (!is_null($node->left)) {
        $queue->enqueue($node->left);
      }
      if (!is_null($node->right)) {
        $queue->enqueue($node->right);
      }
    }
}
//层次遍历
public function LevelOrder(){
//    $level = $this->getdepth($this->tree->root);
//    for($i = 1;$i <= $level;$i ++){
//      $this->level_order1($this->tree->root,$i);
//    }
    $this->level_order2($this->tree->root);
}
//获取树的层数
private function getdepth($root){
    if(is_null($root)){
      return 0;
    }
    $left = getdepth($root -> left);
    $right = getdepth($root -> right);
    $depth = ($left > $right ? $left : $right) + 1;
    return $depth;
}

说明:level_order2方法中,在使用队列的过程中,我使用的是PHP标准库SPL提供的splqueue,如果你们习惯使用数组的话,可以使用 array_push() array_shift() 模拟实现。

使用:

现在我们来看看客户端代码:

class Client
{
  public static function Main()
  {
    try {
      //实现文件的自动加载
      function autoload($class)
      {
        include strtolower($class) . '.php';
      }
      spl_autoload_register('autoload');
      $arr = array(10, 8, 12, 7, 9, 11, 13);
      $tree = new Bst();
//      $tree = new Avl();
//      $tree = new Rbt();
      $tree->init($arr);
      $traverse = new traverse($tree);
      $traverse->PreOrder();
//      $traverse->MidOrder();
//      $traverse->PostOrder();
//      $traverse->LevelOrder();
    } catch (Exception $e) {
      echo $e->getMessage();
    }
  }
}
CLient::Main();

补充:

1. 在客户端中所使用的三个类 Bst、Avl、Rbt 大家可以参考前面一篇:《PHP实现绘制二叉树图形显示功能详解》

2. 为什么我推荐大家使用SPL标准库中提供的splstacksplqueue呢?这是我在某一篇文章中看到的:虽然我们可以使用传统的变量类型来描述数据结构,例如用数组来描述堆栈(Strack)? 然后使用对应的方式 pop 和 push(array_pop()array_push()),但你得时刻小心,因为毕竟它们不是专门用于描述数据结构的 ? 一次误操作就有可能破坏该堆栈。而 SPL 的 SplStack 对象则严格以堆栈的形式描述数据,并提供对应的方法。同时,这样的代码应该也能理解它在操作堆栈而非某个数组,从而能让你的同伴更好的理解相应的代码,并且它更快。原文地址:PHP SPL,遗落的宝石

3. 本文相关参考文章: 《C语言二叉树常见操作详解【前序,中序,后序,层次遍历及非递归查找,统计个数,比较,求深度】》、《Java实现二叉树的深度优先遍历和广度优先遍历算法示例》

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
十天学会php之第九天
Oct 09 PHP
对Session和Cookie的区分与解释
Mar 16 PHP
php获取mysql版本的几种方法小结
Mar 25 PHP
php缩放图片(根据宽高的等比例缩放)实例介绍
Jun 09 PHP
php页面防重复提交方法总结
Nov 25 PHP
Fatal error: session_start(): Failed to initialize storage module: files问题解决方法
May 04 PHP
非常好用的Zend Framework分页类
Jun 25 PHP
跟我学Laravel之路由
Oct 15 PHP
PHP中创建和验证哈希的简单方法实探
Jul 06 PHP
php生成无限栏目树
Mar 16 PHP
php进程(线程)通信基础之System V共享内存简单实例分析
Nov 09 PHP
PHP设计模式(七)组合模式Composite实例详解【结构型】
May 02 PHP
PHP SPL 被遗落的宝石【SPL应用浅析】
Apr 20 #PHP
Laravel 加载第三方类库的方法
Apr 20 #PHP
PHP迭代器和迭代的实现与使用方法分析
Apr 19 #PHP
详解php curl带有csrf-token验证模拟提交方法
Apr 18 #PHP
php-app开发接口加密详解
Apr 18 #PHP
PHPMAILER实现PHP发邮件功能
Apr 18 #PHP
PHP实现数据库的增删查改功能及完整代码
Apr 18 #PHP
You might like
PHP操作文件类的函数代码(文件和文件夹创建,复制,移动和删除)
2011/11/10 PHP
php调用C代码的实现方法
2014/03/11 PHP
浅谈laravel 5.6 安装 windows上使用composer的安装过程
2019/10/18 PHP
基于jQuery的Spin Button自定义文本框数值自增或自减
2010/07/17 Javascript
JavaScript定时器详解及实例
2013/08/01 Javascript
Knockout数组(observable)使用详解示例
2013/11/15 Javascript
前端必备神器 Snap.svg 弹动效果
2014/11/10 Javascript
js的window.showModalDialog及window.open用法实例分析
2015/01/29 Javascript
JavaScript脚本判断蜘蛛来源的方法
2015/09/22 Javascript
Javascript技术难点之apply,call与this之间的衔接
2015/12/04 Javascript
jQuery+CSS实现滑动的标签分栏切换效果
2015/12/17 Javascript
jquery实现树形菜单完整代码
2015/12/29 Javascript
高性能JavaScript循环语句和条件语句
2016/01/20 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
2016/05/19 Javascript
基于js实现的限制文本框只可以输入数字
2016/12/05 Javascript
javascript遍历json对象的key和任意js对象属性实例
2017/03/09 Javascript
JavaScript 日期时间选择器一些小结
2018/04/02 Javascript
Node.js的Koa实现JWT用户认证方法
2018/05/05 Javascript
node.js读取Excel数据(下载图片)的方法示例
2018/08/02 Javascript
解决vue.js this.$router.push无效的问题
2018/09/03 Javascript
详解keep-alive + vuex 让缓存的页面灵活起来
2019/04/19 Javascript
vue+egg+jwt实现登录验证的示例代码
2019/05/18 Javascript
Python模仿POST提交HTTP数据及使用Cookie值的方法
2014/11/10 Python
python爬取亚马逊书籍信息代码分享
2017/12/09 Python
NumPy 数学函数及代数运算的实现代码
2018/07/18 Python
python直接获取API传递回来的参数方法
2018/12/17 Python
Python 仅获取响应头, 不获取实体的实例
2019/08/21 Python
C语言怎样定义和声明全局变量和函数最好
2013/11/26 面试题
如果NULL定义成#define NULL((char *)0)难道不就可以向函数传入不加转换的NULL了吗
2012/02/15 面试题
计算机本科生自荐信
2013/10/15 职场文书
应届毕业生自我评价分享
2013/12/15 职场文书
三八妇女节标语
2014/10/09 职场文书
道德与公民自我评价
2015/03/09 职场文书
开学典礼致辞
2015/07/29 职场文书
2016父亲节感恩话语
2015/12/09 职场文书
python的html标准库
2022/04/29 Python