Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> Drzewko - podejscie iteracyjne
marcini82
post
Post #1





Grupa: Zarejestrowani
Postów: 190
Pomógł: 1
Dołączył: 20.05.2005
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Witam!

Zalozmy, ze mamy w tabeli bazy danych proste drzewko. Kazdy wiersz ma 3 pola: id, parent_id i name.
Odczytalismy te rekordy z bazy, mamy je w dwuwymiarowej tablicy asocjacyjnej i chcemy wyswietlic w formie drzewka:

Kod
level1_1
    level2_1
        level3_1
        level3_2
        level3_3
    level2_2
    level2_3
level1_2
    level2_4
level1_3


Drzewko moze miec dowolna ilosc poziomow zaglebienia.
Jesli sie uzywa rekurencji to proste. Ale czy da sie to zrobic iteracyjnie? A jesli sie da, to jak?
Zakladam, ze jesli sie da, to bedzie wydajniej. Czy sie nie myle?

Ten post edytował marcini82 13.09.2007, 12:37:51
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi
dr_bonzo
post
Post #2





Grupa: Przyjaciele php.pl
Postów: 5 724
Pomógł: 259
Dołączył: 13.04.2004
Skąd: N/A

Ostrzeżenie: (0%)
-----


marcini82: najpierw myslalem ze chodzi ci o rekurencje w samym php, a nie przy pobieraniu parentow z bazy, tak?

  1. <pre><?php
  2.  
  3. $records = array(
  4. array( 'id' => 1, 'parent_id' => null, 'name' => '1' ),
  5. array( 'id' => 3, 'parent_id' => null, 'name' => '3' ),
  6. array( 'id' => 2, 'parent_id' => null, 'name' => '2' ),
  7.  
  8. array( 'id' => 4, 'parent_id' => 1, 'name' => '1_1' ),
  9. array( 'id' => 5, 'parent_id' => 1, 'name' => '1_2' ),
  10. array( 'id' => 6, 'parent_id' => 1, 'name' => '1_3' ),
  11.  
  12. array( 'id' => 7, 'parent_id' => 2, 'name' => '2_1' ),
  13. array( 'id' => 8, 'parent_id' => 2, 'name' => '2_2' ),
  14.  
  15. array( 'id' => 9, 'parent_id' => 3, 'name' => '3_1' )
  16. );
  17.  
  18. $treeObject = createTreeObject( $records );
  19.  
  20. printTree( $treeObject );
  21.  
  22. function createTreeObject( $records )
  23. {
  24. $nodeMap = new NodeMap();
  25.  
  26. foreach ( $records as $record )
  27. {
  28. $nodeMap->addNode( new Node( $record['id'], $record['name'], $record['parent_id'] ) );
  29. }
  30.  
  31. return new Tree( $nodeMap->getTopLevelNodes() );
  32. }
  33.  
  34. function printTree( $tree )
  35. {
  36. $tree->printMe();
  37. }
  38.  
  39.  
  40. class Tree
  41. {
  42. private $topLevelNodes = array();
  43.  
  44. public function __construct( $nodes )
  45. {
  46. $this->topLevelNodes = $nodes;
  47. }
  48.  
  49. public function printMe()
  50. {
  51. foreach ( $this->topLevelNodes as $node )
  52. {
  53. $this->printNode( $node, 0 );
  54. }
  55. }
  56.  
  57. // rekurencja !!!
  58. private function printNode( $node, $level )
  59. {
  60. $str = "+" . str_repeat( '-', $level + 1 ) . " " . $node->getName();
  61. print( $str . "\n" );
  62.  
  63. foreach ( $node->getChildren() as $childNode )
  64. {
  65. $this->printNode( $childNode, $level + 1 );
  66. }
  67.  
  68. }
  69. }
  70.  
  71. class Node
  72. {
  73. private $children = array();
  74. private $id;
  75. private $name;
  76. private $parentId;
  77.  
  78. public function __construct( $id, $name, $parentId )
  79. {
  80. $this->id = $id;
  81. $this->name = $name;
  82. $this->parentId = $parentId;
  83. }
  84.  
  85. public function addChild( Node $child )
  86. {
  87. $this->children[] = $child;
  88. }
  89.  
  90. public function getChildren()
  91. {
  92. return $this->children;
  93. }
  94.  
  95. public function getName()
  96. {
  97. return $this->name;
  98. }
  99.  
  100. public function getParentId()
  101. {
  102. return $this->parentId;
  103. }
  104.  
  105. public function getId()
  106. {
  107. return $this->id;
  108. }
  109. }
  110.  
  111. class NodeMap
  112. {
  113. private $nodes = array();
  114.  
  115. private $nodesWaitingToBeAdded = array();
  116.  
  117. public function addNode( $node )
  118. {
  119. $this->nodes[ $node->getId() ] = $node;
  120.  
  121. $this->addToParentNode( $node );
  122. $this->addWaitingChildren( $node );
  123. }
  124.  
  125. private function addToParentNode( $node )
  126. {
  127. $parent = $this->findParent( $node );
  128. if ( $parent )
  129. {
  130. $parent->addChild( $node );
  131. }
  132. else
  133. {
  134. $this->markWaitingForParent( $node );
  135. }
  136. }
  137.  
  138. private function findParent( $node )
  139. {
  140. $parentId = $node->getParentId();
  141. $parentExists = isset( $this->nodes[ $parentId ] );
  142. if ( $parentExists )
  143. {
  144. return $this->nodes[ $parentId ];
  145. }
  146. else
  147. {
  148. return null;
  149. }
  150. }
  151.  
  152. private function markWaitingForParent( $node )
  153. {
  154. $thereIsArrayForNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getParentId() ] );
  155. if ( $thereIsArrayForNodesToBeAdded )
  156. {
  157. // add next item
  158. $this->nodesWaitingToBeAdded[ $node->getParentId() ][] = $node;
  159. }
  160. else
  161. {
  162. // create new array
  163. $this->nodesWaitingToBeAdded[ $node->getParentId() ] = array( $node );
  164. }
  165. }
  166.  
  167. private function addWaitingChildren( $node )
  168. {
  169. $thereAreNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getId() ] );
  170. if ( $thereAreNodesToBeAdded )
  171. {
  172. $children = $this->nodesWaitingToBeAdded[ $node->getId() ];
  173. foreach ( $children as $child )
  174. {
  175. $node->addChild( $child );
  176. }
  177. }
  178.  
  179. // remove these, so the only one left will be top level nodes
  180. unset( $this->nodesWaitingToBeAdded[ $node->getId() ] );
  181. }
  182.  
  183. public function getTopLevelNodes()
  184. {
  185. return $this->nodesWaitingToBeAdded[''];
  186. }
  187. }
  188. ?>
  189. </pre>


