2010-08-05 15 views
6

Non ho uno sfondo in CS o strutture dati. Voglio fare una classe PHP che memorizza un modified preorder transversal tree, per la manipolazione e la sincronizzazione con un database.struttura dati per albero trasversale in PHP?

Fondamentalmente ho bisogno di memorizzare i dati come:

+-------------+----------------------+-----+-----+ 
| category_id | name     | lft | rgt | 
+-------------+----------------------+-----+-----+ 
|   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 | 
+-------------+----------------------+-----+-----+ 

Stavo pensando di utilizzare un array, ma sembra ingombrante. Se si trattasse di un array di array come questo: array('name'=> "PORTABLE ELECTRONICS", 'lft' => 10, 'rgt' = 19), allora sarebbe ottenere ingombrante per scorrere tale matrice più volte per assicurarsi che tutti i numeri sono presenti, ecc

Dal momento che PHP ha alcune nuove strutture di dati disponibili, mi chiedo se qualcuno di questi mi procurerebbe dei benefici sull'uso di un array?

  • SplDoubly
  • ListaLinkata
  • SplStack
  • SplQueue
  • SplHeap
  • SplMaxHeap
  • SplMinHeap
  • SplPriorityQueue
  • SplFixedArray
  • SplObjectStorage

Edit: Questa classe non sta per essere un gateway ad un albero memorizzato in una tabella del database. (Se lo fosse, vorrei solo avere una query di classi.) È solo un mmpt stand-alone in una sorta di struttura dati PHP.

+1

* (risorsa) * http://matthewturland.com/2010/05/20/new-spl-features-in-php-5-3/ – Gordon

+2

Heh, infatti non è l'adiacenza ma il modello dell'insieme annidato. Non credo che nessuno di questi nuovi SPL possa essere d'aiuto, dato che lo stesso _node_ conosce i suoi vicini, non solo i genitori. Avrei solo un sacco di oggetti che hanno un riferimento agli oggetti a sinistra ea destra, con una sorta di pattern 'Composite' per cambiare le impostazioni in cascata. – Wrikken

+1

Potrebbe valere la pena dare un'occhiata a come [Doctrine] [http://www.propelorm.org/browser/branches/1.6/generator/lib/behavior/nestedset] e [Propel] [http: //trac.doctrine -project.org/browser/branches/1.2/lib/Doctrine/Node] gestisce i set nidificati. Potrebbe essere necessario costruire alcuni modelli fittizi per ottenere una buona occhiata al codice, specialmente con Propel. – prodigitalson

risposta

7

Modifica: Ok, ho esaminato questo un po 'di più. Penso che ci sia stato un errore nella nomenclatura. Non stai cercando uno data structure for a transveral tree in PHP. Si desidera utilizzare un albero come struttura dati in PHP e si desidera recuperare i dati da tale albero utilizzando un metodo denominato modified preorder tree traversal algorithm.

Quoting:

When working with a tree, we work from left to right, one layer at a time, descending to each node's children before assigning a right-hand number and moving on to the right. This approach is called the modified preorder tree traversal algorithm.


si tratta di memorizzazione di dati gerarchici in PHP vs MySQL. In PHP possiamo usare un albero semplice. Il problema è che non è facile memorizzare un albero nel database piatto che è MySQL. Un'opzione consiste nel prendere la lista PHP e recuperare e adiacenza da esso. Questo è essenzialmente un elenco di ogni oggetto e dei suoi genitori. Questo modo di fare le cose ha qualche svantaggio.

Un altro metodo consiste nell'estrarre informazioni dall'albero PHP che descrive gli insiemi nidificati che possono essere ricavati dai dati gerarchici. Per ottenere queste informazioni dall'albero PHP, è necessario utilizzare un algoritmo per l'attraversamento degli alberi preordinato modificato . Questo è un metodo di correre su e giù dall'albero per estrarre determinate informazioni da esso.

Sia che utilizziamo il modello dell'elenco di adiacenza o l'attraversamento dell'albero preordinato modificato per recuperare le informazioni, usiamo lo stesso albero PHP. La differenza diventa il modo in cui recuperiamo le informazioni dall'albero e come archiviamo le informazioni in MySQL.Il codice per come estrarre le informazioni da MySQL è già su the page you quoted. Per sincronizzare i dati tra PHP e MySQL devi solo usare le tecniche MySQL descritte su quella pagina e una classe di albero PHP.

