無限遞歸樹展示

<?php
/**
 * 無限級(受尾節點描述算法限制, 詳見tree_parse註釋)遞歸菜單
 * author: selfimpr
 * blog: http://blog.csdn.net/lgg201
 * mail: [email protected]
 */

define('MAX_NODES',			3);						/* 最大子節點數 */
define('MAX_NODE_INDEX',	MAX_NODES - 1);			/* 子節點最大索引值 */
define('NAME_FMT',			'name-%08d');				/* 節點內容輸出格式串 */

/* 樹節點數據結構 */
define('K_ID',				'id');
define('K_NAME',			'name');
define('K_CHILD',			'children');

/* 輸出構造時使用的拼裝字符 */
define('PREFIX_TOP',		'┏');					/* 第一層第一個節點的標識符 */
define('PREFIX_BOTTOM',		'┗');					/* 每一個父節點的最後一個子節點的標識符 */
define('PREFIX_MIDDLE',		'┠');					/* 所有非上面兩種情況的節點的標識符 */
define('PREFIX_LINE',		'┇');					/* 祖先節點的連線符 */
define('SPACE',				' ');					/* 空白佔位(所有尾節點不顯示連線符) */
define('WIDE_SPACE',		str_repeat(SPACE, 4));	/* 寬的空白佔位, 爲了讓樹的層次清晰 */


/**
 * data_build 
 * 構造一個節點
 * @param mixed $id 		節點id
 * @param mixed $is_leaf 	是否葉子
 * @access public
 * @return void
 */
function node_build($id, $is_leaf = FALSE) {
	return array(
		K_ID	=> $id, 
		K_NAME	=> sprintf(NAME_FMT, $id), 
		K_CHILD	=> $is_leaf ? NULL : array(), 
	);
}
/**
 * tree_build 
 * 構造一棵樹(樹中每個節點的子節點數由MAX_NODES確定)
 * @param mixed $datas 	要返回的樹引用
 * @param mixed $id 	起始ID
 * @param mixed $level 	樹的層級
 * @access public
 * @return void
 */
function tree_build(&$datas, &$id, $level) {
	if ( $level < 1 ) return ;
	$is_leaf	= $level == 1;
	$i			= -1;
	$next_level	= $level - 1;
	while ( ++ $i < MAX_NODES ) {
		$data	= node_build($id ++, $is_leaf);
		if ( !$is_leaf ) 
			tree_build($data[K_CHILD], $id, $next_level);
		array_push($datas, $data);
	}
}

/**
 * node_str 
 * 輸出一個節點自身的信息
 * @param mixed $string 返回結果的字符串(引用傳值)
 * @param mixed $data 	節點數據
 * @access public
 * @return void
 */
function node_str(&$string, $data) {
	$string	.= sprintf(' %s[%d]', $data[K_NAME], $data[K_ID]);
}
/**
 * node_sign 
 * 輸出一個節點的標誌符號
 * @param mixed $string	返回結果的字符串(引用傳值)
 * @param mixed $level 	當前深度
 * @param mixed $i 		當前節點在父節點中的索引(下標)
 * @access public
 * @return void
 */
function node_sign(&$string, $level, $i) {
	switch ( $i ) {
		case 0:
			$string	.= $level == 0 ? PREFIX_TOP : PREFIX_MIDDLE;
			break;
		case MAX_NODE_INDEX:
			$string	.= PREFIX_BOTTOM;
			break;
		default:
			$string	.= PREFIX_MIDDLE;
			break;
	}
}
/**
 * node_prefix 
 * 輸出一個節點的前綴
 * @param mixed $string 	返回結果的字符串(引用傳值)
 * @param mixed $level 		當前深度
 * @param mixed $is_last 	當前節點(含)所有祖先節點是否尾節點標記
 * @access public
 * @return void
 */
function node_prefix(&$string, $level, $is_last) {
	if ( $level > 0 ) {
		$i	= 0;
		/* 前綴格式: "父級連線" ["寬空白符" "父級連線" ...] "寬空白符" */
		$string	.= ($is_last & 1 << ($level - $i) ? SPACE : PREFIX_LINE);
		while ( ++ $i < $level ) 
			$string	.= WIDE_SPACE . ($is_last & 1 << ($level - $i) ? SPACE : PREFIX_LINE);
		$string	.= WIDE_SPACE;
	}
}
/**
 * node_out 
 * 輸出一個節點
 * @param mixed $string 	返回結果的字符串(引用傳值)
 * @param mixed $data 		要處理的節點數據
 * @param mixed $level 		節點深度
 * @param mixed $i 			節點在父節點中的索引(下標)
 * @param mixed $is_last 	當前節點(含)所有祖先節點是否尾節點標記
 * @access public
 * @return void
 */
