DoubleLinkedList.class.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. <?php
  2. /**
  3. * A Linked List data structure.
  4. * The structure allows iteration and array access. Note: when unsetting an array key,
  5. * the list is compressed and all subsequent keys are reset.
  6. *
  7. * @author Ironpilot
  8. * @copyright Copywrite (c) 2011, STAPLE CODE
  9. *
  10. * This file is part of the STAPLE Framework.
  11. *
  12. * The STAPLE Framework is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Lesser General Public License as published by the
  14. * Free Software Foundation, either version 3 of the License, or (at your option)
  15. * any later version.
  16. *
  17. * The STAPLE Framework is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  19. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
  20. * more details.
  21. *
  22. * You should have received a copy of the GNU Lesser General Public License
  23. * along with the STAPLE Framework. If not, see <http://www.gnu.org/licenses/>.
  24. */
  25. class Staple_Data_DoubleLinkedList implements Iterator, Countable, ArrayAccess
  26. {
  27. /**
  28. * Pointer to the starting list node
  29. * @var Staple_Data_LinkedListNodeDouble
  30. */
  31. protected $first;
  32. /**
  33. * Pointer to the current list node
  34. * @var Staple_Data_LinkedListNodeDouble
  35. */
  36. protected $current;
  37. /**
  38. * Pointer to the ending list node
  39. * @var Staple_Data_LinkedListNodeDouble
  40. */
  41. protected $last;
  42. /**
  43. * Size of the list
  44. * @var int
  45. */
  46. protected $size;
  47. /**
  48. * Name of the list. (optional)
  49. * @var string
  50. */
  51. protected $name;
  52. /**
  53. * Constructs the list and allows an optional list name to be set.
  54. * @param unknown_type $name
  55. */
  56. public function __construct($name = NULL)
  57. {
  58. //Sets the first and last pointers to null making a blank list.
  59. $this->first = $this->last = $this->current = null;
  60. $this->size = 0;
  61. //Set the Name
  62. if(isset($name))
  63. {
  64. $this->setName($name);
  65. }
  66. }
  67. /**
  68. * Calls the getListString function on string conversion.
  69. */
  70. public function __toString()
  71. {
  72. return $this->getListString();
  73. }
  74. /**
  75. * Returns the list size.
  76. * @return int
  77. */
  78. public function count()
  79. {
  80. return $this->getSize();
  81. }
  82. /**
  83. * Iteration. Retrieves the data for the current item.
  84. * @return mixed
  85. */
  86. public function current()
  87. {
  88. if(!is_null($this->current))
  89. {
  90. return $this->current->getData();
  91. }
  92. else
  93. {
  94. return NULL;
  95. }
  96. }
  97. /**
  98. * Iteration. Gets the key number for the current item in the list.
  99. * @return int
  100. */
  101. public function key()
  102. {
  103. return $this->findKey($this->current);
  104. }
  105. /**
  106. * Iteration. Forwards the internal pointer to the next item on the list.
  107. */
  108. public function next()
  109. {
  110. if(!is_null($this->current))
  111. {
  112. $this->current = $this->current->getNext();
  113. }
  114. }
  115. /**
  116. * Iteration. Rewinds the internal pointer to the first item in the list.
  117. */
  118. public function rewind()
  119. {
  120. $this->current = $this->first;
  121. }
  122. /**
  123. * Iteration. Checks that the current item pointed to is valid.
  124. */
  125. public function valid()
  126. {
  127. return !is_null($this->current);
  128. }
  129. /**
  130. * Array Access. Checks for a valid offset.
  131. */
  132. public function offsetExists($offset)
  133. {
  134. return !is_null($this->findItemByKey($offset));
  135. }
  136. /**
  137. * Array Access. Returns the data for a specified offset.
  138. */
  139. public function offsetGet($offset)
  140. {
  141. return $this->findItemByKey($offset)->getData();
  142. }
  143. /**
  144. * Array Access. Set the data for a specified offset.
  145. */
  146. public function offsetSet($offset, $value)
  147. {
  148. $item = $this->findItemByKey($offset);
  149. if($item instanceof Staple_Data_LinkedListNodeDouble)
  150. {
  151. $item->setData($value);
  152. }
  153. }
  154. /**
  155. * Array Access. Removes the node for a specified offset
  156. */
  157. public function offsetUnset($offset)
  158. {
  159. $prev = $this->findItemByKey($offset-1);
  160. $item = $this->findItemByKey($offset);
  161. $prev->setNext($item->getNext());
  162. }
  163. /**
  164. * Alias of addBack()
  165. * @param mixed $data
  166. */
  167. public function add($data)
  168. {
  169. return $this->addBack($data);
  170. }
  171. /**
  172. * Add a node to the beginning of the list.
  173. * @param mixed $data
  174. */
  175. public function addBefore($data, Staple_Data_LinkedListNodeDouble $beforeNode)
  176. {
  177. if($this->is_empty())
  178. {
  179. throw new Exception('Cannot add a node before another in a blank list.');
  180. }
  181. else
  182. {
  183. //Create the new node and insert it into the list.
  184. $new = new Staple_Data_LinkedListNodeDouble($data,$beforeNode, $beforeNode->prev);
  185. //Set the Surrounding Nodes
  186. if($beforeNode->prev != NULL)
  187. {
  188. $beforeNode->prev->setNext($new);
  189. }
  190. else
  191. {
  192. //Set the Current Node to the beginning of the list.
  193. $this->current = $this->first = $new;
  194. }
  195. $beforeNode->prev = $new;
  196. }
  197. //Increase List Size
  198. $this->size++;
  199. return $this;
  200. }
  201. /**
  202. * Add a node to the beginning of the list.
  203. * @param mixed $data
  204. */
  205. public function addFront($data)
  206. {
  207. if($this->is_empty())
  208. {
  209. $this->first = $this->last = new Staple_Data_LinkedListNodeDouble($data);
  210. }
  211. else
  212. {
  213. $new = new Staple_Data_LinkedListNodeDouble($data,$this->first);
  214. $this->first = $new;
  215. }
  216. //Increase List Size
  217. $this->size++;
  218. //Set the Current Node to the beginning of the list.
  219. $this->current = $this->first;
  220. return $this;
  221. }
  222. /**
  223. * Add a node to the end of the list
  224. * @param mixed $data
  225. */
  226. public function addBack($data)
  227. {
  228. //Adds a node to the end of the list.
  229. if($this->is_empty())
  230. {
  231. $this->first = $this->last = new Staple_Data_LinkedListNodeDouble($data);
  232. }
  233. else
  234. {
  235. $new = new Staple_Data_LinkedListNodeDouble($data);
  236. $new->prev = $this->last;
  237. $this->last->next = $new;
  238. $this->last = $new;
  239. }
  240. //Increase List Size
  241. $this->size++;
  242. //Set the Current Node to the end of the list.
  243. $this->current = $this->last;
  244. return $this;
  245. }
  246. /**
  247. * Alias of removeBack()
  248. * @return Staple_Data_LinkedListNodeDouble | false
  249. */
  250. public function remove()
  251. {
  252. return $this->removeBack();
  253. }
  254. /**
  255. * Removes the specified node and returns it's data
  256. * @param Staple_Data_LinkedListNodeDouble $node
  257. * @return boolean|Ambigous <the, mixed>
  258. */
  259. public function removeNode(Staple_Data_LinkedListNodeDouble $node)
  260. {
  261. if($this->is_empty())
  262. {
  263. $this->size = 0;
  264. return false;
  265. }
  266. elseif($node == $this->first && $node == $this->last)
  267. {
  268. $this->first = $this->last = $this->current = null;
  269. $this->size = 0;
  270. return $node->getData();
  271. }
  272. else
  273. {
  274. $next = $node->getNext();
  275. $prev = $node->getPrev();
  276. if($this->getCurrentNode() == $node && $node->prev != NULL)
  277. {
  278. //Set the Current Node to the previous node in the list.
  279. $this->current = $node->prev;
  280. //If there is no next node end the list with the previous node.
  281. if($node->next == NULL)
  282. {
  283. $this->last = $node->prev;
  284. }
  285. }
  286. else
  287. {
  288. //Set the Current Node & First Node to the next node in the list.
  289. $this->current = $this->first = $node->next;
  290. }
  291. //Cross-link the next and previous nodes together.
  292. if($prev != NULL)
  293. {
  294. $prev->setNext($next);
  295. if($next == NULL)
  296. {
  297. $this->last = $prev;
  298. }
  299. $this->current = $prev;
  300. }
  301. if($next != NULL)
  302. {
  303. $next->setPrev($prev);
  304. if($prev == NULL)
  305. {
  306. $this->first = $next;
  307. $this->current = $next;
  308. }
  309. }
  310. //Decrease List Size
  311. $this->size--;
  312. return $node->getData();
  313. }
  314. }
  315. /**
  316. * Removes a node from the front of the list.
  317. * Returns the node data on success or false on an empty list.
  318. * @return mixed
  319. */
  320. public function removeFront()
  321. {
  322. if($this->is_empty())
  323. {
  324. $this->size = 0;
  325. return false;
  326. }
  327. else
  328. {
  329. $node = $this->first;
  330. $this->first = $this->first->next;
  331. $this->size--;
  332. //Set the Current Node to the beginning of the list.
  333. $this->current = $this->first;
  334. return $node->getData();
  335. }
  336. }
  337. /**
  338. * Remove a node from the end of the list.
  339. * Returns the node data on removal or false if the list is empty.
  340. * @return mixed
  341. */
  342. public function removeBack()
  343. {
  344. if($this->is_empty())
  345. {
  346. $this->size = 0;
  347. return false;
  348. }
  349. elseif($this->first == $this->last)
  350. {
  351. $node = $this->first;
  352. $this->first = $this->last = $this->current = null;
  353. $this->size = 0;
  354. return $node->getData();
  355. }
  356. else
  357. {
  358. $current = $this->first;
  359. while($current->next != $this->last)
  360. {
  361. $current = $current->next;
  362. }
  363. $this->last = $current;
  364. $node = $current->next;
  365. $this->last->next = null;
  366. //Decrease List Size
  367. $this->size--;
  368. //Set the Current Node to the beginning of the list.
  369. $this->current = $this->last;
  370. return $node->getData();
  371. }
  372. }
  373. /**
  374. * Sets the optional Name of the list
  375. * @param string $name
  376. */
  377. public function setName($name)
  378. {
  379. $this->name = $name;
  380. return $this;
  381. }
  382. /**
  383. * Return the current node
  384. * @return Staple_Data_LinkedListNodeDouble|NULL
  385. */
  386. public function getCurrentNode()
  387. {
  388. if(!is_null($this->current))
  389. {
  390. return $this->current;
  391. }
  392. else
  393. {
  394. return NULL;
  395. }
  396. }
  397. /**
  398. * Gets the list name
  399. * @return string
  400. */
  401. public function getName()
  402. {
  403. return $this->name;
  404. }
  405. /**
  406. * @return int $size
  407. */
  408. public function getSize()
  409. {
  410. return (int)$this->size;
  411. }
  412. /**
  413. * Alias of getSize()
  414. */
  415. public function length()
  416. {
  417. return $this->getSize();
  418. }
  419. /**
  420. * Returns the list as a string. It converts arrays into a comma separated string.
  421. * Throws an exception if objects are encountered.
  422. * The Verbose option will also list the name of the list and its size.
  423. * @todo convert this to Iterator
  424. * @param bool $verbose
  425. * @throws Exception
  426. */
  427. public function getListString($verbose = false)
  428. {
  429. if($verbose)
  430. $lstring = "Name: {$this->name} \nSize: {$this->size} \n";
  431. else
  432. $lstring = "";
  433. $current = $this->first;
  434. while($current != null)
  435. {
  436. if(is_array($current->data))
  437. $lstring .= implode(",",$current->data)." \n";
  438. elseif(is_object($current->data))
  439. throw new Exception("Cannot interpret object as a string");
  440. else
  441. $lstring .= $current->data." \n";
  442. $current = $current->next;
  443. }
  444. return $lstring;
  445. }
  446. /**
  447. * Returns the list as an array
  448. * The Verbose option will also list the name of the list and its size.
  449. * Adding ArrayAccess Implementation deprecates this function.
  450. * @deprecated
  451. * @param bool $verbose
  452. */
  453. public function getListArray($verbose = false)
  454. {
  455. $larray = array();
  456. if($verbose)
  457. {
  458. $larray[-2] = $this->name;
  459. $larray[-1] = $this->size;
  460. }
  461. $current = $this->first;
  462. while($current != null)
  463. {
  464. $larray[] = $current->data;
  465. $current = $current->next;
  466. }
  467. return $larray;
  468. }
  469. /**
  470. * Checks the list to see if it is empty.
  471. */
  472. public function is_empty()
  473. {
  474. if($this->first == null && $this->last == null)
  475. {
  476. return true;
  477. }
  478. else
  479. {
  480. return false;
  481. }
  482. }
  483. /**
  484. * An internal troubleshooting test to see if the list is sizing correctly
  485. * @todo remove this function
  486. * @deprecated
  487. */
  488. private function check_size()
  489. {
  490. echo "List thinks it is {$this->size} item(s) long.<br>";
  491. $newSize = 0;
  492. $current = $this->first;
  493. while($current != null)
  494. {
  495. $newSize++;
  496. $current = $current->next;
  497. }
  498. echo "List currently contains $newSize node(s)";
  499. }
  500. /**
  501. * Returns the key for a specified node
  502. * @param Staple_Data_LinkedListNodeDouble $item
  503. */
  504. private function findKey(Staple_Data_LinkedListNodeDouble $item)
  505. {
  506. $counter = 0;
  507. $current = $this->first;
  508. while($item !== $current && $current instanceof Staple_Data_LinkedListNodeDouble)
  509. {
  510. $current = $current->getNext();
  511. $counter++;
  512. }
  513. return $counter;
  514. }
  515. /**
  516. * Returns the node for a specific key
  517. * @param int $key
  518. * @return Staple_Data_LinkedListNodeDouble
  519. */
  520. private function findItemByKey($key)
  521. {
  522. $current = $this->first;
  523. while($current != NULL && $key > 0)
  524. {
  525. $current = $current->getNext();
  526. $key--;
  527. }
  528. return $current;
  529. }
  530. }