The Algorithm for Infinite Classification of Left and Right Values

The implementation algorithm of infinite classification of left and right values ​​(pre-sorting tree traversal algorithm)

1. Introduction
Product classification, multi-level tree-structured forums, mailing lists and many other places we will encounter such a problem: how to store multi-level structure data? In the application of PHP, the relational database that provides background data storage is usually a relational database, which can save a large amount of data and provide efficient data retrieval and update services. However, the basic form of relational data is a criss-cross table, which is a flat structure. If a multi-level tree structure is to be stored in a relational database, reasonable translation work is required. Next, I will discuss what I have seen and heard and some practical experience with you:
There are basically two common design methods for storing hierarchically structured data in a flat database:

adjacency list model (adjacency list model)
Pre-sorting Modified preorder tree traversal algorithm (modified preorder tree traversal algorithm)


I am not a computer major, and I have not learned anything about data structures, so these two names are translated literally by myself. If I am wrong, please give me more advice. . These two things may sound scary, but they are actually very easy to understand.

2. Model
Here I use a simple food catalog as our example data.
Our data structure is like this, here is the code:

Copy Code
Food
|
|---Fruit
| |
| |---Red
| | |
| | |--Cherry
| |
| +---Yellow
| |
| +--Banana
|
+---Meat
      |--Beef
      +-- PorkCopy
code
To take care of those PHP lovers whose English is a mess

Copy code
Food : Food
Fruit : FruitRed
: Red
Cherry:
CherryYellow: Yellow
Banana : Banana
Meat : Meat
Beef : Beef
Pork : Pork
Copy code
3. Realization

1. Adjacency list model

This model is often used and introduced in many tutorials and books. We describe the entire tree structure through a flat table by adding an attribute parent to each node to represent the parent node of this node. According to this principle, the data in the example can be transformed into the following table:
The following is the code:

copy code
+-------------+
| parent | name |
+------------+
| | Food |
| Food | Fruit |
| Fruit | Green |
| Green | Pear |
| Fruit | Red |
| Red | Cherry |
| Fruit | Yellow |
| Yellow | Banana |
| Food | Meat |
| Meat | Beef |
| Meat | Pork |
+-------------+
Copy code
We see that Pear is a child node of Green, and Green is a child node of Fruit. And the root node 'Food' has no parent node. To simplify the description of the problem, only name is used in this example to represent a record. In the actual database, you need to identify each node with a numerical id, and the table structure of the database should be like this: id, parent_id, name, descrīption.
With such a table, we can save the entire multi-level tree structure through the database.

Displaying a multilevel tree requires a recursive function if we need to display such a multilevel structure.
Here is the code:

copy code
<?php
// $parent is the parent of the children we want to see
// $level is increased when we go deeper into the tree,
// used to display a nice indented tree
function display_children($parent, $level) {
    // Get all children of a parent $parent
    $result = mysql_query( "
        SELECT name
        FROM tree
        WHERE parent = '" . $parent . "'
        ;"
    );
    // show each child node
    while ($row = mysql_fetch_array($result)) {
        // indent show node name
        echo str_repeat(' ' , $level) . $row['name'] . "\n";
        //Call this function again to display the child nodes of the child node
        display_children($row['name'], $level+1);
    }
}
?>
copy code
Use this function on the root node (Food) of the entire structure to print out the entire multi-level tree structure. Since Food is the root node and its parent node is empty, call it like this: display_children('',0). Will display the whole tree:

Copy Code
Food
    Fruit
        Red
            Cherry
        Yellow
            Banana
    Meat
        Beef
        Pork Using almost the same method we can know the path from the root node to any node. For example, the path for Cherry is "Food >; Fruit >; Red". To get such a path we need to start from the deepest level "Cherry", query for its parent "Red" and add it to the path, then we query Red's parent and add it to the path as well , and so on until the top "Food", the following is the code: copy code <











        SELECT parent
        FROM tree
        WHERE name = '" . $node ."'
        ;"
    );
    $row = mysql_fetch_array($result);
    // use an array to store the path
    $path = array();
    // continue if not the root node Query up
    // (the root node has no parent)
    if ($row['parent'] != '') {
        // the last part of the path to $node, is the name
        // of the parent of $node
        $ path[] = $row['parent'];
        // we should add the path to the parent of this node
        // to the path
        $path = array_merge(get_path($row['parent']), $path);
    }
    // return the path
    return $path;
}
?>
Copy code
If you use this function for "Cherry": print_r(get_path('Cherry')), you will get an array like this

