php 查找树,4.3 数据结构(PHP实现) -- 二分搜索树的遍历(非递归实现)

基于4.2章节做的延伸,采用非递归的方式来实现前序遍历、中序遍历和后序遍历

1. 二分搜索树(本章节都基于该二分搜索树做举例)

bVcIeAU

2. 代码 (在traversalByStack方法中实现了非递归的遍历二叉树)/**

* content: 二叉树的二分搜索树的实现

* auth: yujiaming

* create: 2020-10-25 */

namespace TreeBundle;

use ArrayBundle\BaseArray;

use StackBundle\BaseStack;

class BaseBinaryTree

{

public function __construct()

{

$this->rootNode = null;

$this->size = 0;

}

/**

* 二叉树的根节点

* @var Node

*/

protected $rootNode;

/**

* 获取根节点

* @return Node

*/

public function getRootNode(): Node

{

return $this->rootNode;

}

/**

* 二叉树的个数

* @var int

*/

protected $size;

/**

* 获取二叉树的元素个数

* @return int

*/

public function getSize(): int

{

return $this->size;

}

/**

* 判断是否为空

* @return bool

*/

public function isEmpty(): bool

{

return $this->size == 0;

}

/**

* 插入结点

* @param mixed $value

* @return void

*/

public function add($value): void

{

// 如果不存在根节点就插入根节点

if (is_null($this->rootNode)) {

$this->rootNode = new Node($value);

$this->size;

return;

} else {

$this->addChild($value, $this->rootNode);

return;

}

}

/**

* 插入儿子结点

* @param mixed $value

* @param Node|null $parentNode

*/

private function addChild($value, ?Node $parentNode): void

{

if (bccomp($parentNode->getValue(), $value) > 0) {

// 左儿子

if (is_null($parentNode->getLeftChild())) {

$parentNode->setLeftChild(new Node($value));

$this->size++;

return;

} else {

$this->addChild($value, $parentNode->getLeftChild());

}

} elseif (bccomp($parentNode->getValue(), $value) < 0) {

// 右儿子

if (is_null($parentNode->getRightChild())) {

$parentNode->setRightChild(new Node($value));

$this->size++;

return;

} else {

$this->addChild($value, $parentNode->getRightChild());

}

} else {

return;

}

}

const TRAVERSAL_PREORDER = 1; // 先序遍历

const TRAVERSAL_INORDER = 2; // 中序遍历

const TRAVERSAL_POSTORDER = 3; // 后序遍历

/**

* 遍历二叉树

* @param int $traversalType

* @return string

*/

public function traversal(int $traversalType = 1): string

{

$result = '';

switch ($traversalType) {

case self::TRAVERSAL_PREORDER:

$this->preorderTraversal($result, $this->rootNode);

break;

case self::TRAVERSAL_INORDER:

$this->inorderTraversal($result, $this->rootNode);

break;

case self::TRAVERSAL_POSTORDER:

$this->postorderTraversal($result, $this->rootNode);

break;

default:

break;

}

return $result;

}

/**

* 先序遍历

* @param string $result 结果值

* @param Node|null $node 结点

* @return void

*/

protected function preorderTraversal(string &$result, ?Node $node): void

{

if (is_null($node)) return;

// 拼接

if ($result != '') $result .= ' -> ';

// 先遍历当前节点

$result .= $node->getValue();

// 再遍历左儿子

$this->preorderTraversal($result, $node->getLeftChild());

// 在遍历右儿子

$this->preorderTraversal($result, $node->getRightChild());

return;

}

/**

* 中序遍历

* @param string $result 结果值

* @param Node|null $node 结点

* @return void

*/

public function inorderTraversal(string &$result, ?Node $node): void

{

if (is_null($node)) return;

// 先遍历左儿子

$this->inorderTraversal($result, $node->getLeftChild());

// 拼接

if ($result != '') $result .= ' -> ';

// 再获取当前结点

$result .= $node->getValue();

// 在遍历右儿子

$this->inorderTraversal($result, $node->getRightChild());

return;

}

/**

* 后序遍历

* @param string $result 结果值

* @param Node|null $node 结点

* @return void

*/

public function postorderTraversal(string &$result, ?Node $node): void

{

if (is_null($node)) return;

// 先遍历左儿子

$this->postorderTraversal($result, $node->getLeftChild());

// 在遍历右儿子

$this->postorderTraversal($result, $node->getRightChild());

// 拼接

if ($result != '') $result .= ' -> ';

// 再获取当前结点

$result .= $node->getValue();

return;

}

/**

* 非递归实现遍历

* @param int $traversalType

* @return string

*/

public function traversalByStack(int $traversalType = 1): string

{

$stack = new BaseStack(new BaseArray());

$result = '';

switch ($traversalType) {

// 前序遍历

case self::TRAVERSAL_PREORDER:

// 将根节点插入栈底

$stack->push($this->rootNode);

// 循环

while (!$stack->isEmpty()) {

// 弹出栈顶的结点

// 之后的循环会先弹出左儿子然后再弹出右儿子,中结点的值以及在上一次循环中被输出掉了

$node = $stack->pop();

// 将栈顶的元素拼接到结果集里

if ($result != '') {

$result .= ' -> ';

}

$result .= $node->getValue();

// 将弹出栈顶的右儿子插入栈中

if (!is_null($node->getRightChild())) {

$stack->push($node->getRightChild());

}

// 将弹出栈顶的左儿子插入栈中

if (!is_null($node->getLeftChild())) {

$stack->push($node->getLeftChild());

}

}

break;

// 中序遍历

case self::TRAVERSAL_INORDER:

// 将根节点赋值给node变量

$node = $this->rootNode;

// 循环

// !$stack->isEmpty() >> 用于判断栈里的元素是否都弹出

// !is_null($node) >> 用于判断是否与遍历完了左儿子结点

while (!$stack->isEmpty() || !is_null($node)) {

// 循环该结点,将该结点的所有左儿子一个一个插入栈中

while (!is_null($node)) {

$stack->push($node);

// echo '插入结点:'. $node->getValue(). PHP_EOL;

$node = $node->getLeftChild();

}

// 弹出栈顶的第一个元素,左儿子或者是根节点

$popNode = $stack->pop();

if ($result != '') {

$result .= ' -> ';

}

// 输出结果

$result .= $popNode->getValue();

// 将右儿子赋值给node结点变量中

$node = $popNode->getRightChild();

}

return $result;

break;

// 后序遍历

case self::TRAVERSAL_POSTORDER:

// 创建一个临时栈

$stackTmp = new BaseStack(new BaseArray());

// 将根节点放入栈底

$stackTmp->push($this->rootNode);

// 循环临时栈

while (!$stackTmp->isEmpty()) {

// 从临时栈顶弹出一个元素

$node = $stackTmp->pop();

// 将该元素的左儿子放入临时栈中

if (!is_null($node->getLeftChild())) {

$stackTmp->push($node->getLeftChild());

}

// 将该元素的右儿子放入临时栈中

if (!is_null($node->getRightChild())) {

$stackTmp->push($node->getRightChild());

}

// 将该结点放入需要遍历数据的栈中

$stack->push($node);

}

while (!$stack->isEmpty()) {

if ($result != '') {

$result .= ' -> ';

}

$result .= $stack->pop()->getValue();

}

break;

default:

break;

}

return $result;

}

}

