`
xieye
  • 浏览: 834941 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

预排序遍历树算法(非递归无限极分类算法)学习笔记(转载文章)

 
阅读更多
原文:
http://be-evil.org/post-168.html

本文是我学习MySQL官方教程Managing Hierarchical Data in MySQL的笔记

多层数据结构估计所有的web开发者估计都不会陌生,各种软件的分类都是基于多层结构来设计的。

下面是一个典型的多层数据结构示意图:

点击查看原图

相关创建数据语句:
CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);


INSERT INTO category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);

SELECT * FROM category ORDER BY category_id;

在这种数据结构中,各层之间通过字段 parent 来形成邻接表,我们查询某些层级的关系的时候一般都是通过递归的方式,遍历某个层级关系的SQL的查询次数会顺着层级的增加,想想在层级有20的时候,根据某个底层节点取它到顶层节点的查询次数吧。

为了解决这个问题,人们想出了嵌套集模型(The Nested Set Model),请看下图:

点击查看原图

上图依然是表现的与图一相同的层级关系,但是却更换了一种表现形式 下面是新的关系表和数据(关系和数据与之前相同,但是表结构不一样):



CREATE TABLE nested_category (
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);


INSERT INTO nested_category
VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
(4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),
(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
(9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);


SELECT * FROM nested_category ORDER BY category_id;

这里将 left,right 修改为 lft,rgt因为这两个词在MYSQL中属于关键字 下面我们将插入的数据标识在图上: 点击查看原图

同样,我们将数据标识在原来的结构上:

点击查看原图


怎么样,是不是很明确了

下面使我自己标定一种形式,方便理解

[1
      [2
           [3 4]
           [5 6]
           [7 8]
      9]
      [10
           [11
                 [12 13]
           14]
           [15 16]
           [17 18]
      19]
20]

遍历整个树,查询子集 条件:左边 > 父级L, 右边 < 父级R

SELECT node.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| TELEVISIONS          |
| TUBE                 |
| LCD                  |
| PLASMA               |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
| CD PLAYERS           |
| 2 WAY RADIOS         |
+----------------------+

- 查询所有无分支的节点 条件:右边 = 左边L + 1

SELECT name
FROM nested_category
WHERE rgt = lft + 1;

- 查询某个字节点到根节点的路径

SELECT parent.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'FLASH'
ORDER BY parent.lft;


SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

- 查询子节点的深度
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
    nested_category AS parent,
    nested_category AS sub_parent,
    (
        SELECT node.name, (COUNT(parent.name) - 1) AS depth
        FROM nested_category AS node,
        nested_category AS parent
        WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.name = 'PORTABLE ELECTRONICS'
        GROUP BY node.name
        ORDER BY node.lft
    )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
    AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;



- 插入新节点
算法详解:
1.所有分类 左边和右边的值 > 插入节点的左边节点记录的右值 的全部 + 2
2.插入节点 左值 = 插入位置左边节点记录的右值 + 1, 右值 = 插入位置左边节点记录的右值 + 2
例子:
在 R = 9(L8, R9)与 L = 10(L10,R11) 节点之间插入一个新节点
那么所有 左值 和 右值 > 9 的节点的左值和右值需要 + 2
例如新节点右边的节点(L10,R11)左值右值都需要 + 2 那么插入后的新值为 L12 R13
新节点的左值为 9 + 1 = 10 右值为 9 + 2 = 11
SQL语句实现
LOCK TABLE nested_category WRITE;
SELECT @myRight := rgt FROM nested_category
WHERE name = 'TELEVISIONS';
UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;
INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2);
UNLOCK TABLES;

- 删除新节点
删除节点的算法与添加一个节点的算法相反

删除一个没有子节点的节点
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'GAME CONSOLES';
DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;
UNLOCK TABLES;

删除一个分支节点和它所有的子节点
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'MP3 PLAYERS';
DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;
UNLOCK TABLES;


删除一个节点后移动其字节点到
LOCK TABLE nested_category WRITE;
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'PORTABLE ELECTRONICS';
DELETE FROM nested_category WHERE lft = @myLeft;
UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > @myRight;
UNLOCK TABLES;


总结:

预排序遍历树算法的核心就是牺牲了写的性能来换取读取的性能

在你的开发的应用遇到此类问题的时(读压力 > 写压力),尝试下使用预排序遍历树算法来提高你的程序的性能吧。
分享到:
评论

相关推荐

    无限级分类----改进前序遍历树

    改进的前序遍历算法可以借助递归实现,遍历过程中不仅访问当前节点,还要将所有子节点(包括孙节点、曾孙节点等)都纳入遍历范围。以下是一个C#的递归实现示例: ```csharp public static void ...

    php通过前序遍历树实现无需递归的无限极分类

    本文介绍了一种基于前序遍历树的非递归方法,这种方法在大数据量场景下更高效。 首先,我们需要创建一个存储分类的数据表,这里是一个名为`category`的示例表结构: ```sql CREATE TABLE IF NOT EXISTS `category`...

    laravel5无限极分类递归

    本文将详细讲解如何在Laravel 5中实现无限极分类递归,以及提供的压缩包文件内容及其用途。 首先,无限级分类通常基于自关联的数据库表结构,这意味着每个分类都可以有父分类,也可以有子分类,形成一个树状结构。...

    php递归获取子级,父级,无限极分类,带demo,效率超高

    通常,使用预排序遍历树(Preorder Traversal)或后序遍历(Postorder Traversal)可以有效减少递归深度,降低内存使用。另外,通过缓存结果,避免重复计算,也能显著提高性能。 5. **DataTree.php**: 这个文件很...

    tp树形无限极分类

    4. **预排序遍历树(Pre-Order Tree Traversal,PTT)**:在数据库查询时,通过一次遍历获取所有分类,并在代码中构造树形结构。 **在ThinkPHP 3.2.3中的实现:** 这个示例可能包括以下部分: 1. 数据库表设计:...

    无限极分类下拉框 无限极 分类 下拉框

    在实现无限极分类下拉框时,关键在于递归遍历数据源,将具有层级关系的数据逐层展开并添加到下拉框中。具体步骤如下: 1. **数据源准备**:首先,需要一个数据源,这个数据源应该包含每个分类项的唯一标识符(ID)...

    Java 无限极 树结构

    对于无限极树,遍历可能需要递归实现。例如,前序遍历的实现: ```java public void preOrderTraversal(TreeNode node) { if (node != null) { System.out.println(node.getName()); for (TreeNode child : node...

    php无限极分类递归排序实现方法

    在PHP编程中,无限极分类递归排序是一个常见的需求,特别是在处理树形结构的数据时,如网站导航菜单、组织架构或者商品分类等。无限极分类意味着一个分类可以有任意多的子分类,而递归排序则能确保这些子分类按照...

    asp 无限极分类完整实例

    在ASP(Active Server Pages)开发中,无限极分类是一种常见的需求,特别是在构建网站内容管理系统时。这个"asp 无限极分类完整实例"提供了一个完整的解决方案,帮助开发者处理具有层级关系的数据,例如:导航菜单、...

    用递归算法实现无限极添加、删除、修改和移动菜单

    通过递归遍历菜单树,找到目标节点后进行更新即可。如果目标节点的子节点也需要修改,同样可以通过递归来实现。 **6. 移动菜单项** 移动菜单项涉及到更改节点的父节点。这需要递归地找到目标节点,然后将其从当前父...

    php无限极分类源码.

    无限极分类的基本思想是利用递归或者自连接的方式来实现。在这个实例中,我们可以假设有一个数据库表,其中包含`id`(主键)、`name`(分类名称)和`parent_id`(父分类ID)字段。通过`parent_id`,我们可以确定每个...

    php无限极分类函数

    php无限极分类函数包,下载即可用,绝对好用,里面有多种无限极分类函数,可以参考,我都试过了

    php无限极分类两种方法.rar

    首先,我们来详细讲解**递归无限极分类**。这种方法基于函数的自我调用来实现。假设我们有一个`categories`表,其中包含`id`(主键)、`name`(类别名)和`parent_id`(父类ID)字段。我们可以通过以下步骤实现递归...

    无限极分类

    3. **预排序遍历树(Preorder Traversal)**:在遍历数据库获取分类时,通常采用预排序遍历,即先访问根节点,再访问左子树,最后访问右子树。这种方式便于构建无限级分类的层级关系。 4. **存储方式**:无限级分类...

    C#无限极分类

    在IT行业中,尤其是在数据库设计和数据展示领域,无限极分类是一种常见的需求。它允许我们创建一个没有固定深度的层级结构,比如网站导航菜单、组织架构、产品类别等。C#作为.NET框架的主要编程语言,提供了多种实现...

    jQuery递归无限极树状菜单.zip

    本项目"jQuery递归无限极树状菜单"就是这样一个解决方案,它利用jQuery的灵活性和CSS的样式控制,实现了可扩展的无限级树形菜单。 首先,我们需要理解jQuery的核心概念。jQuery是一个JavaScript库,它简化了HTML...

    无限极分类sort排序,自己填写排序

    php无限极分类,手动排序,排序,无限极分类排序,自己填写排序

    递归,无限极评论实现

    在IT领域,递归是一种强大的编程技术,常用于解决复杂问题,如数据结构的遍历、树形结构的处理等。在这个场景中,我们关注的是如何利用递归来实现无限极评论的功能。无限极评论通常应用于社交网络、论坛或博客系统,...

Global site tag (gtag.js) - Google Analytics