PHP二叉樹的一些操作練習

首先是創建一個樹節點類,這個類有兩個方法,compare()用於比較節點鍵值的大小,createNode()用於創建新節點。
// 樹節點類
class binaryTreeNode
{
	// 比較節點鍵值的大小
	function compare($oldkey, $newkey){
		return $newkey - $oldkey;
	}
	// 建立一個新節點
	function createNode($key, $left, $right){
		return array('k'=>$key, 'l'=>$left, 'r'=>$right);
	}
}

然後再創建一個二叉樹類。
// 二叉樹類
class binaryTree
{
	var $id = 1;   			// 內部UID,自動加1
 	var $nodeCount = 0; 	// 該樹節點數,每插入一個加1
 	var $root = null; 		// 樹根的引用
 	var $tree = array(); 	// 樹結構,中間存貯所有節點
  	var $_nodes = array(); 	// 存貯不同類型的節點,主要是爲了引用該類的方法,便於繼承,暫時沒有用到
   	var $_node = null; 		// 當前節點類的引用
  	// 構造函數,初始化
  	function binaryTree($nodetype = 'binaryTreeNode'){
  		if(!class_exists($nodetype))
          	$nodetype = 'binaryTreeNode';
       	$this->_nodes[$nodetype] = new $nodetype();
      	$this->_node =& $this->_nodes[$nodetype];
	}
  	// 設定節點類型,暫時用處不大,主要爲擴展
   	function setNodeType($nodetype = 'binaryTreeNode'){
      	if(!class_exists($nodetype))
          	$nodetype = 'binaryTreeNode';
		$this->_node =& $this->_nodes[$nodetype];
	}
 	// 插入一節點後修改所有相關節點的信息
	// $pnode 爲當前根節點,後兩個參數爲新節點的參數
 	// 即在樹中插入一個新節點後,找到此節點所插入的位置並修改其父節點的信息
    // 此函數本爲遞歸,但因爲是尾遞歸(rear-recursive),因此可以改成循環
   	function modifyTree(&$pnode, $key, $id){
    	$p =& $pnode;
      	while(true){
         	$b = $this->_node->compare($p['k'], $key);
            if($b<=0)
			{
           		if($p['l'] != 0)
				{
                	$p =& $this->tree[$p['l']];
               	}
				else
				{
                   	$p['l'] = $id;
                   	break;
               	}
           	}
			else
			{
               	if($p['r'] != 0)
				{
                	$p =& $this->tree[$p['r']];
             	}
				else
				{
                   	$p['r'] = $id;
                  	break;
            	}
			}
		}
	}
	// 重置樹信息
	function reset()
	{
     	$this->tree = array();
      	$this->root = null;
      	$this->id = 1;
     	$this->nodeCount = 0;
    	$this->_node = null;
	}
	// 插入一個鍵爲$key的節點,並自動爲此節點生成一個唯一ID
	function insertNode($key)
	{
      	$node = $this->_node->createNode($key, 0, 0);
     	$this->tree[$this->id] = $node;
      	if($this->root == null)
		{
           	$this->root =& $this->tree[1];
     	}
		else
		{
           	$this->modifyTree($this->root, $key, $this->id);
      	}
     	$this->id ++;
      	$this->nodeCount ++;
	}
    // 先根遍歷,打印樹結構,用的遞歸
	// 此函數可修改用於任何用途
	function preorder(&$root, $level, $r = 'r')
	{
     	$p =& $root;
       	if($r == 'l') 
			$s = str_repeat("\t", 1);
     	else 
			$s = str_repeat("\t", $level);
   		echo $s.$p['k']."";
        if($p['r'] != 0)
		{
         	$p1 =& $this->tree[$p['r']];
          	$this->preorder($p1, $level + 1, 'l');
       	}
		else
		{
           	$s = str_repeat("\t", 1);
       		echo $s.'null'."\n";
     	}
        if($p['l'] != 0)
		{
          	$p1 =& $this->tree[$p['l']];
            $this->preorder($p1, $level + 1);
    	}
		else
		{
         	$s = str_repeat("\t", $level + 1);
          	echo $s.'null'."\n";
     	}
	}
}