Per questo, ho creato una classe in PHP che memorizza un albero. Usa un nodo. Ogni nodo può essere considerato come la radice di un albero completo, poiché da ciascun nodo è possibile accedere a una sottostruttura completa. Era semplicemente più semplice separare il nodo dall'albero e causare meno spese generali.

La parte importante della classe è il metodo showAdjacency. Questo esegue l'albero utilizzando una traversata dell'albero preordinata modificata e visualizza la quantità lft e rgt per ciascun nome che consente di memorizzare i dati in MySQL come un Set nidificato.

È anche possibile visualizzare l'albero, in modo che sia possibile visualizzarlo. Il metodo di eliminazione manca in questa classe. Quando lo si implementa, è necessario passare i figli del nodo eliminato al genitore del nodo. Forse lo farò dopo.

te lo comprendono l'intera classe in fondo al post, ma qui è come i dati vengono recuperati per l'albero di preorder modificato attraversamento:

 // The modified preorder tree traversal. 
    private function showAdjacency($root, $spaces) 
    { 
      // Print the data first 
     if ($root) 
     { 
       // On the way down the tree, we get lft. 
      $left = ++$spaces;     
      foreach($root->next as $child) 
      {      
       if ($child) 
       { 
        $spaces = $this->showAdjacency($child, $spaces);      
       } 
      } 
     } 
      // On the way back up, we get rgt. 
     $right = ++$spaces; 
     echo "$left - $right - $root->data <br/>";     
     return $spaces; 
    } 

È possibile, ovviamente, archiviare root-> Dati $, $ rgt e $ lft in un array che si utilizza per sincronizzarsi con il proprio database.

Ecco l'intera classe. Dopo la lezione, creo un albero utilizzando i dati di esempio da the page you linked to e restituisco i valori lft e rgt e la visualizzazione ad albero.

You can run the code on Codepad

<?php 
    // Class defintions and methods: 
    // It's easier to use separate nodes. Each node still points to an entire and complete subtree. 
class Node 
{ 
    public $data; 
    public $next = array(); 
} 

    // A first try at creating a tree with any number of branches from its nodes 
    // by Peter Ajtai - feel free to use and modify 
class Tree 
{ 
    // The root 
    private $root; 
    public function __construct() 
    { 
     $this->root = NULL; 
    } 

     // The public wrapper. 
     // This is needed because we must start the recursive functions 
     // at the root, and we do not want to give public access to root. 
     // I am not familiar w overloading in PHP, maybe __set should be used for this 
    public function insertPub($data, $parent) 
    { 
     $root =& $this->root; 
     $this->insert($root, $data, $parent); 
    } 

    private function insert(&$root, $data, $parent) 
    { 

      // Create the root of the entire tree if no parent is passed in   
     if (!$root && !$parent) 
     { 
      $root = new Node; 
      $temp =& $root; 
      $temp->data = $data; 
      // Create space for next insertion 
      $temp->next[] = NULL;      
     } else if ($root->data == $parent) 
     { 

       // Add data as a child if we're at the parent, and we're done. 
       // Find the empty node to insert at 
      foreach($root->next as &$child) 
      { 
        // Insert at the first (and only) NULL 
       if (!$child) 
       { 
        $child = new Node; 
        $temp =& $child; 
        $temp->data = $data;       
        // Create space for next insertion 
        $temp->next[] = NULL; 
       } 
      } 
       // Create space for the next insertion 
      $root->next[] = NULL; 
     } else 
     { 
       // Keep searching for the parent as default behavior. 
      foreach($root->next as $child) 
      { 
       if ($child) 
       { 
        $this->insert($child, $data, $parent); 
       } 
      } 
     } 
    } 
     // Public wrapper for the display function. 
    public function showAdjPub() 
    { 
     echo "The neighbors:<br/><br/>"; 
     $root =& $this->root; 
     $this->showAdjacency($root, 0); 
     echo "<br/>"; 
    } 

     // The display function. 
    private function showAdjacency($root, $spaces) 
    { 
      // Print the data first 
     if ($root) 
     { 
      $left = ++$spaces;     
      foreach($root->next as $child) 
      {      
       if ($child) 
       { 
        $spaces = $this->showAdjacency($child, $spaces);      
       } 
      } 
     } 
     $right = ++$spaces; 
     echo "$left - $right - $root->data <br/>";     
     return $spaces; 
    } 
     // Public wrapper for the display function. 
    public function showAllPub() 
    { 
     echo "The tree:<br/><br/>"; 
     $root =& $this->root; 
     $this->showAll($root, 0); 
     echo "<br/>"; 
    } 