3. 前序遍历代码块// 将根节点插入栈底

$stack->push($this->rootNode);

// 循环

while (!$stack->isEmpty()) {

// 弹出栈顶的结点

// 之后的循环会先弹出左儿子然后再弹出右儿子,中结点的值以及在上一次循环中被输出掉了

$node = $stack->pop();

// 输出栈顶的元素

echo $node->getValue();

// 将弹出栈顶的右儿子插入栈中

if (!is_null($node->getRightChild())) {

$stack->push($node->getRightChild());

}

// 将弹出栈顶的左儿子插入栈中

if (!is_null($node->getLeftChild())) {

$stack->push($node->getLeftChild());

}

}代码流程解析

bVcIeAD

4. 中序遍历代码块// 将根节点赋值给node变量

$node = $this->rootNode;

// 循环

// !$stack->isEmpty() >> 用于判断栈里的元素是否都弹出

// !is_null($node) >> 用于判断是否与遍历完了左儿子结点

while (!$stack->isEmpty() || !is_null($node)) {

// 循环该结点,将该结点的所有左儿子一个一个插入栈中

while (!is_null($node)) {

$stack->push($node);

$node = $node->getLeftChild();

}

// 弹出栈顶的第一个元素,左儿子或者是根节点

$popNode = $stack->pop();

// 输出结果

echo $popNode->getValue();

// 将右儿子赋值给node结点变量中

$node = $popNode->getRightChild();

}代码流程解析

bVcIfFl

5. 后序遍历代码块// 创建一个临时栈

$stackTmp = new BaseStack(new BaseArray());

// 将根节点放入栈底

$stackTmp->push($this->rootNode);

// 循环临时栈

while (!$stackTmp->isEmpty()) {

// 从临时栈顶弹出一个元素

$node = $stackTmp->pop();

// 将该元素的左儿子放入临时栈中

if (!is_null($node->getLeftChild())) {

$stackTmp->push($node->getLeftChild());

}

// 将该元素的右儿子放入临时栈中

if (!is_null($node->getRightChild())) {

$stackTmp->push($node->getRightChild());

}

// 将该结点放入需要遍历数据的栈中

$stack->push($node);

}

while (!$stack->isEmpty()) {

echo $stack->pop()->getValue();

}代码流程解析

bVcIibF

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值