function node_out(&$string, $data, $level, $i, $is_last) {
	/* 處理前綴字符串: 祖先的連接符及空白 */
	node_prefix($string, $level, $is_last);
	/* 處理本節點的標識符號 */
	node_sign($string, $level, $i);
	/* 處理本節點數據信息 */
	node_str($string, $data);
	/* 追加換行 */
	$string		.= "\n";
}
/**
 * tree_parse 
 * 輸出一棵樹
 * 	1. 由於使用了整型的$is_last作爲祖先是否尾節點的標記, 所以最多支持PHP_INT_MAX的深度
 * 	2. 如果需要擴展, 修正$is_last的數據類型及校驗方法即可
 * @param mixed $string 返回結果的字符串(引用傳值)
 * @param mixed $datas 	要處理的樹數據
 * @param int $level 	當前處理的深度
 * @param int $is_last 	當前深度所有祖先是否尾節點標記
 * @access public
 * @return void
 */
function tree_parse(&$string, $datas, $level = 0, $is_last = 0) {
	if ( !is_array($datas) || count($datas) < 1 ) return ;
	$max_index	= count($datas) - 1;
	/* 處理本層的所有節點 */
	foreach ( $datas as $i => $data ) {
		/* 當前節點及所有祖先是否尾節點標記 */
		$tmp_is_last	= $is_last << 1 | 1 & $i == $max_index;
		/* 輸出當前節點 */
		node_out($string, $data, $level, $i, $tmp_is_last);
		/* 如果有子節點, 遞歸子節點 */
		if ( is_array($data[K_CHILD]) && !empty($data[K_CHILD]) )
			tree_parse($string, $data[K_CHILD], $level + 1, $tmp_is_last);
	}
}

/* 計算實際節點數 */
function n_node($n, $s) {
	$sum	= 0;
	while ( $n > 0 ) 
		$sum	+= pow($s, $n --);
	return $sum;
}
/* 計算ruage時間 */
function ru_time($info, $type) {
	return floatval(sprintf('%d.%d', $info[$type . '.tv_sec'], $info[$type . '.tv_usec']));
}
/* 輸出資源使用情況 */
function resource_usage($lv, $nodes, $cb, $ce, $mb, $me, $rb, $re) {
	printf("\nresource usage[level: %d, node number: %d]: \n%20s%0.6fs\n%20s%0.6fs\n%20s%0.6fs\n%20s%d byte\n", 
		$lv, $nodes,
		'clock time: ',		$ce - $cb, 
		'system cpu: ',		ru_time($re, 'ru_stime') - ru_time($rb, 'ru_stime'), 
		'user cpu: ',		ru_time($re, 'ru_utime') - ru_time($rb, 'ru_utime'), 
		'memory usage: ',	$me - $mb);
}
/* 用法 */
function usage($cmd) {
	printf("usage: \n%s <tree deepth>\n", $cmd);
	exit;
}

/* 測試入口函數 */
function run() {
	global	$argc, $argv;

	if ( $argc != 2 || intval($argv[1]) < 1 )
		usage($argv[0]);


	$datas	= array();
	$id		= 1;
	$string	= '';
	$level	= intval($argv[1]);

	/* 初始構造測試樹 */
	tree_build($datas, $id, $level);

	$clock_begin	= microtime(TRUE);
	$memory_begin	= memory_get_usage();
	$rusage_begin	= getrusage();
	/* 解析樹 */
	tree_parse($string, $datas);
	$rusage_end	= getrusage();
	$memory_end	= memory_get_usage();
	$clock_end	= microtime(TRUE);

	/* 輸出結果 */
	echo $string . "\n";

	resource_usage($level, n_node($level, MAX_NODES),
		$clock_begin, $clock_end, 
		$memory_begin, $memory_end, 
		$rusage_begin, $rusage_end);
}

/* 執行入口函數 */
run();
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: t
 * End:
 */

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