     // The display function. 
    private function showAll($root, $spaces) 
    { 
      // Print the data first 
     if ($root) 
     { 
      for ($i=0; $i < $spaces; ++$i) 
       echo "---> "; 
      echo "$root->data <br/>"; 
      ++$spaces; 
      foreach($root->next as $child) 
      {      
       if ($child) 
       { 
        $this->showAll($child, $spaces); 
       } 
      } 
     } 
    }   
} 

    // Create a new instance of the tree 
$myTree = new Tree; 

    // Insert the data into the tree.  
    // The following data would be retrieved using MySQL 
$name = 'Electronics'; $parent = NULL; 
$myTree->insertPub($name, $parent); 
$name = 'Televisions'; $parent = 'Electronics'; 
$myTree->insertPub($name, $parent); 
$name = 'Tube'; $parent = 'Televisions'; 
$myTree->insertPub($name, $parent);  
$name = 'Lcd'; $parent = 'Televisions'; 
$myTree->insertPub($name, $parent); 
$name = 'Plasma'; $parent = 'Televisions'; 
$myTree->insertPub($name, $parent);  
$name = 'Portable Electronics'; $parent = 'Electronics'; 
$myTree->insertPub($name, $parent);  
$name = 'MP3 Players'; $parent = 'Portable Electronics'; 
$myTree->insertPub($name, $parent);  
$name = 'Flash'; $parent = 'MP3 Players'; 
$myTree->insertPub($name, $parent);  
$name = 'CD Players'; $parent = 'Portable Electronics'; 
$myTree->insertPub($name, $parent);  
$name = '2 Way Radios'; $parent = 'Portable Electronics'; 
$myTree->insertPub($name, $parent);  

    // Display adjacency 
$myTree->showAdjPub(); 

    // Show hierarchy  
$myTree->showAllPub();  
?> 

PS: La memorizzazione dei dati in PHP in matrici nidificate come da te suggerito sarebbe molto difficile. Nella classe precedente, se un membro dati viene eliminato, l'albero viene modificato (comprese le aggiunte di interi sottoalberi, ecc.) I valori lft e rgt verranno comunque recuperati correttamente.

Se si utilizzano gli array per archiviare le informazioni, sarà estremamente difficile eliminare elementi con genitori e figli e aggiornare il valore di lft e rgt sarebbe molto difficile. Infine, aggiungere serie di grandi dimensioni (sottoalberi) all'array sarebbe estremamente difficile.

Un albero è davvero il modo ideale per archiviare questo tipo di dati gerarchici. Imita le nostre nozioni di set. Il problema è che mentre PHP memorizza alberi con facilità, MySQL non lo fa, quindi dobbiamo passare attraverso tutto il difficile lavoro della traversata dell'albero preordinata modificata per estrarre le informazioni dall'albero PHP in modo che possiamo memorizzarle nel db MySQL.

+1

Un composito non sarebbe più appropriato quindi un nodo === Albero? – Wrikken

+0

@Wrikken è esattamente quello che stavo pensando - che ci sarebbe una funzionalità addNode() che accetta un albero. – user151841

+0

@Wrikken - In realtà è più doloroso, ma non usare un nodo separato. Ogni nodo è in realtà la radice di una sottostruttura separata. È solo più facile avere un sacco di istanze del nodo rispetto alla classe Tree. Ma teoricamente sono essenzialmente identici. –

0

Questo è il codice che ho usato per costruire un albero binario e le sue operazioni in PHP:

