數據結構-PHP 壓棧遍歷二分搜索樹

前面寫了一篇的文章,實現的方法是用的遞歸思想遍歷,這篇文章主要介紹一下如何使用 壓棧 的思想來遍歷二分搜索樹。php

1.棧

爲了更好的結合壓棧的思想,下面先來介紹一下  數據結構的知識:node

1.1 棧的特色

  • 棧是一種線性數據結構。
  • 棧只能從一端添加數據,也只能從同一端取出元素,每次刪除的元素都是最後入棧的元素。
  • 入棧的元素具備後進先出的特色,即 Last In First Out(LIFO)。
  • 棧頂處理方法一般有 入棧(push)出棧(pop)查看棧頂(peek)
  • 如果用 鏈表 數據結構實現的 ,在 棧頂 會有一個 棧頂指針
  •  這種數據結構的應用舉例,如:能夠實現 撤銷(undo)程序的調用(系統棧)

1.2 棧的圖示

1.3 鏈表的實現

這是封裝好的一個鏈表類,能實現鏈表的基本功能:git

<?php
/**
 * 鏈表的實現
 * Class LinkedList
 */
class LinkedList
{
    private $dummyHead;
    private $size;
    /**
     * 初始化鏈表 null->null
     * LinkedList constructor.
     */
    public function __construct() {
        $this->dummyHead = new Node(null, null);
        $this->size = 0;
    }
    /**
     * 獲取鏈表大小
     * @return int
     */
    public function getSize(): int {
        return $this->size;
    }
    /**
     * 判斷鏈表是否爲空
     * @return bool
     */
    public function isEmpty(): bool {
        return $this->size == 0;
    }
    /**
     * 在鏈表的第 index 位置添加元素
     * @param int $index
     * @param $e
     */
    public function add(int $index, $e): void {
        if ($index < 0 || $index > $this->size) {
            echo "索引範圍錯誤";
            exit;
        }
        $prve = $this->dummyHead;
        for ($i = 0; $i < $index; $i++) {
            $prve = $prve->next;
        }
        //將上插入位置的上一個位置的 next 節點指向插入節點,插入節點的 next 節點信息指向原上節點的 next 節點
        $prve->next = new Node($e, $prve->next);
        $this->size++;
    }
    /**
     * 向鏈表開頭添加元素
     * @param $e
     */
    public function addFirst($e): void {
        $this->add(0, $e);
    }
    /**
     * 向鏈表末尾添加元素
     * @param $e
     */
    public function addLast($e): void {
        $this->add($this->size, $e);
    }
    /**
     * 獲取鏈表第 index 位置元素
     * @param $index
     */
    public function get($index) {
        if ($index < 0 || $index > $this->size) {
            echo "索引範圍錯誤";
            exit;
        }
        $node = $this->dummyHead;
        for ($i = 0; $i < $index + 1; $i++) {
            $node = $node->next;
        }
        return $node->e;
    }
    /**
     * 獲取鏈表第一個元素
     * @return mixed
     */
    public function getFirst() {
        return $this->get(0);
    }
    /**
     * 獲取鏈表最後一個元素
     * @return mixed
     */
    public function getLast() {
        return $this->get($this->size - 1);
    }
    /**
     * 修改鏈表中第 index 位置元素值
     * @param $index
     * @param $e
     */
    public function update($index, $e) {
        if ($index < 0 || $index > $this->size) {
            echo "索引範圍錯誤";
            exit;
        }
        $node = $this->dummyHead;
        for ($i = 0; $i < $index + 1; $i++) {
            $node = $node->next;
        }
        $node->e = $e;
    }
    /**
     * 判斷鏈表中是否存在某個元素
     * @param $e
     * @return bool
     */
    public function contains($e): bool {
        for ($node = $this->dummyHead->next; $node != null; $node = $node->next) {
            if ($node->e == $e) {
                return true;
            }
        }
        return true;
    }
    /**
     * 刪除鏈表中第 index 位置元素
     * @param $index
     */
    public function remove($index) {
        if ($index < 0 || $index > $this->size) {
            echo "索引範圍錯誤";
            exit;
        }
        if ($this->size == 0) {
            echo "鏈表已是空";
            exit;
        }
        $prve = $this->dummyHead;
        for ($i = 0; $i < $index; $i++) {
            $prve = $prve->next;
        }
        $node = $prve->next;
        $prve->next = $node->next;
        $this->size--;
        return $node->e;
    }
    /**
     * 刪除鏈表頭元素
     */
    public function removeFirst() {
       return $this->remove(0);
    }
    /**
     * 刪除鏈表末尾元素
     */
    public function removeLast() {
       return $this->remove($this->size - 1);
    }
    /**
     * 鏈表元素轉化爲字符串顯示
     * @return string
     */
    public function toString(): string {
        $str = "";
        for ($node = $this->dummyHead->next; $node != null; $node = $node->next) {
            $str .= $node->e . "->";
        }
        return $str . "null";
    }
}
class Node
{
    public $e;//節點元素
    public $next; //下個節點信息
    /**
     * 構造函數 設置節點信息
     * Node constructor.
     * @param $e
     * @param $next
     */
    public function __construct($e, $next) {
        $this->e = $e;
        $this->next = $next;
    }
}

