回來更新一波,最近刷《劍指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