<?php 
class Node 
{ 
public $data; 
public $leftChild; 
public $rightChild; 

public function __construct($data) 
    { 
    $this->data=$data; 
    $this->leftChild=null; 
    $this->rightChild=null; 
    } 
public function disp_data() 
    { 
    echo $this->data; 
    } 


}//end class Node 
class BinaryTree 
{ 
public $root; 
//public $s; 
public function __construct() 
    { 
    $this->root=null; 
    //$this->s=file_get_contents('store'); 

    } 
//function to display the tree 
    public function display() 
    { 
    $this->display_tree($this->root); 

    } 
    public function display_tree($local_root) 
    { 

    if($local_root==null) 
    return; 
    $this->display_tree($local_root->leftChild); 
    echo $local_root->data."<br/>"; 
    $this->display_tree($local_root->rightChild); 

    } 
// function to insert a new node 
    public function insert($key) 
    { 
    $newnode=new Node($key); 
     if($this->root==null) 
     { 
     $this->root=$newnode; 
     return; 
     } 
     else 
     { 
     $parent=$this->root; 
     $current=$this->root; 
      while(true) 
      { 
       $parent=$current; 
       //$this->find_order($key,$current->data); 
       if($key==($this->find_order($key,$current->data))) 
        { 
         $current=$current->leftChild; 
         if($current==null) 
         { 
          $parent->leftChild=$newnode; 
          return; 
         }//end if2 
        }//end if1 
       else 
        { 
         $current=$current->rightChild; 
         if($current==null) 
         { 
          $parent->rightChild=$newnode; 
          return; 
         } //end if1      
        } //end else 
      }//end while loop 
     }//end else 

    } //end insert function 

//function to search a particular Node 
public function find($key) 
    { 
    $current=$this->root; 
    while($current->data!=$key) 
      { 
      if($key==$this->find_order($key,$current->data)) 
       { 
       $current=$current->leftChild; 
       } 
      else 
       { 
       $current=$current->rightChild; 
       } 
      if($current==null) 
       return(null); 

      } 
     return($current->data); 
    }// end the function to search 
public function delete1($key) 
    { 
    $current=$this->root; 
    $parent=$this->root; 

    $isLeftChild=true; 
    while($current->data!=$key) 
      { 
      $parent=$current; 
      if($key==($this->find_order($key,$current->data))) 
      { 
       $current=$current->leftChild; 
       $isLeftChild=true; 
      } 
      else 
      { 
       $current=$current->rightChild; 
       $isLeftChild=false; 
      } 
      if($current==null) 
       return(null); 
      }//end while loop 

     echo "<br/><br/>Node to delete:".$current->data; 
    //to delete a leaf node 
    if($current->leftChild==null&&$current->rightChild==null) 
     { 
      if($current==$this->root) 
       $this->root=null; 
      else if($isLeftChild==true) 
      { 
      $parent->leftChild=null; 
      } 
     else 
      { 
      $parent->rightChild=null; 
      } 
     return($current);  
     }//end if1 
    //to delete a node having a leftChild 
    else if($current->rightChild==null) 
     { 
      if($current==$this->root) 
      $this->root=$current->leftChild; 
      else if($isLeftChild==true) 
      { 
      $parent->leftChild=$current->leftChild; 
      } 
      else 
      { 
      $parent->rightChild=$current->leftChild; 
      } 
      return($current); 
     }//end else if1 
    //to delete a node having a rightChild 
    else if($current->leftChild==null) 
     { 
     if($current==$this->root) 
      $this->root=$current->rightChild; 
     else if($isLeftChild==true) 
      { 
      $parent->leftChild=$current->rightChild; 
      } 
     else 
      { 
      $parent->rightChild=$current->rightChild; 
      } 
      return($current); 
     } 
    //to delete a node having both childs 
    else 
     { 
     $successor=$this->get_successor($current); 
     if($current==$this->root) 
      { 
      $this->root=$successor; 

      } 
     else if($isLeftChild==true) 
      { 
      $parent->leftChild=$successor; 
      } 
     else 
      { 
      $parent->rightChild=$successor; 
      }  
     $successor->leftChild=$current->leftChild; 
     return($current); 
     } 


    }//end the function to delete a node 
//Function to find the successor node 
public function get_successor($delNode) 
    { 
    $succParent=$delNode; 
    $successor=$delNode; 
    $temp=$delNode->rightChild; 
    while($temp!=null) 
     { 
      $succParent=$successor; 
      $successor=$temp; 
      $temp=$temp->leftChild; 
     } 
    if($successor!=$delNode->rightChild) 
    { 
     $succParent->leftChild=$successor->rightChild; 
     $successor->rightChild=$delNode->rightChild; 
    } 
    return($successor); 
    } 
//function to find the order of two strings 
public function find_order($str1,$str2) 
    { 
    $str1=strtolower($str1); 
    $str2=strtolower($str2); 
    $i=0; 
    $j=0; 

    $p1=$str1[i]; 
    $p2=$str2[j]; 
    while(true) 
    { 
     if(ord($p1)<ord($p2)||($p1==''&&$p2=='')) 
     { 

      return($str1); 
     } 
     else 
     { 
      if(ord($p1)==ord($p2)) 
      { 
       $p1=$str1[++$i]; 
       $p2=$str2[++$j]; 
       continue; 
      } 
      return($str2); 
     } 
    }//end while 

    } //end function find string order 

public function is_empty() 
    { 
    if($this->root==null) 
     return(true); 
    else 
     return(false); 
    } 
}//end class BinaryTree 
?> 
1