測試用代碼:

// 此函數主要是爲了兼容性
function make_seed() {
	list($usec, $sec) = explode(' ', microtime());
	return (float) $sec + ((float) $usec * 100000);
}
// 生成隨機序列鍵值
function generateRandamSequence($min = 1, $max = 100){
	srand(make_seed());
 	$n = $min;
  	$a = array();
   	while($n <= $max)
	{
      	$randval = rand($min, $max);
      	if($a[$randval - $min] == '')
		{
           	$a[$randval - $min] = $n;
           	$n++;
    	}
	}
   	reset($a);
  	ksort($a);
	reset($a);
  	return $a;
}
$min = 1;
$max = 100;
// 將隨機序列鍵值存入一數組內
$a = generateRandamSequence($min, $max);
print_r($a);   // 打印數組
$tree = new binaryTree; // 建立一棵樹
// 將節點按鍵值順序插入到樹中,同時調整樹的結構
for($i=0;$i<$max-$min+1;$i++)
	$tree->insertNode($a[$i]);
print_r($tree); // 打印樹對象的內容
echo serialize($tree); // 打印樹序列化之後的內容
echo "<hr>;\n<pre>;";
$t =& $tree->root; // 指定樹根,爲以下傳入引用參數用
$tree->preorder($t, 0); // 先根遍歷,打印樹型結構
// 打印完畢可發現,鍵值比根鍵值小的所有節點均在根的左邊,反之則在右邊,每個節點都是如此
// 但此樹不是平衡樹(AVL樹),因此查詢效率還是比較低,特別是如果是連成一直線,則效率達到最低,不能利用樹的對數特性了
echo "</pre>;"; // 打印完畢

完整版本:

