php無(wú)序樹(shù)實(shí)現(xiàn)方法
本文實(shí)例講述了php無(wú)序樹(shù)實(shí)現(xiàn)方法。分享給大家供大家參考。具體如下:
運(yùn)行效果如下圖所示:

php代碼如下:
<?php
/*
用php寫(xiě)的無(wú)序樹(shù)
*/
class unorderedTree{
// 節(jié)點(diǎn)id計(jì)數(shù)器
protected $nodeId=0;
// 樹(shù)的深度
protected $depth=0;
// 樹(shù)的節(jié)點(diǎn)數(shù),
protected $nodesCount=0;
// 樹(shù)的度 @todo: 使其發(fā)揮作用
public $degree=" to be implent";
// 根節(jié)點(diǎn)id
// 由于樹(shù)有多種從根節(jié)點(diǎn)開(kāi)始操作,不想每次遍歷樹(shù)到頂找root,用一個(gè)變量始終指向根節(jié)點(diǎn)
protected $rootid=null;
// 子節(jié)點(diǎn)集合, k-v 為 nodeid=>(stdclass)node
// 一些樹(shù)的實(shí)現(xiàn)常常是采用節(jié)點(diǎn)和樹(shù)同一class,這里節(jié)點(diǎn)是使用 stdclass{ data, parent, id , childrenIds} ,因我認(rèn)為節(jié)點(diǎn)和樹(shù)應(yīng)為兩種對(duì)象,且stdclass要輕于樹(shù)的class
// 節(jié)點(diǎn)格式說(shuō)明: $this->nodes[nodeId] = new stdclass{ id ,parentId, childrenIds, data }
// id: 節(jié)點(diǎn)id
// parentId: 節(jié)點(diǎn)父節(jié)點(diǎn)id
// childrenIds: 子節(jié)點(diǎn)的id 不想每次遍歷樹(shù)確定層次關(guān)系
// 注意: 節(jié)點(diǎn)中, #只保存其自身數(shù)據(jù)和其子節(jié)點(diǎn)id的集合#, 子節(jié)點(diǎn)的數(shù)據(jù)通過(guò)從樹(shù) $tree->nodes[ $node->childrenIds[a_child_id] ] 訪(fǎng)問(wèn)
// data: 節(jié)點(diǎn)中包含的數(shù)據(jù),如節(jié)點(diǎn)名稱(chēng)等屬性數(shù)據(jù)
protected $nodes=array();
// 用戶(hù)自定義訪(fǎng)問(wèn)節(jié)點(diǎn)
protected $userVisitFunction=null;
/* 分組: 類(lèi)的基本函數(shù) */
// @todo: 構(gòu)造樹(shù)
public function __construct(){
}
// @todo: 銷(xiāo)毀樹(shù)
public function __destruct(){
unset($this->nodes) ;
}
//------------ 獲取數(shù)據(jù)類(lèi)函數(shù)---------------
// 獲取樹(shù)的深度,
public function getTreeDepth(){
return $this->depth;
}
// 獲取樹(shù)的節(jié)點(diǎn)數(shù)目
public function getCount(){
return $this->NodesCount;
}
// 獲取樹(shù)的度
public function getDegree(){
// @todo: 獲取樹(shù)的度(因?yàn)閷?duì)度暫時(shí)沒(méi)什么需要就不實(shí)現(xiàn)了 )
return $this->degree;
}
//獲取指定節(jié)點(diǎn)
public function getNode($nodeId){
if(isset($this->Nodes[$nodeId])){
return $this->Nodes[$nodeId];
}
else{
return false;
}
}
// 獲取最新id
public function getId(){
return $this->nodeId;
}
//獲取指定節(jié)點(diǎn)高度
public function getNodeHeight($nodeId){
if( array_key_exists($nodeId, $this->nodes) ){
// 此節(jié)點(diǎn)已在樹(shù)里,高度至少為1,每找到一個(gè)父節(jié)點(diǎn)+1
$height=1;
// 記錄此樹(shù)中已經(jīng)訪(fǎng)問(wèn)過(guò)的節(jié)點(diǎn), 用于防止節(jié)點(diǎn)構(gòu)造時(shí)互相parent導(dǎo)致此函數(shù)死循環(huán)且及時(shí)結(jié)束查找
$visitedNodesIds=array();
// 記錄當(dāng)前操作節(jié)點(diǎn)的id
$cid=$nodeId;
// 當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)必須存在于此樹(shù)中
// 不用遞歸
while( isset($cid) ) {
if( !in_array($cid,$visitedNodesIds ) ){
if( $this->rootid===$cid){ //到頂,返回
return $height;
}
$visitedNodesIds[]=$cid;
$cid= $this->nodes[ $cid ]->parentId;
$height++;
}
else{
return false;
}
}
return false;
}
else{
return false;
}
}
//獲取根節(jié)點(diǎn)
public function getRoot(){
return (!is_null($this->rootid) ) && $this->nodes[$this->rootid];
}
//獲取指定節(jié)點(diǎn)和其所有子節(jié)點(diǎn)構(gòu)成的數(shù)組
//這是用于獲取子樹(shù)的一個(gè)關(guān)鍵基礎(chǔ)操作
public function getSubNodes($nodeId){
if(isset($this->nodes[$nodeId])){
$result=array();
$toVisitNodeIds=array();
$toVisitedNodeIds[]=$nodeId;
$result[]=$this->nodes[$nodeId]->id;
array_shift($toVisitedNodeIds);
$toVisitedNodeIds=array_merge( $toVisitedNodeIds, $this->nodes[$nodeId]->childrenIds);
while(!empty($toVisitedNodeIds)){
$toVisitNodeId=array_shift($toVisitedNodeIds);
$result[]=$this->nodes[$toVisitNodeId]->id;
$toVisitedNodeIds=array_merge( $toVisitedNodeIds, $this->nodes[$toVisitNodeId]->childrenIds);
}
return $result ;
}
else{
return false;
}
}
//@todo: 獲取由指定節(jié)點(diǎn)和其所有子節(jié)點(diǎn)構(gòu)建的子樹(shù)
public function getSubTree($nodeid){
}
//---------------- 數(shù)據(jù)更新 -----------------
public function setId($nodeId){
$this->nodeId=$nodeId;
return $this;
}
// 創(chuàng)建不重復(fù)的(樹(shù)中未被使用的) 新id
public function seekId(){
$this->nodeId++;
return $this->nodeId;
}
public function setVisitFunction($userFunction){
$this->userVisitFunction=$userFunction;
}
//插入子節(jié)點(diǎn),默認(rèn)為插在根節(jié)點(diǎn)下
public function insertNode($parent_id=null , $data=null){
//注意node不是class tree
$node = new stdclass;
$node->data = $data;
//樹(shù)的節(jié)點(diǎn)數(shù)增加
$this->nodeCount++;
// 分配節(jié)點(diǎn)id
$this->seekId();
$node->id =$this->getId();
//插入根節(jié)點(diǎn)
if( (is_null($parent_id)) && is_null($this->rootid)){
$node->parentId = null;
$node->childrenIds = array();
$this->depth=1;
$this->rootid=$node->id;
$this->nodes [$node->id]=$node;
return $this;
}
elseif( isset($this->nodes[$parent_id]) || is_null($parent_id) ){
// 插在此樹(shù)已有節(jié)點(diǎn)下
if(is_null($parent_id)){
$parent_id=$this->rootid;
}
$node->parentId = $parent_id;
$node->childrenIds = array();
//更新樹(shù)的最大深度
$depth=$this->getNodeHeight($parent_id);
$this->depth=max($depth+1, $this->depth);
$this->nodes[$parent_id]->childrenIds []= $node->id;
$this->nodes [$node->id]=$node;
return $this;
}
else{
return $this;
}
}
//insert node 的別名
public function append($parent_id=null , $data=null){
return $this->insertNode($parent_id,$data);
}
// --------------- 數(shù)據(jù)訪(fǎng)問(wèn) -----
//廣度優(yōu)先遍歷節(jié)點(diǎn)的別名, 全名太長(zhǎng)了
public function b($nodeId=null){
return $this->breadthTraversal($nodeId);
}
// 廣度優(yōu)先遍歷節(jié)點(diǎn)
public function breadthTraversal($nodeId=null){
if(is_null($this->rootid)){
die("此樹(shù)為空樹(shù),不可訪(fǎng)問(wèn)");
}
else{
//全部遍歷
if(is_null($nodeId) || ( $this->rootid===$nodeId) ){
$nodeId=$this->rootid;
}
$toVisitNodeIds=array();
$toVisitedNodeIds[]=$nodeId;
$this->visit( $this->nodes[$nodeId]);
array_shift($toVisitedNodeIds);
$toVisitedNodeIds=array_merge( $toVisitedNodeIds, $this->nodes[$nodeId]->childrenIds);
while(!empty($toVisitedNodeIds)){
$toVisitNodeId=array_shift($toVisitedNodeIds);
$this->visit( $this->nodes[$toVisitNodeId]);
$toVisitedNodeIds=array_merge( $toVisitedNodeIds, $this->nodes[$toVisitNodeId]->childrenIds);
}
}
return $this;
}
//深度優(yōu)先的別名
public function d($nodeId=null){
return $this->depthTraversall($nodeId);
}
// 深度優(yōu)先遍歷
// 和廣度優(yōu)先的不同實(shí)現(xiàn)只在于array_merge的順序不同而已 ( php array 忒好用啊忒好用 )
public function depthTraversall($nodeId=null){
if(is_null($this->rootid)){
die("此樹(shù)為空樹(shù),不可訪(fǎng)問(wèn)");
}
else{
//全部遍歷
if(is_null($nodeId)){
$nodeId=$this->rootid;
}
$toVisitNodeIds=array();
$toVisitedNodeIds[]=$nodeId;
$this->visit( $this->nodes[$nodeId]);
array_shift($toVisitedNodeIds);
$toVisitedNodeIds=array_merge( $this->nodes[$nodeId]->childrenIds, $toVisitedNodeIds );
while(!empty($toVisitedNodeIds)){
$toVisitNodeId=array_shift($toVisitedNodeIds);
$this->visit( $this->nodes[$toVisitNodeId]);
$toVisitedNodeIds=array_merge( $this->nodes[$toVisitNodeId]->childrenIds, $toVisitedNodeIds );
}
}
return $this;
}
//訪(fǎng)問(wèn)單個(gè)節(jié)點(diǎn)
public function visit($node){
if(is_null($this->userVisitFunction )){
return $node->id;
}
else{
return call_user_func($this->userVisitFunction,$node,$this);
}
}
}
?>
希望本文所述對(duì)大家的php程序設(shè)計(jì)有所幫助。
相關(guān)文章
微信公眾平臺(tái)開(kāi)發(fā)關(guān)注及取消關(guān)注事件的方法
這篇文章主要介紹了微信公眾平臺(tái)開(kāi)發(fā)關(guān)注及取消關(guān)注事件的方法,較為詳細(xì)的分析了微信公眾平臺(tái)設(shè)置關(guān)注的技巧,并附帶了相關(guān)參數(shù)的說(shuō)明,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
php不使用copy()函數(shù)復(fù)制文件的方法
這篇文章主要介紹了php不使用copy()函數(shù)復(fù)制文件的方法,涉及php讀寫(xiě)文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
PHP四種排序算法實(shí)現(xiàn)及效率分析【冒泡排序,插入排序,選擇排序和快速排序】
這篇文章主要介紹了PHP四種排序算法實(shí)現(xiàn)及效率分析,結(jié)合具體實(shí)例形式分析了php冒泡排序,插入排序,選擇排序和快速排序的具體定義、用法及算法復(fù)雜度分析,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2018-04-04
php下實(shí)現(xiàn)一個(gè)阿拉伯?dāng)?shù)字轉(zhuǎn)中文數(shù)字的函數(shù)
最近因需要,寫(xiě)了個(gè)“阿拉伯?dāng)?shù)字轉(zhuǎn)中文數(shù)字的函數(shù)”。搜索了精華區(qū)只見(jiàn)到一個(gè)類(lèi)似的。 感覺(jué)到我的算法不錯(cuò),所以貼出來(lái)共享一下2008-07-07
詳解PHP中時(shí)間處理類(lèi)Carbon常用方法的使用
Carbon是php的日期處理類(lèi)庫(kù),繼承了PHP的Datetime類(lèi)。本文為大家詳細(xì)介紹了Carbon中常用的一些方法的使用,感興趣的小伙伴可以了解一下2022-05-05
Function eregi is deprecated (解決方法)
本篇文章是對(duì)Function eregi() is deprecated錯(cuò)誤的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
php實(shí)現(xiàn)微信公眾號(hào)主動(dòng)推送消息
這篇文章主要介紹了php實(shí)現(xiàn)微信公眾號(hào)主動(dòng)推送消息的方法,PHP版微信公共平臺(tái)消息主動(dòng)推送,突破訂閱號(hào)一天只能發(fā)送一條信息限制,需要的朋友可以參考下2015-12-12