Un semplice programma in esecuzione con il nodo e gli oggetti dell'albero.Senza ado ulteriori, onorevoli colleghi, qui è il codice:

<?php 
#Create a node class 
#----------------------------------------------------------------------------- 
class Node 
{ 
    #The node properties 
    public $value; 
    public $leftChild; 
    public $rightChild; 

    #Set the properties for the node 
    public function set_value($passedValue) 
    { 
     $this->value = $passedValue; 
    } 

    public function set_left_child($passedChild) 
    { 
     $this->leftChild = $passedChild; 
    } 

    public function set_right_child($passedChild) 
    { 
     $this->rightChild = $passedChild; 
    } 

    #Get the properties for the node 
    public function get_value() 
    { 
     return $this->value; 
    } 

    public function get_left_child() 
    { 
     return $this->leftChild; 
    } 

    public function get_right_child() 
    { 
     return $this->rightChild; 
    } 
} 





#Create a tree class with a function to transverse the node 
#----------------------------------------------------------------------------- 
class Tree 
{ 
    #The node properties 
    public $treeNodes = array(); 
    public $preorderedNodes = array(); 
    public $nodeArray = array(); 

    #Set the tree nodes from the passed array 
    public function set_tree_nodes($nodeArray) 
    { 
     $this->nodeArray = $nodeArray; 
     #Set each node with its children based on the passed array 
     foreach($this->nodeArray AS $node) array_push($this->treeNodes, $this->set_node_object($node)); 
    } 


    public function set_node_object($node) 
    { 
     $nodeObject = new Node(); 
     $nodeObject->set_value($node['value']); 
     if(!empty($node['left_child'])) $nodeObject->set_left_child($this->nodeArray[$node['left_child']]); 
     if(!empty($node['right_child'])) $nodeObject->set_right_child($this->nodeArray[$node['right_child']]); 

     return $nodeObject; 
    } 

    #perfom a preorder transversal on the tree and return the tree nodes in the expected order 
    public function do_preorder_transversal($node) 
    { 
     #If the node is empty, end the recursion 
     if(empty($node)) return $this->preorderedNodes; 
     #Start the transversal 
     if($node == 'head' && !empty($this->treeNodes[0])) $node=$this->treeNodes[0]; 

     #Add the node to the pre-ordered node list 
     array_push($this->preorderedNodes, $node); 

     $leftChild = $node->get_left_child(); 
     $rightChild = $node->get_right_child(); 
     if(!empty($leftChild)) $this->do_preorder_transversal($this->set_node_object($leftChild)); 
     if(!empty($rightChild)) $this->do_preorder_transversal($this->set_node_object($rightChild)); 
    } 


    #Get the preordered nodes 
    public function get_preordered_nodes() 
    { 
     return $this->preorderedNodes; 
    } 
} 









#----------------------------------------------------------------------------- 
# Test the objects 
#----------------------------------------------------------------------------- 
#Call the class in an example 
$sampleTree = new Tree(); 

$seedData = array(); 
#Seed data array is in the format [value, [key to left child], [key to right child]] 
$seedData[0] = array('value'=>2, 'left_child'=>1, 'right_child'=>2); 
$seedData[1] = array('value'=>7, 'left_child'=>3, 'right_child'=>4); 
$seedData[2] = array('value'=>5, 'left_child'=>'', 'right_child'=>7); 
$seedData[3] = array('value'=>2, 'left_child'=>'', 'right_child'=>''); 
$seedData[4] = array('value'=>6, 'left_child'=>5, 'right_child'=>6); 
$seedData[5] = array('value'=>5, 'left_child'=>'', 'right_child'=>''); 
$seedData[6] = array('value'=>11, 'left_child'=>'', 'right_child'=>''); 
$seedData[7] = array('value'=>9, 'left_child'=>8, 'right_child'=>''); 
$seedData[8] = array('value'=>4, 'left_child'=>'', 'right_child'=>''); 

$sampleTree->set_tree_nodes($seedData); 
#Create the root node to start the tree transversal 
$sampleTree->do_preorder_transversal('head'); 

#Now get the preordered nodes and print out their values 
$preordered = $sampleTree->get_preordered_nodes(); 
foreach($preordered AS $count=>$node) echo "<BR>[".$count."] ".$node->get_value(); 
?> 

I risultati sono i seguenti:

[0] 2

[1] 7

[2] 2

[3] 6

[4] 5

[5] 11

[6] 5

[7] 9

[8] 4

Problemi correlati