<?php
// 樹節點類
class binaryTreeNode
{
	// 比較節點鍵值的大小
	function compare($oldkey, $newkey){
		return $newkey - $oldkey;
	}
	// 建立一個新節點
	function createNode($key, $left, $right){
		return array('k'=>$key, 'l'=>$left, 'r'=>$right);
	}
}
// 二叉樹類
class binaryTree
{
	var $id = 1;   			// 內部UID,自動加1
 	var $nodeCount = 0; 	// 該樹節點數,每插入一個加1
 	var $root = null; 		// 樹根的引用
 	var $tree = array(); 	// 樹結構,中間存貯所有節點
  	var $_nodes = array(); 	// 存貯不同類型的節點,主要是爲了引用該類的方法,便於繼承,暫時沒有用到
   	var $_node = null; 		// 當前節點類的引用
  	// 構造函數,初始化
  	function binaryTree($nodetype = 'binaryTreeNode'){
  		if(!class_exists($nodetype))
          	$nodetype = 'binaryTreeNode';
       	$this->_nodes[$nodetype] = new $nodetype();
      	$this->_node =& $this->_nodes[$nodetype];
	}
  	// 設定節點類型,暫時用處不大,主要爲擴展
   	function setNodeType($nodetype = 'binaryTreeNode'){
      	if(!class_exists($nodetype))
          	$nodetype = 'binaryTreeNode';
		$this->_node =& $this->_nodes[$nodetype];
	}
 	// 插入一節點後修改所有相關節點的信息
	// $pnode 爲當前根節點,後兩個參數爲新節點的參數
 	// 即在樹中插入一個新節點後,找到此節點所插入的位置並修改其父節點的信息
    // 此函數本爲遞歸,但因爲是尾遞歸(rear-recursive),因此可以改成循環
   	function modifyTree(&$pnode, $key, $id){
    	$p =& $pnode;
      	while(true){
         	$b = $this->_node->compare($p['k'], $key);
            if($b<=0)
			{
           		if($p['l'] != 0)
				{
                	$p =& $this->tree[$p['l']];
               	}
				else
				{
                   	$p['l'] = $id;
                   	break;
               	}
           	}
			else
			{
               	if($p['r'] != 0)
				{
                	$p =& $this->tree[$p['r']];
             	}
				else
				{
                   	$p['r'] = $id;
                  	break;
            	}
			}
		}
	}
	// 重置樹信息
	function reset()
	{
     	$this->tree = array();
      	$this->root = null;
      	$this->id = 1;
     	$this->nodeCount = 0;
    	$this->_node = null;
	}
	// 插入一個鍵爲$key的節點,並自動爲此節點生成一個唯一ID
	function insertNode($key)
	{
      	$node = $this->_node->createNode($key, 0, 0);
     	$this->tree[$this->id] = $node;
      	if($this->root == null)
		{
           	$this->root =& $this->tree[1];
     	}
		else
		{
           	$this->modifyTree($this->root, $key, $this->id);
      	}
     	$this->id ++;
      	$this->nodeCount ++;
	}
    // 先根遍歷,打印樹結構,用的遞歸
	// 此函數可修改用於任何用途
	function preorder(&$root, $level, $r = 'r')
	{
     	$p =& $root;
       	if($r == 'l') 
			$s = str_repeat("\t", 1);
     	else 
			$s = str_repeat("\t", $level);
   		echo $s.$p['k']."";
        if($p['r'] != 0)
		{
         	$p1 =& $this->tree[$p['r']];
          	$this->preorder($p1, $level + 1, 'l');
       	}
		else
		{
           	$s = str_repeat("\t", 1);
       		echo $s.'null'."\n";
     	}
        if($p['l'] != 0)
		{
          	$p1 =& $this->tree[$p['l']];
            $this->preorder($p1, $level + 1);
    	}
		else
		{
         	$s = str_repeat("\t", $level + 1);
          	echo $s.'null'."\n";
     	}
	}
}
//---------------------------------------------------
// the following is the test
// 此函數主要是爲了兼容性
function make_seed() {
	list($usec, $sec) = explode(' ', microtime());
	return (float) $sec + ((float) $usec * 100000);
}
// 生成隨機序列鍵值
function generateRandamSequence($min = 1, $max = 100){
	srand(make_seed());
 	$n = $min;
  	$a = array();
   	while($n <= $max)
	{
      	$randval = rand($min, $max);
      	if($a[$randval - $min] == '')
		{
           	$a[$randval - $min] = $n;
           	$n++;
    	}
	}
   	reset($a);
  	ksort($a);
	reset($a);
  	return $a;
}
$min = 1;
$max = 100;
// 將隨機序列鍵值存入一數組內
$a = generateRandamSequence($min, $max);
print_r($a);   // 打印數組
$tree = new binaryTree; // 建立一棵樹
// 將節點按鍵值順序插入到樹中,同時調整樹的結構
for($i=0;$i<$max-$min+1;$i++)
	$tree->insertNode($a[$i]);
print_r($tree); // 打印樹對象的內容
echo serialize($tree); // 打印樹序列化之後的內容
echo "<hr>;\n<pre>;";
$t =& $tree->root; // 指定樹根,爲以下傳入引用參數用
$tree->preorder($t, 0); // 先根遍歷,打印樹型結構
// 打印完畢可發現,鍵值比根鍵值小的所有節點均在根的左邊,反之則在右邊,每個節點都是如此
// 但此樹不是平衡樹(AVL樹),因此查詢效率還是比較低,特別是如果是連成一直線,則效率達到最低,不能利用樹的對數特性了
echo "</pre>;"; // 打印完畢
?>

轉載隨意,但請帶上本文地址:

http://www.nowamagic.net/librarys/veda/detail/353






發佈了31 篇原創文章 · 獲贊 3 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章