Co to robi? Przerabia rekordy na drzewko Node'ow i drukuje je. Zachowuje kolejnosc rekordow z bazy, tzn jesli 1_2 bylo przed 1_1 to tak zostana wyswietlone
Niestety jest rekurencja (bo nie chcialo mi sie rozpisywac) O takie cos chodzilo?
Zeby pozbyc sie tej rekurencji trzeba zasymulowac zachowanie stosu przy korzystaniu z rekurencji. Chyba zaraz to napisze (IMG:http://forum.php.pl/style_emoticons/default/smile.gif)

printMe // rekurencja
printMeWithUseOfIterations // iteracja, pewnie da sie przyspieszyc, ale juz mi sie nie chce (IMG:http://forum.php.pl/style_emoticons/default/biggrin.gif)


  1. <pre><?php
  2.  
  3. define( 'DEPTH', 10000);
  4. define( 'REPEAT', 100000);
  5.  
  6. $records = array(
  7. array( 'id' => 1, 'parent_id' => null, 'name' => '1' ),
  8. array( 'id' => 3, 'parent_id' => null, 'name' => '3' ),
  9. array( 'id' => 2, 'parent_id' => null, 'name' => '2' ),
  10.  
  11. array( 'id' => 4, 'parent_id' => 1, 'name' => '1_1' ),
  12. array( 'id' => 5, 'parent_id' => 1, 'name' => '1_2' ),
  13. array( 'id' => 6, 'parent_id' => 1, 'name' => '1_3' ),
  14.  
  15. array( 'id' => 7, 'parent_id' => 2, 'name' => '2_1' ),
  16. array( 'id' => 8, 'parent_id' => 2, 'name' => '2_2' ),
  17.  
  18. array( 'id' => 9, 'parent_id' => 3, 'name' => '3_1' )
  19. );
  20.  
  21. /*
  22.  
  23. glebokie drzewko
  24. $records = array();
  25.  
  26. $item = array('id' => 1, 'parent_id' => null, 'name' => '1' );
  27. for ( $j = 2; $j < DEPTH; $j++ )
  28. {
  29. $records[] = array( 'id' => $j, 'parent_id' => ($j - 1), 'name' => 'blah' );
  30. }
  31. */
  32.  
  33.  
  34.  
  35.  
  36. $treeObject = createTreeObject( $records );
  37.  
  38. printTree( $treeObject );
  39.  
  40. function createTreeObject( $records )
  41. {
  42. $nodeMap = new NodeMap();
  43.  
  44. foreach ( $records as $record )
  45. {
  46. $nodeMap->addNode( new Node( $record['id'], $record['name'], $record['parent_id'] ) );
  47. }
  48.  
  49. return new Tree( $nodeMap->getTopLevelNodes() );
  50. }
  51.  
  52. function printTree( $tree )
  53. {
  54. benchmark( $tree, 'printMe' );
  55. benchmark( $tree, 'printMeWithUseOfIterations' );
  56. }
  57.  
  58. function benchmark( $tree, $func )
  59. {
  60.  
  61. $start = microtime( true );
  62. for ( $i = 0; $i < REPEAT; $i++ )
  63. {
  64. $tree->$func();
  65. }
  66. $end = microtime( true );
  67.  
  68. print( "$func: " . ( $end - $start ) . "\n" );
  69. }
  70.  
  71.  
  72. class Tree
  73. {
  74. private $topLevelNodes = array();
  75.  
  76. public function __construct( $nodes )
  77. {
  78. $this->topLevelNodes = $nodes;
  79. }
  80.  
  81. public function printMeWithUseOfIterations()
  82. {
  83. $todoNodes = array_reverse( $this->topLevelNodes );
  84. $currentProcessedNodes = array();
  85.  
  86. $level = 0;
  87. while ( ! ( empty( $todoNodes ) && empty( $currentProcessedNodes ) ) )
  88. {
  89. if ( empty( $currentProcessedNodes ) )
  90. {
  91. // remove node from TODO and return it
  92. $firstNodeTodo = array_pop( $todoNodes );
  93. $currentProcessedNodes[] = $firstNodeTodo;
  94. }
  95.  
  96. $nextNode = array_pop( $currentProcessedNodes ); // the last one: remove and return
  97.  
  98. //print($nextNode->getName() . "\n" );
  99.  
  100. $reversedChildren = array_reverse( $nextNode->getChildren() );
  101. $currentProcessedNodes = array_merge( $currentProcessedNodes, $reversedChildren );
  102. }
  103. }
  104.  
  105. public function printMe()
  106. {
  107. foreach ( $this->topLevelNodes as $node )
  108. {
  109. $this->printNode( $node, 0 );
  110. }
  111. }
  112.  
  113. // rekurencja !!!
  114. private function printNode( $node, $level )
  115. {
  116. $str = "+" . str_repeat( '-', $level + 1 ) . " " . $node->getName();
  117. //print( $str . "\n" );
  118.  
  119. foreach ( $node->getChildren() as $childNode )
  120. {
  121. $this->printNode( $childNode, $level + 1 );
  122. }
  123.  
  124. }
  125. }
  126.  
  127. class Node
  128. {
  129. private $children = array();
  130. private $id;
  131. private $name;
  132. private $parentId;
  133.  
  134. public function __construct( $id, $name, $parentId )
  135. {
  136. $this->id = $id;
  137. $this->name = $name;
  138. $this->parentId = $parentId;
  139. }
  140.  
  141. public function addChild( Node $child )
  142. {
  143. $this->children[] = $child;
  144. }
  145.  
  146. public function getChildren()
  147. {
  148. return $this->children;
  149. }
  150.  
  151. public function getName()
  152. {
  153. return $this->name;
  154. }
  155.  
  156. public function getParentId()
  157. {
  158. return $this->parentId;
  159. }
  160.  
  161. public function getId()
  162. {
  163. return $this->id;
  164. }
  165. }
  166.  
  167. class NodeMap
  168. {
  169. private $nodes = array();
  170.  
  171. private $nodesWaitingToBeAdded = array();
  172.  
  173. public function addNode( $node )
  174. {
  175. $this->nodes[ $node->getId() ] = $node;
  176.  
  177. $this->addToParentNode( $node );
  178. $this->addWaitingChildren( $node );
  179. }
  180.  
  181. private function addToParentNode( $node )
  182. {
  183. $parent = $this->findParent( $node );
  184. if ( $parent )
  185. {
  186. $parent->addChild( $node );
  187. }
  188. else
  189. {
  190. $this->markWaitingForParent( $node );
  191. }
  192. }
  193.  
  194. private function findParent( $node )
  195. {
  196. $parentId = $node->getParentId();
  197. $parentExists = isset( $this->nodes[ $parentId ] );
  198. if ( $parentExists )
  199. {
  200. return $this->nodes[ $parentId ];
  201. }
  202. else
  203. {
  204. return null;
  205. }
  206. }
  207.  
  208. private function markWaitingForParent( $node )
  209. {
  210. $thereIsArrayForNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getParentId() ] );
  211. if ( $thereIsArrayForNodesToBeAdded )
  212. {
  213. // add next item
  214. $this->nodesWaitingToBeAdded[ $node->getParentId() ][] = $node;
  215. }
  216. else
  217. {
  218. // create new array
  219. $this->nodesWaitingToBeAdded[ $node->getParentId() ] = array( $node );
  220. }
  221. }
  222.  
  223. private function addWaitingChildren( $node )
  224. {
  225. $thereAreNodesToBeAdded = isset( $this->nodesWaitingToBeAdded[ $node->getId() ] );
  226. if ( $thereAreNodesToBeAdded )
  227. {
  228. $children = $this->nodesWaitingToBeAdded[ $node->getId() ];
  229. foreach ( $children as $child )
  230. {
  231. $node->addChild( $child );
  232. }
  233. }
  234.  
  235. // remove these, so the only one left will be top level nodes
  236. unset( $this->nodesWaitingToBeAdded[ $node->getId() ] );
  237. }
  238.  
  239. public function getTopLevelNodes()
  240. {
  241. $nodes = array();
  242. foreach ( $this->nodes as $node )
  243. {
  244. if ( is_null( $node->getParentId() ) )
  245. {
  246. $nodes[] = $node;
  247. }
  248. }
  249.  
  250. return $nodes;
  251. }
  252. }
  253. ?>
  254. </pre>





Wyszlo mi

dla glebokiego drzewka :
define( 'DEPTH', 10000); define( 'REPEAT', 100000);

printMe: 0.088973999023438
printMeWithUseOfIterations: 0.26687598228455

rekurencja rzadzi ? (IMG:http://forum.php.pl/style_emoticons/default/biggrin.gif)




dla "szerokiego"
define( 'REPEAT', 100000);

printMe: 3.2444250583649
printMeWithUseOfIterations: 3.3689439296722

bez roznicy
Go to the top of the page
+Quote Post

Posty w temacie


Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 10.10.2025 - 22:34