本文實例講述了php無序樹實現方法。分享給大家供大家參考。具體如下:
運行效果如下圖所示:

php代碼如下:
<?php
/*
用php寫的無序樹
*/
class unorderedTree{
// 節點id計數器
protected $nodeId=0;
// 樹的深度
protected $depth=0;
// 樹的節點數,
protected $nodesCount=0;
// 樹的度 @todo: 使其發揮作用
public $degree=" to be implent";
// 根節點id
// 由於樹有多種從根節點開始操作,不想每次遍歷樹到頂找root,用一個變量始終指向根節點
protected $rootid=null;
// 子節點集合, k-v 為 nodeid=>(stdclass)node
// 一些樹的實現常常是采用節點和樹同一class,這裡節點是使用 stdclass{ data, parent, id , childrenIds} ,因我認為節點和樹應為兩種對象,且stdclass要輕於樹的class
// 節點格式說明: $this->nodes[nodeId] = new stdclass{ id ,parentId, childrenIds, data }
// id: 節點id
// parentId: 節點父節點id
// childrenIds: 子節點的id 不想每次遍歷樹確定層次關系
// 注意: 節點中, #只保存其自身數據和其子節點id的集合#, 子節點的數據通過從樹 $tree->nodes[ $node->childrenIds[a_child_id] ] 訪問
// data: 節點中包含的數據,如節點名稱等屬性數據
protected $nodes=array();
// 用戶自定義訪問節點
protected $userVisitFunction=null;
/* 分組: 類的基本函數 */
// @todo: 構造樹
public function __construct(){
}
// @todo: 銷毀樹
public function __destruct(){
unset($this->nodes) ;
}
//------------ 獲取數據類函數---------------
// 獲取樹的深度,
public function getTreeDepth(){
return $this->depth;
}
// 獲取樹的節點數目
public function getCount(){
return $this->NodesCount;
}
// 獲取樹的度
public function getDegree(){
// @todo: 獲取樹的度(因為對度暫時沒什麼需要就不實現了 )
return $this->degree;
}
//獲取指定節點
public function getNode($nodeId){
if(isset($this->Nodes[$nodeId])){
return $this->Nodes[$nodeId];
}
else{
return false;
}
}
// 獲取最新id
public function getId(){
return $this->nodeId;
}
//獲取指定節點高度
public function getNodeHeight($nodeId){
if( array_key_exists($nodeId, $this->nodes) ){
// 此節點已在樹裡,高度至少為1,每找到一個父節點+1
$height=1;
// 記錄此樹中已經訪問過的節點, 用於防止節點構造時互相parent導致此函數死循環且及時結束查找
$visitedNodesIds=array();
// 記錄當前操作節點的id
$cid=$nodeId;
// 當前節點的父節點必須存在於此樹中
// 不用遞歸
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;
}
}
//獲取根節點
public function getRoot(){
return (!is_null($this->rootid) ) && $this->nodes[$this->rootid];
}
//獲取指定節點和其所有子節點構成的數組
//這是用於獲取子樹的一個關鍵基礎操作
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: 獲取由指定節點和其所有子節點構建的子樹
public function getSubTree($nodeid){
}
//---------------- 數據更新 -----------------
public function setId($nodeId){
$this->nodeId=$nodeId;
return $this;
}
// 創建不重復的(樹中未被使用的) 新id
public function seekId(){
$this->nodeId++;
return $this->nodeId;
}
public function setVisitFunction($userFunction){
$this->userVisitFunction=$userFunction;
}
//插入子節點,默認為插在根節點下
public function insertNode($parent_id=null , $data=null){
//注意node不是class tree
$node = new stdclass;
$node->data = $data;
//樹的節點數增加
$this->nodeCount++;
// 分配節點id
$this->seekId();
$node->id =$this->getId();
//插入根節點
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) ){
// 插在此樹已有節點下
if(is_null($parent_id)){
$parent_id=$this->rootid;
}
$node->parentId = $parent_id;
$node->childrenIds = array();
//更新樹的最大深度
$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);
}
// --------------- 數據訪問 -----
//廣度優先遍歷節點的別名, 全名太長了
public function b($nodeId=null){
return $this->breadthTraversal($nodeId);
}
// 廣度優先遍歷節點
public function breadthTraversal($nodeId=null){
if(is_null($this->rootid)){
die("此樹為空樹,不可訪問");
}
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;
}
//深度優先的別名
public function d($nodeId=null){
return $this->depthTraversall($nodeId);
}
// 深度優先遍歷
// 和廣度優先的不同實現只在於array_merge的順序不同而已 ( php array 忒好用啊忒好用 )
public function depthTraversall($nodeId=null){
if(is_null($this->rootid)){
die("此樹為空樹,不可訪問");
}
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;
}
//訪問單個節點
public function visit($node){
if(is_null($this->userVisitFunction )){
return $node->id;
}
else{
return call_user_func($this->userVisitFunction,$node,$this);
}
}
}
?>
希望本文所述對大家的php程序設計有所幫助。