您的位置:首页 > 其它

无限级分类的实战应用--查找子孙树,与查找家谱树

2012-11-26 17:25 447 查看
<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
Warning: Call-time pass-by-reference has been deprecated
Fatal error: Call-time pass-by-reference has been removed

在tieba项目中,有同学报出这么两种错误.
***/

/*
$age = 10;
function grow($age) {
$age += 1;
return $age;
}

echo grow($age),'<br />'; // 11
echo $age,'<br />'; // 10

// 确实想改外部$age的值,可以这样
$age = grow($age);
*/

error_reporting(E_ALL|E_STRICT);

$age = 10;
function grow($age) {
$age += 1;
return $age;
}

echo grow(&$age),'<br />'; // 11
echo $age,'<br />'; // 11

/*
第2个函数,用的是引用传值,
函数内部的$age 和 全局$age 指向的是同一个变量地址.
因此,内部变化,影响了外部的变量

但是--仔细思考,这种写法,有一个非常不好的地方
违反了封装性.

函数运行之后,对外部的环境应该是"没有副作用的".

因此:对函数进行引用传参,是不推荐的!
在PHP5.0以上就不推荐了,
PHP5.4的时候,干脆删除了引用传参这个功能.

因此:allow_call_time_pass_reference = Off
这个选项如果off,即不推荐引用传参,会报warning

而在php5.4版本中,彻底不允许引用传参,
因此,报fatal error

解决办法:
1: allow_call_time_pass_reference = On 重启apache
但不推荐这样来做,归根结底,引用传参数不够规范.

2: 不引用传参,自己写方法来递归的转义数组
*/

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
递归对数组进行转义
***/

// 这是一个3维数组
$arr = array('a"b',array("c'd",array('e"f')));

// 先写一个1维数组的转义函数
function _addslashes($arr) {
foreach($arr as $k=>$v) {
if(is_string($v)) {
$arr[$k] = addslashes($v);
} else if(is_array($v)) { // 再加判断,如果是数组,调用自身,再转
$arr[$k] = _addslashes($v);
}
}

return $arr;
}

print_r(_addslashes($arr)); // 递归转义后的数组

print_r($arr); // 原来的值

// 这个递归 就没有用到引用传参

// 如果确定要改全局的$arr,可以把转义的返回值,再次赋给$arr

$arr = _addslashes($arr);

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====

