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

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

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

4.3 数据结构(PHP实现) -- 二分搜索树的遍历(非递归实现)_第1张图片

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

/**
 * content: 二叉树的二分搜索树的实现
 * auth: yujiaming
 * create: 2020-10-25 */
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());
    }
}
  • 代码流程解析

    4.3 数据结构(PHP实现) -- 二分搜索树的遍历(非递归实现)_第2张图片

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();
 } 
  • 代码流程解析

4.3 数据结构(PHP实现) -- 二分搜索树的遍历(非递归实现)_第3张图片

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();
}
  • 代码流程解析

4.3 数据结构(PHP实现) -- 二分搜索树的遍历(非递归实现)_第4张图片

你可能感兴趣的