1.4 調用鏈表實現的棧

這是一個封裝好的 棧(Stack) ,經過實例化 鏈表類(LinkedList) 實現了入棧(push)和 出棧(pop),還有查看棧頂(peek):數組

<?php
require 'LinkedList.php';
class StackByLinkedList
{
    //鏈表類對象,用於存放棧元素
    protected $array = null;
    /**
     * 構造函數 定義棧的容量
     * ArrayStruct constructor.
     * @param int $capacity
     */
    public function __construct() {
        $this->array = new LinkedList();
    }
    /**
     * 獲取棧大小
     * @return int
     */
    public function getSize(): int {
        return $this->array->getSize();
    }
    /**
     * 判斷棧是否爲空
     * @return bool
     */
    public function isEmpty(): bool {
        return $this->array->isEmpty();
    }
    /**
     * 元素入棧
     */
    public function push($e): void {
        $this->array->addFirst($e);
    }
    /**
     * 出棧
     * @return mixed
     */
    public function pop() {
        return $this->array->removeFirst();
    }
    /**
     * 查看棧頂元素
     * @return mixed
     */
    public function peek() {
        return $this->array->getFirst();
    }
    /**
     * 將棧數組轉化爲字符串
     * @return string
     */
    public function toString(): string {
        return $this->array->toString();
    }
}

2.二分搜索樹壓棧思想實現前序遍歷

2.1 節點定義

2.3 PHP 代碼定義節點
class Node
{
    public $e;
    public $left = null;
    public $right = null;
    /**
     * 構造函數 初始化節點數據
     * Node constructor.
     * @param $e
     */
    public function __construct($e) {
        $this->e = $e;
    }
}

2.2 原理說明

這裏以前序遍歷爲例進行說明,利用  的特色,從跟節點開始,先把根節點入棧,而後出棧的時候須要判斷出棧元素是否爲空,若不爲空則須要先把 右兒子節點入棧,而後 左兒子 節點入棧,依此類推直到沒有兒子節點的時候就能夠繼續 出棧 下一個元素了,直到  元素爲空表示遍歷完畢,經過這種 壓棧 的思想能夠達到 遍歷二分搜索樹 的目的。數據結構

Tips:若不爲空的節點沒有兒子節點,這裏實際處理它的兒子節點也會入棧 null

2.3 實現原理圖示

2.4 二分搜索樹前序遍歷壓棧實現

下面展現的都是部分代碼,須要結合以前的《數據結構-PHP 實現二分搜索樹》,前序遍歷操做就是把全部節點都訪問一次,前序遍歷 是先訪問節點,再遍歷左兒子樹,而後再遍歷右兒子樹,要想達到這種效果,對於每一個節點都是先處理當前節點而後入棧右兒子,最後入棧左兒子,若出棧元素爲空,打印 null 以後繼續出棧:函數

Tips:若不爲空的節點沒有兒子節點,這裏實際處理它的兒子節點也會入棧 null
/**
     * 前序遍歷壓棧實現
     */
    public function preTraversalByStack() {
        $stack = new StackByLinkedList();
        //將根節點壓入棧
        $stack->push($this->root);
        //循環依次出棧
        $node = $stack->pop();
        do {
            if ($node != null) { //若出棧的當前節點不是空
                echo $node->e . "<br>"; //先打印當前節點信息
                //先入棧右兒子
                $stack->push($node->right);
                //而後入棧左兒子
                $stack->push($node->left);
            } else { //如果空
                echo "null<br>";
            }
            //繼續出棧
            $node = $stack->pop();
        } while (!$stack->isEmpty());
    }

下面是打印結果:ui

<?php
require 'BinarySearchTree.php';
$binarySearchTree = new BinarySearchTree();
$binarySearchTree->add(45);
$binarySearchTree->add(30);
$binarySearchTree->add(55);
$binarySearchTree->add(25);
$binarySearchTree->add(35);
$binarySearchTree->add(50);
$binarySearchTree->add(65);
$binarySearchTree->add(15);
$binarySearchTree->add(27);
$binarySearchTree->add(31);
$binarySearchTree->add(48);
$binarySearchTree->add(60);
$binarySearchTree->add(68);
//下面是預期想要的結果
/**
 *                     45
 *           /                   
 *          30                   55
 *        /                    /    
 *      25       35         50       65
 *     /       /          /       /   
 *   15   27  31         48       60     68
 *
 */
//調用前序遍歷的遞歸實現
$binarySearchTree->preTraversalByStack();
/**
打印輸出
45
30
25
15
null
null
27
null
null
35
31
null
null
null
 */
Tips:能夠看到打印輸出結果和預期一致,而且和以前遞歸實現的方式一致,對於  中序遍歷後續遍歷來講具體實現邏輯比  前序遍歷 要複雜一些。

代碼倉庫 :https://gitee.com/love-for-po...this

掃碼關注愛因詩賢spa

相關文章
相關標籤/搜索