// 对于下面这个数组,每个单元有自己的id和地区名
// 每个单元之间的地位是平等的
// 因此谈不上谁是谁的上级/下级
$area = array(
array('id'=>1,'name'=>'安徽'),
array('id'=>2,'name'=>'海淀'),
array('id'=>3,'name'=>'濉溪县'),
array('id'=>4,'name'=>'昌平'),
array('id'=>5,'name'=>'淮北'),
array('id'=>6,'name'=>'朝阳'),
array('id'=>7,'name'=>'北京'),
array('id'=>8,'name'=>'上地');

// 我们为了表示地区之间的上下级关系,人为的加了一个字段
// parent
// parent的值 是 该栏目的父栏目的id
// 找A栏目的子栏目时: 谁的parnet值等于A的id值,谁就是A的儿子
$area = array(
array('id'=>1,'name'=>'安徽','parent'=>0),
array('id'=>2,'name'=>'海淀','parent'=>7),
array('id'=>3,'name'=>'濉溪县','parent'=>5),
array('id'=>4,'name'=>'昌平','parent'=>7),
array('id'=>5,'name'=>'淮北','parent'=>1),
array('id'=>6,'name'=>'朝阳','parent'=>7),
array('id'=>7,'name'=>'北京','parent'=>0),
array('id'=>8,'name'=>'上地','parent'=>2)
);

顺着这层关系,我们可以分析出
0
安徽
淮北
濉溪县
北京
海淀
上地
昌平
朝阳
***/

/**
无限级分类,牵涉3个应用
0是 找指定栏目的子栏目
1是 找指定栏目的子孙栏目,即子孙树
2是 找指定的栏目的父栏目/父父栏目....顶级栏目, 即家谱树
**/

$area = array(
array('id'=>1,'name'=>'安徽','parent'=>0),
array('id'=>2,'name'=>'海淀','parent'=>7),
array('id'=>3,'name'=>'濉溪县','parent'=>5),
array('id'=>4,'name'=>'昌平','parent'=>7),
array('id'=>5,'name'=>'淮北','parent'=>1),
array('id'=>6,'name'=>'朝阳','parent'=>7),
array('id'=>7,'name'=>'北京','parent'=>0),
array('id'=>8,'name'=>'上地','parent'=>2)
);

// 找子栏目
function findson($arr,$id=0) {
// $id栏目的儿子有些呢?
// 答:数组循环一遍,谁的parent值 等于 $id,谁就是他儿子

$sons = array(); // 子栏目数组

foreach($arr as $v) {
if($v['parent'] == $id) {
$sons[] = $v;
}
}

return $sons;
}

// print_r(findson($area,0));

// 找子孙树
// 用静态变量
/*
function subtree($arr,$id=0,$lev=1) {
static $subs = array(); // 子孙数组

foreach($arr as $v) {
if($v['parent'] == $id) {
$v['lev'] = $lev;
$subs[] = $v; // 举例说找到array('id'=>1,'name'=>'安徽','parent'=>0),
subtree($arr,$v['id'],$lev+1);
}
}

return $subs;
}

// print_r(subtree($area,0,1));
*/

function subtree($arr,$id=0,$lev=1) {
$subs = array(); // 子孙数组

foreach($arr as $v) {
if($v['parent'] == $id) {
$v['lev'] = $lev;
$subs[] = $v; // 举例说找到array('id'=>1,'name'=>'安徽','parent'=>0),
$subs = array_merge($subs,subtree($arr,$v['id'],$lev+1));
}
}

return $subs;
}

$tree = subtree($area,0,1);
foreach($tree as $v) {
echo str_repeat('  ',$v['lev']),$v['name'],'<br />';
}

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
再见 static
***/

function t() {
$age = 5;

$age += 1;

return $age;
}

echo t(),'<br />';

echo t(),'<br />';
echo t(),'<br />';

/*

每调用一次t函数

t函数就要初始化$age = 5;
并执行,

因此 6 6 6

函数就是个封装的执行体,前后执行,没有联系 这是正常的.

要想让$age 不每次初始化,可以使用static 静态变量
*/

function st() {
static $age = 5;

$age += 1;

return $age;
}

echo st(),'<br />';
echo st(),'<br />';
echo st(),'<br />';

/***
在函数中声明的static 静态变量,
无论此函数调用多少次,只初始化一次.

以后就会直接沿用该变量,
这在递归时,很有用.

static总结
1: 修饰类的属性与方法为静态属性,静态方法
2: static::method(), 延迟绑定
3: 在函数/方法中,声明静态变量用
***/

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
数组的加,和merge
***/

$a = array('a','b');
$b = array(2=>'c',3=>'d');

print_r($a + $b);

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
无限级分类,求家谱树

家谱树的应用 ,如面包屑导航 首页 > 手机类型 > CDMA手机 > 公益PHP > 递归应用
***/

$area = array(
array('id'=>1,'name'=>'安徽','parent'=>0),
array('id'=>2,'name'=>'海淀','parent'=>7),
array('id'=>3,'name'=>'濉溪县','parent'=>5),
array('id'=>4,'name'=>'昌平','parent'=>7),
array('id'=>5,'name'=>'淮北','parent'=>1),
array('id'=>6,'name'=>'朝阳','parent'=>7),
array('id'=>7,'name'=>'北京','parent'=>0),
array('id'=>8,'name'=>'上地','parent'=>2)
);

/*
人肉上地的家谱树

上地[parent == 2]
海淀[id==2,parent==7]
北京[id==7,parent==0]

parnet==0,到头了.....

思路:只要parent!=0,就继续找

*/

/*
function familytree($arr,$id) {
static $tree = array();

foreach($arr as $v) {
if($v['id'] == $id) {
$tree[] = $v; // 以找到上地为例

// 判断要不要找父栏目
if($v['parent'] > 0) { // parnet>0,说明有父栏目
familytree($arr,$v['parent']);
}
}
}

return $tree;
}
*/

/*
function familytree($arr,$id) {
$tree = array();

foreach($arr as $v) {
if($v['id'] == $id) {
$tree[] = $v; // 以找到上地为例

// 判断要不要找父栏目
if($v['parent'] > 0) { // parnet>0,说明有父栏目
$tree = array_merge($tree,familytree($arr,$v['parent']));
}
}
}

return $tree;
}

print_r(familytree($area,8)); // 上地->海淀->北京
*/

function familytree($arr,$id) {
$tree = array();

foreach($arr as $v) {
if($v['id'] == $id) {// 判断要不要找父栏目
if($v['parent'] > 0) { // parnet>0,说明有父栏目
$tree = array_merge($tree,familytree($arr,$v['parent']));
}

$tree[] = $v; // 以找到上地为例
}
}

return $tree;
}

print_r(familytree($area,8)); // 北京->海淀->上地

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
用迭代来找家谱树
***/

$area = array(
array('id'=>1,'name'=>'安徽','parent'=>0),
array('id'=>2,'name'=>'海淀','parent'=>7),
array('id'=>3,'name'=>'濉溪县','parent'=>5),
array('id'=>4,'name'=>'昌平','parent'=>7),
array('id'=>5,'name'=>'淮北','parent'=>1),
array('id'=>6,'name'=>'朝阳','parent'=>7),
array('id'=>7,'name'=>'北京','parent'=>0),
array('id'=>8,'name'=>'上地','parent'=>2)
);

// 迭代,效率比递归高,代码也没多.
// 找家谱树推荐用迭代
function tree($arr,$id) {
$tree = array();

while($id !== 0) {
foreach($arr as $v) {
if($v['id'] == $id) {
$tree[] = $v;
$id = $v['parent'];
break;
}
}
}

return $tree;
}

print_r(tree($area,8));

/***
如果用迭代法,找一个栏目的子孙树呢?
提示:用栈来帮忙.
***/

<?php
/****
燕十八 公益PHP讲堂

论 坛: http://www.zixue.it 微 博: http://weibo.com/Yshiba YY频道: 88354001
****/

/***
====笔记部分====
用迭代法找子孙树
***/

$area = array(
array('id'=>1,'name'=>'安徽','parent'=>0),
array('id'=>2,'name'=>'海淀','parent'=>7),
array('id'=>3,'name'=>'濉溪县','parent'=>5),
array('id'=>4,'name'=>'昌平','parent'=>7),
array('id'=>5,'name'=>'淮北','parent'=>1),
array('id'=>6,'name'=>'朝阳','parent'=>7),
array('id'=>7,'name'=>'北京','parent'=>0),
array('id'=>8,'name'=>'上地','parent'=>2)
);

function subtree($arr,$parent=0) {
$task = array($parent); // 任务表
$tree = array(); // 地区表

while(!empty($task)) {
$flag = false;

foreach($arr as $k=>$v) {

if($v['parent'] == $parent) {
$tree[] = $v;
array_push($task,$v['id']); // 把最新的地区id入任务栈
$parent = $v['id'];
unset($arr[$k]); // 把找到单元unset掉

$flag = true; //说明找到了子栏目

}
}

if($flag == false) {
array_pop($task);
$parent = end($task);
}

//print_r($task);
}

return $tree;
}

print_r(subtree($area,0));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: