多叉树 php,php利用多叉树(平衡树)的方式构建无限分类

说起无限分类..大多数的结构都是  id   name   parent_id  这种模式.整个结构比较简单清晰.要构建和更新整个分类也比较容易.但是查询起来就会非常的麻烦.经常会用到递归的算法.例如 获取某个节点的所有父节点之类.

今天说一说通过多叉树的方式构建无限分类,结构上可能会复杂一点,构建和更新也比较麻烦.但是查询非常方便.两种方法的优劣就不评论了.

先看一张图

A162813599-17235.jpg_small.jpg

这是我们要构建的无限分类的模型. 电子产品是最大的分类.家用电器 ,数码产品是其子分类.可以看到子分类是被父分类包含起来的.每个分类都有左右 两个节点编号分别是1、2、3.....

根据上面的图mysql中建立表和插入数据

CREATE TABLE  `product_categories` (

`id` MEDIUMINT( 8 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,

`name` VARCHAR( 20 ) NOT NULL ,

`left_node` MEDIUMINT( 8 ) NOT NULL ,

`right_node` MEDIUMINT( 8 ) NOT NULL

) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO `product_categories` (`id`, `name`, `left_node`, `right_node`) VALUES

(1, '电子产品', 1, 20),

(2, '家用电器', 2, 9),

(3, '电视机', 3, 4),

(4, '电冰箱', 5, 6),

(5, '空调', 7, 8),

(6, '数码产品', 10, 19),

(7, '电脑', 11, 18),

(8, '台式电脑', 12, 13),

(9, '笔记本电脑', 14, 15),

(10, '平板电脑', 16, 17);

表结构如下:

A162815834-17236.png

下面是PHP的实例代码:

1、获取所有节点

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

$stmt = $pdo->prepare("SELECT c.name FROM product_categories as c, product_categories as p

WHERE c.left_node BETWEEN p.left_node AND p.right_node

AND p.name='电子产品' ORDER BY c.left_node");

$stmt->execute();

$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);

foreach($rs as $v){

echo $v['name'].'
';

} 输出:

电子产品

家用电器

电视机

电冰箱

空调

数码产品

电脑

台式电脑

笔记本电脑

平板电脑 2、

获取某个父节点以及其所有子节点

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

$stmt = $pdo->prepare("SELECT c.name FROM product_categories as c, product_categories as p

WHERE c.left_node BETWEEN p.left_node AND p.right_node

AND p.name='数码产品' ORDER BY c.left_node");

$stmt->execute();

$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);

foreach($rs as $v){

echo $v['name'].'
';

}

输出:

数码产品

电脑

台式电脑

笔记本电脑

平板电脑

3、获取所有的叶子节点

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

$stmt = $pdo->prepare("SELECT name FROM product_categories where right_node-left_node=1");

$stmt->execute();

$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);

foreach($rs as $v){

echo $v['name'].'
';

}

输出:

电视机

电冰箱

空调

台式电脑

笔记本电脑

平板电脑

4、获取某个子节点及其所有父节点

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

$stmt = $pdo->prepare("SELECT p.name FROM product_categories AS c, product_categories AS p WHERE c.left_node BETWEEN p.left_node AND p.right_node AND c.name = '平板电脑' ORDER BY p.left_node");

$stmt->execute();

$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);

foreach($rs as $v){

echo $v['name'].'
';

} 输出:

电子产品

数码产品

电脑

平板电脑

5、获取所有节点极其所处的层级

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

$stmt = $pdo->prepare("SELECT c.name, (COUNT(p.name) - 1) AS level FROM product_categories AS c, product_categories AS p WHERE c.left_node BETWEEN p.left_node AND p.right_node GROUP BY c.name ORDER BY c.left_node");

$stmt->execute();

$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($rs);

echo '
';

foreach($rs as $v){

echo $v['name'].' level:'.$v['level'].'
';

} 输出:

电子产品 level:0

家用电器 level:1

电视机 level:2

电冰箱 level:2

空调 level:2

数码产品 level:2

电脑 level:2

台式电脑 level:3

笔记本电脑 level:3

平板电脑 level:3

6、获取某个节点的层级

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

$stmt = $pdo->prepare("SELECT c.name, (COUNT(p.name) - 1) AS level FROM product_categories AS c, product_categories AS p WHERE c.left_node BETWEEN p.left_node AND p.right_node and c.name='平板电脑' GROUP BY c.name ORDER BY c.left_node");

$stmt->execute();

$rs=$stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($rs);

echo '
';

foreach($rs as $v){

echo $v['name'].' level:'.$v['level'].'
';

} 输出:

平板电脑 level:3

7、在某个节点后平行的插入一个节点

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

function addNode($left_node,$new_node){

global $pdo;

$stmt = $pdo->prepare("SELECT right_node FROM product_categories WHERE name = '$left_node'");

$stmt->execute();

$rs=$stmt->fetch(PDO::FETCH_ASSOC);

$right_node=$rs['right_node'];

$pdo->exec("UPDATE product_categories SET right_node = right_node + 2 WHERE right_node > $right_node");

$pdo->exec("UPDATE product_categories SET left_node = left_node + 2 WHERE left_node > $right_node");

$pdo->exec("INSERT INTO product_categories(name, left_node, right_node) VALUES('$new_node', $right_node + 1, $right_node + 2)");

}

addNode('家用电器','办公用品'); 完成之后表结构如下:

A162817912-17237.png

8、删除某个节点及其所有子节点

$pdo = new PDO(

'mysql:host=localhost;dbname=test',

'root',

''

);

$pdo->exec("SET NAMES UTF8");

function deleteNode($node_name){

global $pdo;

$stmt = $pdo->prepare("SELECT left_node,right_node, right_node - left_node + 1 as width FROM product_categories WHERE name ='$node_name'");

$stmt->execute();

$rs=$stmt->fetch(PDO::FETCH_ASSOC);

$left_node=$rs['left_node'];

$right_node=$rs['right_node'];

$width=$rs['width'];

$pdo->exec("DELETE FROM product_categories WHERE left_node BETWEEN $left_node AND $right_node");

$pdo->exec("UPDATE product_categories SET right_node = right_node - $width WHERE right_node > $right_node");

$pdo->exec("UPDATE product_categories SET left_node = left_node - $width WHERE left_node > $right_node");

}

deleteNode('数码产品'); 完成之后表结构如下:

A162819990-17238.png

可以看到用多叉树的方式构建无限分类,查询的时候是非常简便的.但是在插入新的节点和删除节点时就比较麻烦了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值