利用PHP實現常用的資料結構之二叉樹(小白系列文章六)

entner發表於2019-02-16

回來更新一波,最近刷《劍指offer》,才又發現樹真是一個大頭,二叉樹的題目和變化運用好多啊~

/**
*    PHP二叉樹演算法
*    Created on 2017-5-6
*   Update  on 2017-8-10
*    Author     entner
*    Email     1185087164@qq.com
*/

引子

    很多人說二叉樹沒什麼卵用,我覺得是他的工資和公司讓他跨不過這個坎;還有很多人學了一些樹的知識,發現也用不上,我想說的是,讀一本書體現不了這本書的價值,要多讀幾本才會把你和別人的差別體現出來。
    二叉樹是嚴格定義的,有很好的對稱性和比較高的資料關聯度,對資料的儲存和計算,有很好的演示作用,比如完全二叉樹:

    當然,二叉樹更適合連結串列儲存,效率更高,總的來說,以二叉樹為基礎我們可以衍生出許多神奇的運用,舉幾個常用的場景為我說的話正言:

  • 編譯器表示式處理
  • 快速查詢與排序
  • 檔案壓縮
  • 檔案系統管理
  • 遊戲場景劃分、監測、渲染

     我承認,上面好多東西你並不會直接接觸,但是,我們關鍵是去學習一種思維和設計,退一萬步也可以增加一點見識:)
    

一、二叉樹抽象資料型別操作條目

關於書的操作太多了,我只列一些常用的(這些都是常考的知識點),有興趣的相信也有技能去淘到“好貨”

 -InitTree    構造空樹
 -PreTra    返回樹中某結點的值
 -InTra        給樹中某結點賦值為value
 -LevelTra     返回某非根結點的雙親,否則返回空
 -LeftChild  返回某非葉結點的最左孩子,否則返回空
 

二、默寫二叉樹常用資料結構

默寫會讓你記憶更深刻,同時也會鍛鍊抽象的邏輯思維,一邊看不懂,就多看幾遍,再查一查相關資料,應該問題不大,你甚至可以找張紙默寫一下。
/**     
*InitTree 初始化樹
*    
*
    Typedef int TElementType //構造一個資料型別
    Typedef Struct Tree{    //構造二叉樹抽象資料型別
        TElementType data;    //宣告一個陣列,先構建一個樹
        Struct Tree *leftChild;    //左孩子節點
        Struct Tree *rightChild;   //右孩子節點
    }Tree;
*/

/**
 * Value     獲取樹的結點(前序遍歷)
 * Return   返回獲取的結點值
     Status Value(Tree *T,int e){
        if(T == null){
            return error;
        }
        e = T->Value(T->leftChild);
        e = T->Value(T->rightChild);
        return e;
     }
 */

/**
 * Assign     給樹中某結點賦值為v(前序遍歷)
 * Return   返回賦值後的結點值
     Status Assign(Tree *T,int e, TElementType v){
        if(T == null){
            return error;
        }
        e = T->Assign(T->leftChild);
        e = T->Assign(T->rightChilg);
        e = v;
        return e;
     }
 */

三、二叉樹結構基本實現

/**
*    PHP二叉樹演算法
*    Created on 2017-8-7
*    Author     entner
*    Email     1185087164@qq.com
*/

/*
    假設我構造一顆如下的二叉樹
            A
        B       C
      #   D   #   # 
        #   #
*/

error_reporting(E_ALL ^ E_WARNING);



Class BinaryTree{

    
    public $lChild;
    public $rChild;
    public $value;

    /*    初始化構造空樹    */
    public function __construct($data = null){
        $this->value = $data;
    }



    /**
     * TODO:構建二叉樹
     * @param $root object 二叉樹
     */
    public function preOrderCreateBT(&$root){

        
        while(!is_null($elem = array_shift($this->value))){
            $root = ``;
            if($elem == null){    #判斷:當陣列為空時    
                return $root;
            }else if($elem == `#`){    #判斷:當陣列為無效單元時,該節點是虛節點(無孩子節點),退出當前遞迴,執行下一個遞迴
                $root->value = `#`;
                return ;
            }else{
                $root->value = $elem;
                $this->preOrderCreateBT($root->lChild);
                $this->preOrderCreateBT($root->rChild);
            }
        }
        
        
        return $root;
    }


    /**
     * TODO:先序遍歷二叉樹
     * @param $tree object 二叉樹
     * @param $temp array  二叉樹輸出節點儲存陣列
     */
    public function printBTPreOrder($tree,&$temp){
        if($tree != null){
            $temp[] = $tree->data;
            $this->printBTPreOrder($tree->lChild,$temp);
            $this->printBTPreOrder($tree->rChild,$temp);
        }else{
            return ;
        }
        return $temp;
    }

    /**
     * TODO:中序遍歷二叉樹
     * @param $tree object 二叉樹
     * @param $temp array  二叉樹輸出節點儲存陣列
     */
    public function printBTInOrder($tree,&$temp){
        if($tree != null){
            $this->printBTInOrder($tree->lChild,$temp);
            $temp[] = $tree->data;
            $this->printBTInOrder($tree->rChild,$temp);
        }else{
            return;
        }
        return $temp;
    }

    /**
     * TODO:後序遍歷二叉樹
     * @param $tree object 二叉樹
     * @param $temp array  二叉樹輸出節點儲存陣列
     */
    public function printBTPosOrder($tree,&$temp){
        if($tree != null){
            $this->printBTPosOrder($tree->lChild,$temp);
            $this->printBTPosOrder($tree->rChild,$temp);
            $temp[] = $tree->data;
            
        }else{
            return;
        }
        return $temp;
    }

    /**
     * TODO:層序遍歷二叉樹(需要將書中節點放入隊中)
     * 
     */
    function PrintFromTopToBottom(&$root)
{
    // write code here

    $queueVal = array();
       $queue = array();
    if($root == null){
        return $queueVal;    #注意當二叉樹為空樹時,應該返回空陣列
    }
   
    array_push($queue,$root);
    
    while($queue){
        $node = array_shift($queue);
        array_push($queueVal,$node->value);
        if($node->lChild != null){
            array_push($queue,$node->lChild);
        }
        if($node->rChild != null){
            array_push($queue,$node->rChild);
        }
         
    }
    return $queueVal;
}


    /**
     * TODO:樹的深度
     * 
     */
    public function treeDeepth(&$root){
        if($root == null){
            return 0;
        }
        
        if($root != null){
            
            $ld = $this->treeDeepth($root->lChild) + 1;
            $rd = $this->treeDeepth($root->rChild) + 1;    

        }
        $max = max($ld,$rd);

        return $max;
    }

}
$node = array(
        0=>`A`,
        1=>`B`,
        2=>`#`,
        3=>`D`,
        4=>`#`,
        5=>`#`,
        6=>`C`,
        7=>`#`,
        8=>`#`,
    );


$object = new BinaryTree($node);
$tree = $object->preOrderCreateBT();
echo "<pre>";
print_r($tree);
echo $object->treeDeepth($tree) . "<br>";
print_r($object->PrintFromTopToBottom($tree));

以下為效果圖:
分別是構造樹的結構、樹的深度、層序遍歷輸出
![圖片描述][3]

四、二叉排序樹

/**
*FindBitTree      二叉樹查詢
*@param T BItTree 代指二叉樹及其元素結點
*@param key int   樹中某結點
*@param f   point 指向該結點父結點
*@param p   point 指向該元素結點或空
*@param return bool true|false
   status SearchBST(BitTree T,int key,BitTree f = null,BitTree p){
        if(!T){
            p = f;
            return false;
        }
        if(key == T->data){
            p = T;//其實就是根結點
            return true;
        }else if(key <T->data){
            SearchBST(T->lChild,int key,T,p);
        }else if(key >T->data){
            SearchBST(T->rChild,int key,T,p);
        }
   }
*/

/**
InsertBitTree      二叉樹插入 
*【如果當前樹中沒有key元素,則插入,插入的結點一定是葉子結點】
*@param T BItTree 代指二叉樹及其元素結點
*@param key int   樹中某結點
*@param return bool true|false
   status InsertBST(BitTree T,int key){
        if(SearchBST(T,key,NULL,&p) == false){
            s->data = key;
            s->lChild = s->rChild = NULL;
            if(!p){
                T= s;
            }else if(key < p->data){
                p->lChild = s;
            }else{
                p->rChild = s;
            }
          return true;
        }
        return false;
   }
*/

五、樹應用實現-無限極分類(引用&遞迴)

$items = array(
    1 => array(`id` => 1, `pid` => 0, `name` => `江西省`),
    2 => array(`id` => 2, `pid` => 0, `name` => `黑龍江省`),
    3 => array(`id` => 3, `pid` => 1, `name` => `南昌市`),
    4 => array(`id` => 4, `pid` => 2, `name` => `哈爾濱市`),
    5 => array(`id` => 5, `pid` => 2, `name` => `雞西市`),
    6 => array(`id` => 6, `pid` => 4, `name` => `香坊區`),
    7 => array(`id` => 7, `pid` => 4, `name` => `南崗區`),
    8 => array(`id` => 8, `pid` => 6, `name` => `和興路`),
    9 => array(`id` => 9, `pid` => 7, `name` => `西大直街`),
    10 => array(`id` => 10, `pid` => 8, `name` => `東北林業大學`),
    11 => array(`id` => 11, `pid` => 9, `name` => `哈爾濱工業大學`),
    12 => array(`id` => 12, `pid` => 8, `name` => `哈爾濱師範大學`),
    13 => array(`id` => 13, `pid` => 1, `name` => `贛州市`),
    14 => array(`id` => 14, `pid` => 13, `name` => `贛縣`),
    15 => array(`id` => 15, `pid` => 13, `name` => `於都縣`),
    16 => array(`id` => 16, `pid` => 14, `name` => `茅店鎮`),
    17 => array(`id` => 17, `pid` => 14, `name` => `大田鄉`),
    18 => array(`id` => 18, `pid` => 16, `name` => `義源村`),
    19 => array(`id` => 19, `pid` => 16, `name` => `上壩村`),
);

/**
*TODO:通過引用方式實現無限極分類
*    
*/
function tree_Classify1($items){
    $tree=array();
    $packData=array();
    foreach ($items as  $data) {
        $packData[$data[`id`]] = $data;
    }
    foreach ($packData as $key =>$val){     
        if($val[`pid`]==0){//代表跟節點       
            $tree[]=& $packData[$key];
        }else{
            //找到其父類
            $packData[$val[`pid`]][`son`][]=& $packData[$key];
        }
    }
    return $tree;
}

/**
*TODO:通過遞迴方式實現無限極分類
*      
*/
function tree_Classify2($items,$child=`_child`,$root = 0){
    $tree=array();
    foreach($items as $key=> $val){

        if($val[`pid`]==0){
            //獲取當前$pid所有子類 
                unset($items[$key]);
                if(! empty($items)){
                    $child=make_tree1($items,$child,$val[`id`]);
                    if(!empty($child)){
                        $val[`_child`]=$child;
                    }                   
                }              
                $tree[]=$val; 
        }
    }   
    return $tree;
}

echo "<pre>";
print_r(make_tree1($items,$child=`_child`,$root=0));
``


  [1]: /img/bV7ljv
  [2]: /img/bV7ljz

相關文章