Array (
    [0] => Food
    [1] => Fruit
    [2] => Red
)
then how to print it in the format you want is up to you.

Disadvantages:
This method is simple, easy to understand, and easy to use. But there are also some disadvantages. The main reason is that the running speed is very slow, and since each node needs to be queried in the database, when the amount of data is large, many queries are required to complete a tree. In addition, due to recursive operations, each level of recursion needs to occupy some memory, so the efficiency of space utilization is relatively low.

2. Preorder tree traversal algorithm

     Now let's take a look at another method that does not use recursive calculation and is faster. This is the modified preorder tree traversal algorithm.
     This method may be less familiar to you. The first use is not as easy to understand as the above method, but since this method does not use the recursive query algorithm, it has higher query efficiency.

     We first draw the multi-level data on the paper in the following way, write 1 on the left side of the root node Food and then continue down the tree and write 2 on the left side of Fruit and then continue to move forward, along the entire tree Edges label each node with numbers on the left and right. The last number is 18 marked to the right of Food. In the image below you can see the entire multilevel structure with the numbers labeled. (Don't understand? Point your finger at the number to count from 1 to 18 and understand what's going on. If you still don't understand, count again and move your finger.)
     These numbers indicate the relationship between the various nodes, the numbers 3 and 6 of "Red" are descendant nodes of "Food" 1-18. Likewise, we can see that all nodes with an lvalue greater than 2 and an rvalue less than 11 are descendants of "Fruit" 2-11


Copy Code
                         1 Food 18
                             |
            +-------------- ----------------+
            | |
        2 Fruit 11 12 Meat 17
            | |
    +-------------+ +------- -----+
    | | | |
3 Red 6 7 Yellow 10 13 Beef 14 15 Pork 16
    | |
4 Cherry 5 8 Banana 9
Copy code
This way the entire tree structure can be stored in the database by left and right values. Before continuing, let's take a look at the data table compiled below.

Here is the code:

Copy Code
+----------+------------+-----+-----+
| parent | name | lft | rgt |
+- ---------+------------+-----+-----+
| | Food | 1 | 18 |
| Food | Fruit | 2 | 11 |
| Fruit | Red | 3 | 6 |
| Red | Cherry | 4 | 5 |
| Fruit | Yellow | 7 | 10 |
| Yellow | Banana | 8 | 9 |
| Food | Meat | 12 | 17 |
| Meat | Beef | 13 | 14 |
| Meat | Pork | 15 | 16 |
+----------+------------+-----+---- -+
Copy code
Note : Since "left" and "right" have special meanings in SQL, we need to use "lft" and "rgt" to represent the left and right fields. Also "parent" is no longer needed in this structure field to represent the tree structure. That is to say, the following table structure is sufficient.

Here is the code:

copy code
+------------+-----+-----+
| name | lft | rgt |
+------------+- ----+-----+
| Food | 1 | 18 |
| Fruit | 2 | 11 |
| Red | 3 | 6 |
| Cherry | 4 | 5 |
| Yellow | 7 | 10 |
| Banana | 8 | 9 |
| Meat | 12 | 17 |
| Beef | 13 | 14 |
| Pork | 15 | 16 |
+------------+-----+-----+ Okay
, we can now get data from the database, for example, we need to get all the nodes under the "Fruit" item, we can write a query like this: SELECT * FROM tree WHERE lft BETWEEN 2 AND 11; This query gets the following the result of. Here is the code: copy code +------------+-----+-----+ | name | lft | rgt |










+------------+-----+-----+
| Fruit | 2 | 11 |
| Red | 3 | 6 |
| Cherry | 4 | 5 |
| Yellow | 7 | 10 |
| Banana | 8 | 9 |
+------------+-----+-----+ Copy
Code
these nodes. In order to be able to display the entire tree structure like the recursive function above, we also need to sort such a query. Sort by node's lvalue:

SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;
the remaining question is how to display the indentation of the hierarchy.
The following is the code:

Copy code
<?php
function display_tree($root) {
    // Get the left and right values ​​of the root node
    $result = mysql_query("
        SELECT lft, rgt
        FROM tree
        WHERE name = '" . $root . "'
        ;"
    ) ;
    $row = mysql_fetch_array($result);
    // Prepare an empty rvalue stack
    $right = array();
    // Get all descendants of the root
    $result = mysql_query("
        SELECT name, lft, rgt
        FROM tree
        WHERE lft BETWEEN '" . $row['lft'] . "' AND '" . $row['rgt'] ."'
        ORDER BY lft ASC
        ;"
    );
    // display each row
    while ($row = mysql_fetch_array($result )) {
        // only check stack if there is one
        if (count($right) > 0) {
            // check if we should move the node off the stack
            while ($right[count($right) - 1] < $row[ 'rgt']) {
                array_pop($right);
            }
        }
        // Indent the name of the node
        echo str_repeat(' ',count($right)) . $row['name'] . "\n";
        // Add this node to the stack
        $right[] = $ row['rgt'];
    }
}
?>
Copy code
If you run the above function once you will get the same result as the recursive function. It's just that this new function of ours might be faster because there are only 2 database queries.
To know the path of a node is even simpler. If we want to know the path of Cherry, we use its left and right values ​​4 and 5 to do a query.

SELECT name FROM tree WHERE lft < 4 AND rgt >; 5 ORDER BY lft ASC;
This will result in the following result:

Here is the code:

Copy Code
+------------+
| name |
+------------+
| Food |
| Fruit |
| Red |
+------------+
Copy code
So how many descendants does a node have? Very simple, the total number of descendants = (rvalue-lvalue-1)/2

descendants = (right - left - 1) / 2
don't believe? Do the math for yourself.
Using this simple formula, we can quickly calculate that the "Fruit 2-11" node has 4 descendants, while the "Banana 8-9" node has no descendants, which means it is not a parent node.
Amazing, right? Although I've used this method many times, it still feels amazing every time I do it.
This is indeed a very good way, but is there any way to help us build such a data table with left and right values? Here is another function to introduce to you, this function can automatically convert the table of name and parent structure into a data table with left and right values.
Here is the code:

copy code
<?php
function rebuild_tree($parent, $left) {
    // the right value of this node is the left value + 1
    $right = $left+1;
    // get all children of this node
    $ result = mysql_query("
        SELECT name
        FROM tree
        WHERE parent = '" . $parent . "'
        ;"
    );
    while ($row = mysql_fetch_array($result)) {
        // recursive execution of this function for each
        // child of this node
        // $right is the current right value, which is
        // incremented by the rebuild_tree function
        $right = rebuild_tree($row['name'], $right);
    }
    // we've got the left value, and now that we've processed
    // the children of this node we also know the right value
    mysql_query("
        UPDATE tree
        SET
            lft = '" . $left . "',
            rgt= '" . $right . "'
        WHERE name = '" . $parent . "'
        ;"
    );
    // return the right value of this node + 1
    return $right + 1;
}
?> Of course
this function is a recursive function, we need to run this function from the root node to rebuild a tree with left and right values ​​rebuild_tree( 'Food',1); This function looks a bit complicated, but its function is the same as manually numbering the table, that is, it converts the three-dimensional multi-layer structure into a data table with left and right values. So how do we add, update and delete a node for such a structure? There are generally two ways to add a node: the first way, keep the original name and parent structure, add data to the data using the old method, and use the rebuild_tree function to renumber the entire structure after each piece of data is added. The second, more efficient way is to change all the values ​​to the right of the new node. For example: We want to add a new fruit "Strawberry" which will be the last child of the "Red" node. First we need to make some space for it. The rvalue of "Red" should be changed from 6 to 8, and the left and right values ​​of "Yellow 7-10" should be changed to 9-12. By analogy, we can see that to make room for the new value, we need to add 2 to all nodes whose left and right values ​​are greater than 5 (5 is the right value of the last child of "Red"). So we do the database operation like this: UPDATE tree SET rgt = rgt + 2 WHERE rgt > 5;












This frees up space for the newly inserted value. Now a new data node can be created in the freed space. Its left and right values ​​are 6 and 7 respectively.

INSERT INTO tree SET lft=6, rgt=7, name = 'Strawberry';
do another query to see it! how about it? Soon.

Fourth, the conclusion
Well , now you can design your multi-level database structure in two different ways, which method is completely up to your personal judgment, but for the structure with many levels and large numbers, I prefer the second one method. The first method is easier if the query volume is small but requires frequent additions and updates of data.
In addition, if the database supports it, you can also write rebuild_tree() and free space operations as database-side trigger functions, which are automatically executed when inserting and updating, which can achieve better operating efficiency, and you can add new nodes. SQL statements will become simpler.

This article is from http://www.cnblogs.com/sonicit/archive/2013/05/21/3090518.html Thanks here!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326171992&siteId=291194637
Recommended