├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── DataStructures ├── Exceptions │ ├── FullException.php │ └── NotFoundException.php ├── Lists │ ├── ArrayList.php │ ├── CircularLinkedList.php │ ├── DoublyLinkedList.php │ ├── Interfaces │ │ └── ListInterface.php │ ├── ListAbstract.php │ ├── Nodes │ │ ├── DoublyLinkedListNode.php │ │ └── SinglyLinkedListNode.php │ ├── Queue.php │ ├── SinglyLinkedList.php │ ├── Stack.php │ └── Traits │ │ └── ArrayAccessTrait.php ├── Sets │ ├── DisjointSet.php │ └── TreeSet.php └── Trees │ ├── AVLTree.php │ ├── BinarySearchTree.php │ ├── BinaryTreeAbstract.php │ ├── Interfaces │ ├── BinaryNodeInterface.php │ ├── ComparableInterface.php │ └── TreeInterface.php │ ├── Nodes │ ├── AVLNode.php │ ├── BSTNode.php │ ├── DisjointNode.php │ └── TrieNode.php │ ├── Traits │ └── CountTrait.php │ └── TrieTree.php ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml └── tests ├── Lists ├── ArrayListTest.php ├── CircularLinkedListTest.php ├── DoublyLinkedListTest.php ├── QueueTest.php ├── SinglyLinkedListTest.php └── StackTest.php ├── Sets └── DisjointSetTest.php ├── Trees ├── AVLTreeTest.php ├── BinarySearchTreeTest.php └── TrieTreeTest.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | # PHP folders and phar files 2 | /vendor/ 3 | /phpunit.phar 4 | /composer.lock 5 | /phpDocumentor.phar 6 | 7 | ### SublimeText ### 8 | # cache files for sublime text 9 | *.tmlanguage.cache 10 | *.tmPreferences.cache 11 | *.stTheme.cache 12 | 13 | # workspace files are user-specific 14 | *.sublime-workspace 15 | 16 | # project files should be checked into the repository, unless a significant 17 | # proportion of contributors will probably not be using SublimeText 18 | # *.sublime-project 19 | 20 | 21 | ### VisualStudioCode ### 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | ### WebStorm/IntelliJ ### 29 | /.idea 30 | modules.xml 31 | *.ipr 32 | 33 | 34 | ### System Files ### 35 | .DS_Store 36 | 37 | # Windows thumbnail cache files 38 | Thumbs.db 39 | ehthumbs.db 40 | ehthumbs_vista.db 41 | 42 | # Folder config file 43 | Desktop.ini 44 | 45 | # Recycle Bin used on file shares 46 | $RECYCLE.BIN/ 47 | 48 | # Thumbnails 49 | ._* 50 | 51 | # Files that might appear in the root of a volume 52 | .DocumentRevisions-V100 53 | .fseventsd 54 | .Spotlight-V100 55 | .TemporaryItems 56 | .Trashes 57 | .VolumeIcon.icns 58 | .com.apple.timemachine.donotpresent -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | php: 3 | code_rating: true 4 | duplication: true 5 | 6 | filter: 7 | excluded_paths: 8 | - "tests/" -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI configuration 2 | 3 | language: php 4 | 5 | dist: trusty 6 | 7 | php: 8 | - 7.0 9 | - 7.1 10 | 11 | before_script: 12 | - curl -s http://getcomposer.org/installer | php 13 | - php composer.phar install 14 | 15 | script: phpunit -------------------------------------------------------------------------------- /DataStructures/Exceptions/FullException.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class FullException extends Exception {} -------------------------------------------------------------------------------- /DataStructures/Exceptions/NotFoundException.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class NotFoundException extends Exception {} 22 | -------------------------------------------------------------------------------- /DataStructures/Lists/ArrayList.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class ArrayList extends ListAbstract { 25 | use ArrayAccessTrait; 26 | 27 | private $data; 28 | private $current; 29 | private $position; 30 | 31 | public function __construct() { 32 | $this->data = []; 33 | $this->size = 0; 34 | $this->position = 0; 35 | } 36 | 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | public function insert($index, $data) { 41 | array_splice($this->data, $index, 0, $data); 42 | $this->size++; 43 | } 44 | 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | protected function insertAt($index, $data) {} 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | protected function insertEnd($data) {} 54 | 55 | /** 56 | * {@inheritDoc} 57 | */ 58 | protected function insertBeginning($data) {} 59 | 60 | /** 61 | * Removes all the array items. 62 | */ 63 | public function clear() { 64 | $this->data = []; 65 | $this->size = 0; 66 | } 67 | 68 | /** 69 | * It execution time is O(1) because arrays in PHP are 70 | * hashtables. 71 | * {@inheritDoc} 72 | */ 73 | public function get($index) { 74 | if($index < 0 || $index > $this->size - 1) { 75 | throw new OutOfBoundsException(); 76 | } 77 | 78 | return $this->data[$index]; 79 | } 80 | 81 | protected function search($index) {} 82 | 83 | /** 84 | * {@inheritDoc} 85 | */ 86 | public function getLast() { 87 | if(!$this->empty()) { 88 | return $this->data[$this->size - 1]; 89 | } 90 | 91 | return null; 92 | } 93 | 94 | public function getAll() { 95 | foreach($this->data as $data) { 96 | yield $data; 97 | } 98 | } 99 | 100 | /** 101 | * {@inheritDoc} 102 | */ 103 | public function contains($data) : bool { 104 | return in_array($data, $this->data); 105 | } 106 | 107 | /** 108 | * {@inheritDoc} 109 | */ 110 | public function indexOf($data) { 111 | return array_search($data, $this->data, true); 112 | } 113 | 114 | /** 115 | * {@inheritDoc} 116 | */ 117 | public function lastIndexOf($data) { 118 | for($i = $this->size - 1; $i >= 0; $i--) { 119 | if($this->data[$i] === $data) { 120 | return $i; 121 | } 122 | } 123 | 124 | return false; 125 | } 126 | 127 | /** 128 | * {@inheritDoc} 129 | */ 130 | public function remove($data) { 131 | if($this->empty()) { 132 | throw new NotFoundException(); 133 | } 134 | 135 | $i = 0; 136 | while($i < $this->size) { 137 | if($this->data[$i] === $data) { 138 | $aux = $this->data[$i]; 139 | array_splice($this->data, $i, 1); 140 | $this->size--; 141 | return $aux; 142 | } 143 | $i++; 144 | } 145 | 146 | throw new NotFoundException(); 147 | } 148 | 149 | /** 150 | * Delete a node in the given position and returns it back. 151 | * 152 | * @param integer $index the position. 153 | * @throws OutOfBoundsException if index is negative 154 | * or is greater than the size of the list. 155 | */ 156 | public function delete($index) { 157 | if($this->size === 0 || $index < 0 || $index > $this->size - 1) { 158 | throw new OutOfBoundsException(); 159 | } 160 | 161 | $data = $this->data[$index]; 162 | array_splice($this->data, $index, 1); 163 | $this->size--; 164 | 165 | return $data; 166 | } 167 | 168 | /** 169 | * {@inheritDoc} 170 | */ 171 | protected function deleteBeginning() {} 172 | 173 | /** 174 | * {@inheritDoc} 175 | */ 176 | protected function deleteAt($index) {} 177 | 178 | /** 179 | * {@inheritDoc} 180 | */ 181 | protected function deleteEnd() {} 182 | 183 | /** 184 | * Returns array stored in the data attribute. 185 | * 186 | * @return array data stored in all nodes. 187 | */ 188 | public function toArray() : array { 189 | return $this->data; 190 | } 191 | 192 | /** 193 | * Reset the cursor position. 194 | */ 195 | public function rewind() { 196 | $this->position = 0; 197 | $this->current = $this->data[$this->position]; 198 | } 199 | 200 | /** 201 | * Returns the current node data. 202 | * 203 | * @return mixed 204 | */ 205 | public function current() { 206 | $this->current = $this->data[$this->position]; 207 | return $this->current; 208 | } 209 | 210 | /** 211 | * Key or index that indicates the cursor position. 212 | * 213 | * @return integer The current position. 214 | */ 215 | public function key() { 216 | return $this->position; 217 | } 218 | 219 | /** 220 | * Move the cursor to the next node and increments the 221 | * position counter. 222 | */ 223 | public function next() { 224 | ++$this->position; 225 | } 226 | 227 | /** 228 | * Returns if the current pointer position is valid. 229 | * 230 | * @return boolean true if pointer is not last, else false. 231 | */ 232 | public function valid() { 233 | return isset($this->data[$this->position]); 234 | } 235 | } -------------------------------------------------------------------------------- /DataStructures/Lists/CircularLinkedList.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class CircularLinkedList extends ListAbstract { 26 | use ArrayAccessTrait; 27 | protected $head; 28 | private $tail; 29 | private $current; 30 | private $position; 31 | 32 | public function __construct() { 33 | $this->head = null; 34 | $this->tail = &$this->head; 35 | $this->size = 0; 36 | $this->position = 0; 37 | $this->current = &$this->head; 38 | } 39 | 40 | 41 | /** 42 | * Inserts at the beginning of the list. 43 | * 44 | * @param mixed $data 45 | */ 46 | protected function insertBeginning($data) { 47 | $newNode = new SinglyLinkedListNode($data); 48 | if($this->head === null) { 49 | $newNode->next = &$this->head; 50 | $this->head = &$newNode; 51 | $this->tail = &$newNode; 52 | } else { 53 | $this->tail->next = &$newNode; 54 | $newNode->next = $this->head; 55 | $this->head = &$newNode; 56 | } 57 | } 58 | 59 | /** 60 | * Add a new node in the specified index. 61 | * 62 | * @param integer $index the position. 63 | * @param mixed $data the data to be stored. 64 | */ 65 | protected function insertEnd($data) { 66 | $newNode = new SinglyLinkedListNode($data); 67 | $this->tail->next = &$newNode; 68 | $newNode->next = &$this->head; 69 | $this->tail = &$newNode; 70 | } 71 | 72 | /** 73 | * Add a new node in the specified index. 74 | * 75 | * @param integer $index the position. 76 | * @param mixed $data the data to be stored. 77 | */ 78 | protected function insertAt($index, $data) { 79 | $newNode = new SinglyLinkedListNode($data); 80 | $current = $this->head; 81 | $prev = null; 82 | $i = 0; 83 | while($i < $index) { 84 | $prev = $current; 85 | $current = $current->next; 86 | $i++; 87 | } 88 | 89 | $prev->next = &$newNode; 90 | $newNode->next = &$current; 91 | } 92 | 93 | /** 94 | * Returns the last node with O(1). 95 | * 96 | * @return mixed null if the list is empty. 97 | */ 98 | protected function searchLast() { 99 | if($this->head === null) { 100 | return null; 101 | } 102 | 103 | return $this->tail; 104 | } 105 | 106 | /** 107 | * Returns the node stored in the given position. 108 | * If index is 0 or (size - 1) the method is O(1) else O(n). 109 | * 110 | * @param integer $index the position. 111 | * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1) 112 | * @return DataStructures\Lists\Nodes\SinglyLinkedListNode the node stored in $index. 113 | */ 114 | protected function search($index) { 115 | if($index < 0 || $index > $this->size - 1) { 116 | throw new OutOfBoundsException(); 117 | } 118 | 119 | if($index === 0) { 120 | return $this->head; 121 | } 122 | 123 | if($index === $this->size - 1) { 124 | return $this->searchLast(); 125 | } 126 | 127 | $i = 0; 128 | $current = $this->head; 129 | while($i < $index) { 130 | $current = $current->next; 131 | $i++; 132 | } 133 | 134 | return $current; 135 | } 136 | 137 | /** 138 | * {@inheritDoc} 139 | */ 140 | public function contains($data) : bool { 141 | if($this->size === 0) { 142 | return false; 143 | } 144 | 145 | $current = $this->head->next; 146 | $prev = $this->head; 147 | while($current !== $this->head) { 148 | if($prev->data === $data) { 149 | return true; 150 | } 151 | 152 | $prev = $current; 153 | $current = $current->next; 154 | } 155 | 156 | if($prev->data === $data) { 157 | return true; 158 | } 159 | 160 | return false; 161 | } 162 | 163 | /** 164 | * {@inheritDoc} 165 | */ 166 | public function indexOf($data) { 167 | if($this->head === null) { 168 | return false; 169 | } 170 | 171 | $current = $this->head; 172 | $i = 0; 173 | 174 | while($i < $this->size) { 175 | if($current->data === $data) { 176 | return $i; 177 | } 178 | 179 | $current = $current->next; 180 | $i++; 181 | } 182 | 183 | return false; 184 | } 185 | 186 | /** 187 | * {@inheritDoc} 188 | */ 189 | public function lastIndexOf($data) { 190 | if($this->head === null) { 191 | return false; 192 | } 193 | 194 | $current = $this->head; 195 | $i = 0; 196 | $pos = false; 197 | while($i < $this->size) { 198 | if($current->data === $data) { 199 | $pos = $i; 200 | } 201 | 202 | $current = $current->next; 203 | $i++; 204 | } 205 | 206 | return $pos; 207 | } 208 | 209 | /** 210 | * {@inheritDoc} 211 | */ 212 | public function remove($data) { 213 | $current = $this->head; 214 | $prev = $this->tail; 215 | $i = 0; 216 | 217 | if($this->head === null) { 218 | throw new NotFoundException(); 219 | } 220 | 221 | if($this->head->data === $data) { 222 | $this->head = &$this->head->next; 223 | $this->tail->next = &$this->head; 224 | $this->size--; 225 | return $current->data; 226 | } 227 | 228 | while($i < $this->size) { 229 | if($prev->data === $data) { 230 | $prev->next = &$current->next; 231 | $this->size--; 232 | 233 | return $current->data; 234 | } 235 | 236 | $prev = $current; 237 | $current = $current->next; 238 | } 239 | 240 | throw new NotFoundException(); 241 | } 242 | 243 | /** 244 | * Generator for retrieve all nodes stored. 245 | * 246 | * @return null if the head is null (or list is empty) 247 | */ 248 | public function getAll() { 249 | if($this->head === null) { 250 | return; 251 | } 252 | 253 | if($this->head->next === $this->tail) { 254 | yield $this->head->data; 255 | } else { 256 | $current = $this->head; 257 | $i = 0; 258 | while($i < $this->size) { 259 | yield $current->data; 260 | $current = $current->next; 261 | $i++; 262 | } 263 | } 264 | } 265 | 266 | /** 267 | * {@inheritDoc} 268 | */ 269 | protected function deleteBeginning() { 270 | // if only there is an element 271 | if($this->head->next === $this->head) { 272 | $temp = $this->head; 273 | $this->head = null; 274 | return $temp->data; 275 | } 276 | 277 | $temp = $this->head; 278 | $this->head = &$this->head->next; 279 | $this->tail->next = &$this->head; 280 | 281 | return $temp->data; 282 | } 283 | 284 | /** 285 | * {@inheritDoc} 286 | */ 287 | protected function deleteAt($index) { 288 | $i = 0; 289 | $prev = $this->head; 290 | $current = $this->head; 291 | 292 | while($i < $index) { 293 | $prev = $current; 294 | $current = $current->next; 295 | $i++; 296 | } 297 | 298 | $prev->next = &$current->next; 299 | 300 | return $current->data; 301 | } 302 | 303 | /** 304 | * {@inheritDoc} 305 | */ 306 | protected function deleteEnd() { 307 | $prev = $this->head; 308 | $current = $this->head; 309 | 310 | while($current !== $this->tail) { 311 | $prev = $current; 312 | $current = $current->next; 313 | } 314 | 315 | $temp = $current; 316 | $prev->next = &$this->head; 317 | $this->tail = &$prev; 318 | $current = null; 319 | 320 | return $temp->data; 321 | } 322 | 323 | public function clear() { 324 | while($this->head !== null) { 325 | $this->shift(); 326 | } 327 | } 328 | 329 | /** 330 | * Reset the cursor position. 331 | */ 332 | public function rewind() { 333 | $this->position = 0; 334 | $this->current = &$this->head; 335 | } 336 | 337 | /** 338 | * Returns the current node data. 339 | * 340 | * @return mixed 341 | */ 342 | public function current() { 343 | return $this->current->data; 344 | } 345 | 346 | /** 347 | * Key or index that indicates the cursor position. 348 | * 349 | * @return integer The current position. 350 | */ 351 | public function key() { 352 | return $this->position; 353 | } 354 | 355 | /** 356 | * Move the cursor to the next node and increments the 357 | * position counter. 358 | */ 359 | public function next() { 360 | ++$this->position; 361 | $this->current = $this->current->next; 362 | } 363 | 364 | /** 365 | * Returns if the current pointer position is valid. 366 | * 367 | * @return boolean true if pointer is not last, else false. 368 | */ 369 | public function valid() { 370 | return $this->position < $this->size; 371 | } 372 | } -------------------------------------------------------------------------------- /DataStructures/Lists/DoublyLinkedList.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class DoublyLinkedList extends ListAbstract { 26 | use ArrayAccessTrait; 27 | 28 | protected $head; 29 | private $tail; 30 | private $position; 31 | private $current; 32 | 33 | public function __construct() { 34 | $this->head = null; 35 | $this->tail = &$this->head; 36 | $this->size = 0; 37 | $this->position = 0; 38 | $this->current = &$this->head; 39 | } 40 | 41 | /** 42 | * Gets the node stored in the position especified. 43 | * If index is 0 or (size - 1) the method is O(1) else O(n). 44 | * 45 | * @param integer $index the position. 46 | * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1) 47 | * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null the node stored in $index. 48 | */ 49 | protected function search($index) { 50 | if($index < 0 || $index > $this->size - 1) { 51 | throw new OutOfBoundsException(); 52 | } 53 | 54 | if($index === 0) { 55 | return $this->head; 56 | } 57 | 58 | if($index === $this->size - 1) { 59 | return $this->tail; 60 | } 61 | 62 | $current = &$this->head; 63 | if($index < (int) ceil($this->size / 2)) { 64 | $i = 0; 65 | while($i < $index) { 66 | $current = &$current->next; 67 | $i++; 68 | } 69 | } else { 70 | $i = $this->size; 71 | while($i > $index) { 72 | $current = &$current->prev; 73 | $i--; 74 | } 75 | } 76 | 77 | return $current; 78 | } 79 | 80 | /** 81 | * Returns the last node with O(1). 82 | * 83 | * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null if the list is empty. 84 | */ 85 | public function searchLast() { 86 | if($this->head === null) { 87 | return null; 88 | } 89 | return $this->tail; 90 | } 91 | 92 | /** 93 | * {@inheritDoc} 94 | */ 95 | public function contains($data) : bool { 96 | if($this->empty()) { 97 | return false; 98 | } 99 | 100 | $current = $this->head->next; 101 | $prev = $this->head; 102 | while($current !== $this->head) { 103 | if($prev->data === $data) { 104 | return true; 105 | } 106 | $prev = $current; 107 | $current = $current->next; 108 | } 109 | 110 | return $prev->data === $data; 111 | } 112 | 113 | /** 114 | * {@inheritDoc} 115 | */ 116 | public function indexOf($data) { 117 | if($this->head === null) { 118 | return false; 119 | } 120 | 121 | $current = $this->head; 122 | $i = 0; 123 | 124 | while($i < $this->size) { 125 | if($current->data === $data) { 126 | return $i; 127 | } 128 | 129 | $current = $current->next; 130 | $i++; 131 | } 132 | 133 | return false; 134 | } 135 | 136 | /** 137 | * {@inheritDoc} 138 | */ 139 | public function lastIndexOf($data) { 140 | if($this->head === null) { 141 | return false; 142 | } 143 | 144 | $current = $this->head; 145 | $i = 0; 146 | $pos = false; 147 | while($i < $this->size) { 148 | if($current->data == $data) { 149 | $pos = $i; 150 | } 151 | 152 | $current = $current->next; 153 | $i++; 154 | } 155 | 156 | return $pos; 157 | } 158 | 159 | /** 160 | * {@inheritDoc} 161 | */ 162 | public function remove($data) { 163 | $current = &$this->head; 164 | $i = 0; 165 | 166 | if($this->head === null) { 167 | throw new NotFoundException(); 168 | } 169 | 170 | if($this->head->data === $data) { 171 | $this->head = &$this->head->next; 172 | $this->head->prev = &$this->tail; 173 | $this->size--; 174 | 175 | return $data; 176 | } 177 | 178 | while($i < $this->size) { 179 | if($current->data === $data) { 180 | $current->prev = &$current->next; 181 | $current = null; 182 | $this->size--; 183 | return $data; 184 | } 185 | 186 | $current = $current->next; 187 | } 188 | 189 | throw new NotFoundException(); 190 | } 191 | 192 | /** 193 | * Generator for retrieve all nodes stored. 194 | * 195 | * @return null if the head is null (or list is empty) 196 | */ 197 | public function getAll() { 198 | if($this->head === null) { 199 | return; 200 | } 201 | 202 | if($this->head === $this->tail) { 203 | yield $this->head->data; 204 | } else { 205 | $current = $this->head; 206 | $i = 0; 207 | while($i < $this->size) { 208 | yield $current->data; 209 | $current = $current->next; 210 | $i++; 211 | } 212 | } 213 | } 214 | 215 | /** 216 | * {@inheritDoc} 217 | */ 218 | protected function insertBeginning($data) { 219 | $newNode = new DoublyLinkedListNode($data); 220 | if($this->head === null) { 221 | $newNode->next = &$this->head; 222 | $newNode->prev = &$this->head; 223 | $this->head = &$newNode; 224 | $this->tail = &$newNode; 225 | } else { 226 | $this->tail->next = &$newNode; 227 | $newNode->next = &$this->head; 228 | $newNode->prev = &$this->tail; 229 | $this->head = &$newNode; 230 | } 231 | } 232 | 233 | protected function insertEnd($data) { 234 | $newNode = new DoublyLinkedListNode($data); 235 | $this->tail->next = &$newNode; 236 | $newNode->next = &$this->head; 237 | $newNode->prev = &$this->tail; 238 | $this->tail = &$newNode; 239 | $this->head->prev = &$newNode; 240 | } 241 | 242 | /** 243 | * Add a new node in the specified index. 244 | * 245 | * @param integer $index the position. 246 | * @param mixed $data the data to be stored. 247 | */ 248 | protected function insertAt($index, $data) { 249 | $newNode = new DoublyLinkedListNode($data); 250 | $current = $this->head; 251 | $prev = null; 252 | $i = 0; 253 | while($i < $index) { 254 | $prev = $current; 255 | $current = $current->next; 256 | $i++; 257 | } 258 | 259 | $prev->next = &$newNode; 260 | $newNode->prev = &$prev; 261 | $newNode->next = &$current; 262 | $current->prev = &$newNode; 263 | } 264 | 265 | /** 266 | * Deletes at the beginnig of the list and returns the data stored. 267 | * 268 | * @return mixed the data stored in the node. 269 | */ 270 | protected function deleteBeginning() { 271 | // if only there is an element 272 | if($this->head->next === $this->head) { 273 | $temp = $this->head; 274 | $this->head = null; 275 | } else { 276 | $temp = $this->head; 277 | $this->head = &$this->head->next; 278 | $this->tail->next = &$this->head; 279 | 280 | } 281 | return $temp->data; 282 | } 283 | 284 | /** 285 | * Deletes at the specified position and returns the data stored. 286 | * 287 | * @param integer $index the position. 288 | * @return mixed the data stored in the node. 289 | */ 290 | protected function deleteAt($index) { 291 | $i = 0; 292 | $prev = $this->head; 293 | $current = $this->head; 294 | 295 | while($i < $index) { 296 | $prev = $current; 297 | $current = $current->next; 298 | $i++; 299 | } 300 | 301 | $temp = $current; 302 | $prev->next = &$current->next; 303 | $current->next->prev = &$pre; 304 | $current = null; 305 | 306 | return $temp->data; 307 | } 308 | 309 | /** 310 | * Deletes at the end of the list and returns the data stored. 311 | * 312 | * @return mixed the data stored in the node. 313 | */ 314 | protected function deleteEnd() { 315 | $prev = $this->head; 316 | $current = $this->head; 317 | 318 | while($current !== $this->tail) { 319 | $prev = $current; 320 | $current = $current->next; 321 | } 322 | 323 | $temp = $current; 324 | $prev->next = &$this->head; 325 | $this->head->prev = &$prev; 326 | $this->tail = &$prev; 327 | $current = null; 328 | 329 | return $temp->data; 330 | } 331 | 332 | /** 333 | * Reset the cursor position. 334 | */ 335 | public function rewind() { 336 | $this->position = 0; 337 | $this->current = &$this->head; 338 | } 339 | 340 | /** 341 | * Returns the current node data. 342 | * 343 | * @return mixed 344 | */ 345 | public function current() { 346 | return $this->current->data; 347 | } 348 | 349 | /** 350 | * Key or index that indicates the cursor position. 351 | * 352 | * @return integer The current position. 353 | */ 354 | public function key() { 355 | return $this->position; 356 | } 357 | 358 | /** 359 | * Move the cursor to the next node and increments the 360 | * position counter. 361 | */ 362 | public function next() { 363 | ++$this->position; 364 | $this->current = $this->current->next; 365 | } 366 | 367 | /** 368 | * Returns if the current pointer position is valid. 369 | * 370 | * @return boolean true if pointer is not last, else false. 371 | */ 372 | public function valid() { 373 | return $this->position < $this->size; 374 | } 375 | } -------------------------------------------------------------------------------- /DataStructures/Lists/Interfaces/ListInterface.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | interface ListInterface extends ArrayAccess, Iterator, Countable { 23 | public function insert($index, $data); 24 | // public function addAll(array $data, $index = 0); 25 | // Removes all of the elements from this list (optional operation). 26 | public function clear(); 27 | /** 28 | * Returns true if this list contains the specified element. 29 | * It is a O(n) complexity. 30 | * 31 | * @param mixed $data The data to search. 32 | * @return boolean true if is contained, else false. 33 | */ 34 | public function contains($data) : bool; 35 | 36 | public function get($index); 37 | public function getAll(); 38 | 39 | /** 40 | * Depending the type of the list it returns the last node with O(1) 41 | * in case of doubly linked list, and in case of simply linked list in O(N). 42 | * 43 | * @return mixed null if the array is empty. 44 | */ 45 | public function getLast(); 46 | 47 | /** 48 | * Returns the index of the first occurrence of the specified element in this list, 49 | * or false if this list does not contain the element. 50 | * It is a O(n) complexity. 51 | * 52 | * @param mixed $data The data used to compare. 53 | * @return mixed Returns a boolean (false) if there is no matches. 54 | * Else return a value between 0 and the list length minus one. 55 | */ 56 | public function indexOf($data); 57 | public function empty() : bool; 58 | 59 | /** 60 | * Returns the index of the last occurrence of the specified element in this list, 61 | * or false if this list does not contain the element. 62 | * It is a O(n) complexity. 63 | * 64 | * @param mixed $data The data used to compare. 65 | * @return mixed Returns a boolean (false) if there is no matches. 66 | * Else return a value between 0 and the list length minus one. 67 | */ 68 | public function lastIndexOf($data); 69 | public function delete($index); 70 | 71 | /** 72 | * Removes the first occurrence of the specified element from this list, 73 | * if it is present (optional operation). 74 | * 75 | * @param mixed $data The data used to compare. 76 | * @throws DataStructures\Exceptions\NotFoundException when empty or not found. 77 | * @return the element removed. 78 | */ 79 | public function remove($data); 80 | // Removes from this list all of its elements that are contained in the specified collection (optional operation). 81 | // public function removeAll(ListInterface $list); 82 | // Removes from this list all of its elements that are contained in the specified collection (optional operation). 83 | // public function deleteAll(ListInterface $list); 84 | // public function set($index, $newValue); 85 | // Replaces the element at the specified position in this list with the specified element (optional operation). 86 | public function size() : int; 87 | // Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. 88 | // List subList(int fromIndex, int toIndex) 89 | public function toArray() : array; 90 | } -------------------------------------------------------------------------------- /DataStructures/Lists/ListAbstract.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | abstract class ListAbstract implements ListInterface { 24 | protected $size; 25 | 26 | /** 27 | * Insert a node in the specified list position. 28 | * 29 | * @param integer $index position 30 | * @param mixed $data data to be saved 31 | */ 32 | public function insert($index, $data) { 33 | if($index < 0) { 34 | throw new OutOfBoundsException(); 35 | } 36 | 37 | if($index === 0) { 38 | $this->insertBeginning($data); 39 | } else if($index >= $this->size) { 40 | $this->insertEnd($data); 41 | } else if($index > 0 && $index < $this->size) { 42 | $this->insertAt($index, $data); 43 | } 44 | 45 | $this->size++; 46 | } 47 | 48 | /** 49 | * Add a new node in the specified index. 50 | * 51 | * @param integer $index the position. 52 | * @param mixed $data the data to be stored. 53 | */ 54 | protected abstract function insertAt($index, $data); 55 | 56 | /** 57 | * Add a new node in the specified index. 58 | * 59 | * @param mixed $data the data to be stored. 60 | */ 61 | protected abstract function insertEnd($data); 62 | 63 | /** 64 | * Inserts at the beginning of the list. 65 | * 66 | * @param mixed $data 67 | */ 68 | protected abstract function insertBeginning($data); 69 | 70 | /** 71 | * Removes all nodes of the list. It removes from the beginning. 72 | */ 73 | public function clear() { 74 | while($this->head !== null) { 75 | $this->shift(); 76 | } 77 | } 78 | 79 | /** 80 | * Binds to count() method. This is equal to make $this->tree->size(). 81 | * 82 | * @return integer the tree size. 0 if it is empty. 83 | */ 84 | public function count() { 85 | return $this->size; 86 | } 87 | 88 | /** 89 | * Returns the array size. 90 | * 91 | * @return int the length 92 | */ 93 | public function size() : int { 94 | return $this->size; 95 | } 96 | 97 | /** 98 | * Checks if the list is empty. 99 | * 100 | * @return boolean true if is empty, else false. 101 | */ 102 | public function empty() : bool { 103 | return $this->size === 0; 104 | } 105 | 106 | /** 107 | * Adds at the end of the list new node containing 108 | * the data to be stored. 109 | * 110 | * @param mixed $data The data 111 | */ 112 | public function push($data) { 113 | $this->insert($this->size, $data); 114 | } 115 | 116 | /** 117 | * Adds at the beginning a node in the list. 118 | * 119 | * @param mixed $data 120 | * @return mixed the data stored. 121 | */ 122 | public function unshift($data) { 123 | $this->insert(0, $data); 124 | } 125 | 126 | /** 127 | * Deletes the first node of the list and returns it. 128 | * 129 | * @return mixed the data. 130 | */ 131 | public function shift() { 132 | return $this->delete(0); 133 | } 134 | 135 | /** 136 | * Removes and returns the last node in the list. 137 | * 138 | * @return mixed data in node. 139 | */ 140 | public function pop() { 141 | return $this->delete($this->size - 1); 142 | } 143 | 144 | /** 145 | * Delete a node in the given position and returns it back. 146 | * 147 | * @param integer $index the position. 148 | * @throws OutOfBoundsException if index is negative 149 | * or is greater than the size of the list. 150 | */ 151 | public function delete($index) { 152 | if($index < 0 || ($index > 0 && $index > $this->size - 1)) { 153 | throw new OutOfBoundsException(); 154 | } 155 | 156 | // if the list is empty 157 | if($this->empty()) { 158 | return null; 159 | } 160 | 161 | $nodeData = null; 162 | if($index === 0) { 163 | $nodeData = $this->deleteBeginning(); 164 | } else if($index === $this->size - 1) { 165 | $nodeData = $this->deleteEnd(); 166 | } else { 167 | $nodeData = $this->deleteAt($index); 168 | } 169 | 170 | $this->size--; 171 | return $nodeData; 172 | } 173 | 174 | /** 175 | * Deletes at the beginnig of the list and returns the data stored. 176 | * 177 | * @return mixed the data stored in the node. 178 | */ 179 | protected abstract function deleteBeginning(); 180 | 181 | /** 182 | * Deletes at the specified position and returns the data stored. 183 | * 184 | * @param integer $index the position. 185 | * @return mixed the data stored in the node. 186 | */ 187 | protected abstract function deleteAt($index); 188 | 189 | /** 190 | * Deletes at the end of the list and returns the data stored. 191 | * 192 | * @return mixed the data stored in the node. 193 | */ 194 | protected abstract function deleteEnd(); 195 | 196 | /** 197 | * Converts/exports the list content into array type. 198 | * 199 | * @return array data stored in all nodes. 200 | */ 201 | public function toArray() : array { 202 | $arr = []; 203 | foreach($this->getAll() as $node) { 204 | $arr[] = $node; 205 | } 206 | 207 | return $arr; 208 | } 209 | 210 | /** 211 | * Gets the data stored in the position especified. 212 | * 213 | * @param integer $index Index that must be greater than 0 214 | * and lower than the list size. 215 | * @return mixed The data stored in the given index 216 | * @throws OutOfBoundsException if index is out bounds. 217 | */ 218 | public function get($index) { 219 | $node = $this->search($index); 220 | 221 | return $node->data; 222 | } 223 | 224 | /** 225 | * {@inheritDoc} 226 | */ 227 | public function getLast() { 228 | $lastNode = $this->searchLast(); 229 | return $lastNode === null ? null : $lastNode->data; 230 | } 231 | 232 | /** 233 | * Gets the node stored in the position especified. 234 | * If index is 0 or (size - 1) the method is O(1) else O(n). 235 | * 236 | * @param integer $index the position. 237 | * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1) 238 | * @return DataStructures\Lists\Nodes\SimpleLinkedListNode|null the node stored in $index. 239 | */ 240 | protected abstract function search($index); 241 | } -------------------------------------------------------------------------------- /DataStructures/Lists/Nodes/DoublyLinkedListNode.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class DoublyLinkedListNode extends SinglyLinkedListNode { 19 | public $prev = null; 20 | } -------------------------------------------------------------------------------- /DataStructures/Lists/Nodes/SinglyLinkedListNode.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class SinglyLinkedListNode { 17 | public $data = null; 18 | public $next = null; 19 | 20 | public function __construct($data) { 21 | $this->data = $data; 22 | } 23 | } -------------------------------------------------------------------------------- /DataStructures/Lists/Queue.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Queue implements Countable { 26 | private $head; 27 | private $tail; 28 | private $size; 29 | private $maxSize; 30 | 31 | public function __construct($maxSize = 0) { 32 | if($maxSize < 0) { 33 | throw new InvalidArgumentException(); 34 | } 35 | $this->maxSize = $maxSize; 36 | $this->head = null; 37 | $this->tail = &$this->head; 38 | $this->size = 0; 39 | } 40 | 41 | /** 42 | * Returns the queue size. 43 | * 44 | * @return int the length 45 | */ 46 | public function size() : int { 47 | return $this->size; 48 | } 49 | 50 | /** 51 | * Checks if the queue is empty. 52 | * 53 | * @return bool true if is empty, else false. 54 | */ 55 | public function empty() : bool { 56 | return $this->size === 0; 57 | } 58 | 59 | /** 60 | * Add a new node at the end of the queue. 61 | * 62 | * @param mixed $data the data to store. 63 | * @throws DataStructures\Exceptions\FullException if the queue is full. 64 | */ 65 | public function enqueue($data) { 66 | if($this->isFull()) { 67 | throw new FullException(); 68 | } 69 | 70 | $newNode = new Node($data); 71 | if($this->head === null) { 72 | $this->head = &$newNode; 73 | $this->tail = &$this->head; 74 | $newNode->next = &$this->tail; 75 | } else { 76 | $this->tail->next = &$newNode; 77 | $newNode->next = &$this->head; 78 | $this->tail = &$newNode; 79 | } 80 | $this->size++; 81 | } 82 | 83 | /** 84 | * Removes the first node in the queue. 85 | * 86 | * @return mixed 87 | */ 88 | public function dequeue() { 89 | if($this->head === null) { 90 | return null; 91 | } 92 | 93 | if($this->head === $this->tail) { 94 | $temp = $this->head; 95 | $this->head = null; 96 | $this->tail = &$this->head; 97 | $this->size--; 98 | 99 | return $temp->data; 100 | } 101 | 102 | $temp = $this->head; 103 | $this->head = &$this->head->next; 104 | $this->tail->next = &$this->head; 105 | $this->size--; 106 | 107 | return $temp->data; 108 | } 109 | 110 | /** 111 | * Gets the element at the front of the queue without removing it. 112 | * 113 | * @return mixed 114 | */ 115 | public function peek() { 116 | return ($this->head === null) ? null : $this->head->data; 117 | } 118 | 119 | /** 120 | * Returns true if is full the queue and false if there is 121 | * space available. 122 | * 123 | * @return bool 124 | */ 125 | public function isFull() { 126 | if($this->maxSize === 0) { 127 | return false; 128 | } 129 | 130 | return $this->size > 0 && $this->size >= $this->maxSize; 131 | } 132 | 133 | /** 134 | * Binds to count() method. This is equal to make $this->queue->size(). 135 | * 136 | * @return integer the queue size. 0 if it is empty. 137 | */ 138 | public function count() { 139 | return $this->size; 140 | } 141 | } -------------------------------------------------------------------------------- /DataStructures/Lists/SinglyLinkedList.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class SinglyLinkedList extends ListAbstract { 26 | use ArrayAccessTrait; 27 | 28 | protected $head; 29 | private $position; 30 | private $current; 31 | 32 | public function __construct() { 33 | $this->head = null; 34 | $this->size = 0; 35 | $this->position = 0; 36 | $this->current = &$this->head; 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | protected function search($index) { 43 | if($index > $this->size - 1 || $index < 0) { 44 | throw new OutOfBoundsException(); 45 | } 46 | 47 | $current = $this->head; 48 | $i = 0; 49 | while($i < $index && $current->next !== null) { 50 | $current = $current->next; 51 | $i++; 52 | } 53 | 54 | return $current; 55 | } 56 | 57 | /** 58 | * 59 | */ 60 | public function searchLast() { 61 | if($this->head === null) { 62 | return null; 63 | } 64 | 65 | $current = $this->head; 66 | while($current->next !== null) { 67 | $current = $current->next; 68 | } 69 | 70 | return $current; 71 | } 72 | 73 | /** 74 | * Generator for retrieve all nodes stored. 75 | * 76 | * @return null if the head is null (or list is empty) 77 | */ 78 | public function getAll() { 79 | if($this->head === null) { 80 | return; 81 | } 82 | 83 | $current = $this->head; 84 | while($current !== null) { 85 | yield $current->data; 86 | $current = $current->next; 87 | } 88 | } 89 | 90 | /** 91 | * {@inheritDoc} 92 | */ 93 | public function contains($data) : bool { 94 | if($this->empty()) { 95 | return false; 96 | } 97 | 98 | $current = $this->head; 99 | while($current !== null) { 100 | if($current->data === $data) { 101 | return true; 102 | } 103 | $current = $current->next; 104 | } 105 | 106 | return false; 107 | } 108 | 109 | /** 110 | * {@inheritDoc} 111 | */ 112 | public function indexOf($data) { 113 | if($this->head === null) { 114 | return false; 115 | } 116 | 117 | $current = $this->head; 118 | $i = 0; 119 | 120 | while($i < $this->size) { 121 | if($current->data === $data) { 122 | return $i; 123 | } 124 | 125 | $current = $current->next; 126 | $i++; 127 | } 128 | 129 | return false; 130 | } 131 | 132 | /** 133 | * {@inheritDoc} 134 | */ 135 | public function lastIndexOf($data) { 136 | if($this->head === null) { 137 | return false; 138 | } 139 | 140 | $current = $this->head; 141 | $i = 0; 142 | $pos = false; 143 | while($i < $this->size) { 144 | if($current->data == $data) { 145 | $pos = $i; 146 | } 147 | 148 | $current = $current->next; 149 | $i++; 150 | } 151 | 152 | return $pos; 153 | } 154 | 155 | /** 156 | * {@inheritDoc} 157 | */ 158 | public function remove($data) { 159 | $current = &$this->head; 160 | $prev = null; 161 | $i = 0; 162 | 163 | if($this->head === null) { 164 | throw new NotFoundException(); 165 | } 166 | 167 | if($this->head->data === $data) { 168 | $this->head = &$this->head->next; 169 | $this->size--; 170 | return $data; 171 | } 172 | 173 | while($i < $this->size) { 174 | if($current->data === $data) { 175 | $prev->next = &$current->next; 176 | $this->size--; 177 | 178 | return $data; 179 | } 180 | 181 | $prev = $current; 182 | $current = $current->next; 183 | } 184 | 185 | throw new NotFoundException(); 186 | } 187 | 188 | /** 189 | * Add a new node in the specified index. 190 | * 191 | * @param integer $index the position. 192 | * @param mixed $data the data to be stored. 193 | */ 194 | protected function insertAt($index, $data) { 195 | $newNode = new SinglyLinkedListNode($data); 196 | $current = $this->head; 197 | $prev = null; 198 | $i = 0; 199 | while($i < $index) { 200 | $prev = $current; 201 | $current = $current->next; 202 | $i++; 203 | } 204 | 205 | $prev->next = &$newNode; 206 | $newNode->next = &$current; 207 | } 208 | 209 | protected function insertEnd($data) { 210 | $newNode = new SinglyLinkedListNode($data); 211 | if($this->head === null) { 212 | $this->head = &$newNode; 213 | $this->current = &$this->head; 214 | } else { 215 | $current = $this->head; 216 | while($current->next !== null) { 217 | $current = $current->next; 218 | } 219 | $current->next = &$newNode; 220 | } 221 | } 222 | 223 | /** 224 | * {@inheritDoc} 225 | */ 226 | protected function insertBeginning($data) { 227 | $newNode = new SinglyLinkedListNode($data); 228 | if($this->head === null) { 229 | $this->head = &$newNode; 230 | } else { 231 | $newNode->next = $this->head; 232 | $this->head = &$newNode; 233 | } 234 | } 235 | 236 | /** 237 | * Delete a node in the given position and returns it back. 238 | * 239 | * @param integer $index the position. 240 | * @throws OutOfBoundsException if index is negative 241 | * or is greater than the size of the list. 242 | */ 243 | public function delete($index) { 244 | if($index < 0) { 245 | throw new OutOfBoundsException(); 246 | } 247 | if($this->head === null) { 248 | return null; 249 | } 250 | 251 | if($index >= $this->size) { 252 | return null; // It should return an exception 253 | } 254 | 255 | if($index === 0) { 256 | $node = $this->head; 257 | $this->head = $this->head->next; 258 | $this->current = &$this->head; 259 | $this->size--; 260 | return $node->data; 261 | } 262 | 263 | $i = 0; 264 | $current = $this->head; 265 | $prev = $current; 266 | while($i < $index && $current->next !== null) { 267 | $prev = $current; 268 | $current = $current->next; 269 | } 270 | 271 | if($index === $this->size - 1) { 272 | $prev->next = null; 273 | $this->size--; 274 | return $current->data; 275 | } else { 276 | $prev->next = $current->next; 277 | } 278 | $this->size--; 279 | 280 | return $prev->data; 281 | } 282 | 283 | protected function deleteBeginning() {} 284 | 285 | protected function deleteAt($index) {} 286 | 287 | protected function deleteEnd() {} 288 | 289 | 290 | 291 | /** 292 | * Reset the cursor position. 293 | */ 294 | public function rewind() { 295 | $this->position = 0; 296 | $this->current = $this->head; 297 | } 298 | 299 | /** 300 | * Returns the current node data. 301 | * 302 | * @return mixed 303 | */ 304 | public function current() { 305 | return $this->current->data; 306 | } 307 | 308 | /** 309 | * Key or index that indicates the cursor position. 310 | * 311 | * @return integer The current position. 312 | */ 313 | public function key() { 314 | return $this->position; 315 | } 316 | 317 | /** 318 | * Move the cursor to the next node and increments the 319 | * position counter. 320 | */ 321 | public function next() { 322 | ++$this->position; 323 | $this->current = $this->current->next; 324 | } 325 | 326 | /** 327 | * Returns if the current pointer position is valid. 328 | * 329 | * @return boolean true if pointer is not last, else false. 330 | */ 331 | public function valid() { 332 | return $this->current !== null; 333 | } 334 | } -------------------------------------------------------------------------------- /DataStructures/Lists/Stack.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class Stack implements Countable { 25 | /** 26 | * @var $head represents the stack head. 27 | */ 28 | private $head; 29 | /** 30 | * @var $size contains the stack size. Increments each time 31 | * that is inserted a new item. 32 | */ 33 | private $size; 34 | /** 35 | * @var $maxSize if 0 it means that stack is unlimited, else it indicates 36 | * the maximum size of the stack. 37 | */ 38 | private $maxSize; 39 | 40 | /** 41 | * Initializes the stack. 42 | * 43 | * @param int $maxSize 0 by default. If greater than 0 is limited. 44 | * @throws \InvalidArgumentException if size is lower than 0. 45 | */ 46 | public function __construct($maxSize = 0) { 47 | if($maxSize < 0) { 48 | throw new InvalidArgumentException(); 49 | } 50 | $this->head = null; 51 | $this->maxSize = $maxSize; 52 | $this->size = 0; 53 | } 54 | 55 | /** 56 | * Returns the stack size. 57 | * 58 | * @return int the length 59 | */ 60 | public function size() : int { 61 | return $this->size; 62 | } 63 | 64 | 65 | /** 66 | * Checks if the stack is empty. 67 | * 68 | * @return boolean true if is empty, else false. 69 | */ 70 | public function empty() : bool { 71 | return $this->size === 0; 72 | } 73 | 74 | /** 75 | * Adds at the end of the stack new node containing 76 | * the data to be stored. 77 | * 78 | * @param mixed $data The data 79 | * @throws DataStructures\Exceptions\FullException if the queue is full. 80 | */ 81 | public function push($data) { 82 | if($this->isFull()) { 83 | throw new FullException(); 84 | } 85 | 86 | $newNode = new Node($data); 87 | if($this->head === null) { 88 | $this->head = &$newNode; 89 | $newNode->next = null; 90 | } else { 91 | $temp = $this->head; 92 | $this->head = &$newNode; 93 | $newNode->next = &$temp; 94 | } 95 | 96 | $this->size++; 97 | } 98 | 99 | /** 100 | * Removes and returns the last node in the stack. 101 | * 102 | * @return mixed data in node. 103 | */ 104 | public function pop() { 105 | if($this->head === null) { 106 | return null; 107 | } 108 | 109 | $node = $this->head; 110 | $this->head = $this->head->next; 111 | $this->size--; 112 | 113 | return $node->data; 114 | } 115 | 116 | /** 117 | * Gets the element at the front of the stack without removing it. 118 | * 119 | * @return mixed 120 | */ 121 | public function peek() { 122 | return ($this->head === null) ? null : $this->head->data; 123 | } 124 | 125 | /** 126 | * Returns true if is full the stack and false if there is 127 | * space available. 128 | * 129 | * @return bool 130 | */ 131 | public function isFull() { 132 | if($this->maxSize === 0) { 133 | return false; 134 | } 135 | 136 | return $this->size > 0 && $this->size >= $this->maxSize; 137 | } 138 | 139 | /** 140 | * Binds to count() method. This is equal to make $this->stack->size(). 141 | * 142 | * @return integer the stack size. 0 if it is empty. 143 | */ 144 | public function count() { 145 | return $this->size; 146 | } 147 | } -------------------------------------------------------------------------------- /DataStructures/Lists/Traits/ArrayAccessTrait.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | trait ArrayAccessTrait { 22 | abstract public function get($index); 23 | abstract public function delete($index); 24 | 25 | public function offsetSet($offset, $value) { 26 | if (is_null($offset)) { 27 | $offset = $this->size; 28 | if($this->size === 0) { 29 | $offset = 0; 30 | } 31 | $this->insert($offset, $value); 32 | } else { 33 | $this->insert($offset, $value); 34 | } 35 | } 36 | 37 | public function offsetExists($offset) { 38 | try { 39 | return $this->get($offset); 40 | } catch(OutOfBoundsException $e) { 41 | return false; 42 | } 43 | } 44 | 45 | public function offsetUnset($offset) { 46 | $this->delete($offset); 47 | } 48 | 49 | public function offsetGet($offset) { 50 | return $this->get($offset); 51 | } 52 | } -------------------------------------------------------------------------------- /DataStructures/Sets/DisjointSet.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class DisjointSet { 24 | private $subsets; 25 | 26 | public function __construct() { 27 | $this->subsets = []; 28 | } 29 | 30 | /** 31 | * Creates a new set/tree with zero children and parents. 32 | * Its parent points to itself and the rank is 0 when new 33 | * set is created. 34 | * 35 | * @param mixed $data the data to store. 36 | * @return DataStructures\Trees\Nodes\DisjointNode the node created. 37 | */ 38 | public function makeSet($data) : DisjointNode { 39 | $newSet = new DisjointNode($data); 40 | $this->subsets[] = $newSet; 41 | 42 | return $newSet; 43 | } 44 | 45 | /** 46 | * Returns the representative node (the root of $node in the tree) and 47 | * also applies path compression. 48 | * 49 | * @param DataStructures\Trees\Nodes\DisjointNode $node the node from 50 | * where start to search the root. 51 | * @return DataStructures\Trees\Nodes\DisjointNode the parent node. 52 | */ 53 | public function find($vertex) { 54 | if($this->subsets[$vertex]->parent === null || $this->subsets[$vertex]->parent < 0) { 55 | return $vertex; 56 | } 57 | 58 | $this->subsets[$vertex]->parent = $this->find($this->subsets[$vertex]->parent); 59 | return $this->subsets[$vertex]->parent; 60 | } 61 | 62 | /** 63 | * Performs the union of two sets (or trees). First looks for 64 | * the root of $x and $y set. Then, if both are in the same tree 65 | * finalize the method. Else, depending of the rank, will join a 66 | * set to other set (The set with lower rank will be append to higher 67 | * one). If both have the same rank it doesn't matter what tree 68 | * is joined to the other tree but the rank will increase. 69 | * 70 | * @param DataStructures\Trees\Nodes\DisjointNode $x The set. 71 | * @param DataStructures\Trees\Nodes\DisjointNode $y The other set. 72 | */ 73 | public function union($vertex1, $vertex2) { 74 | if($this->subsets[$vertex2]->parent < $this->subsets[$vertex1]->parent) { 75 | $this->subsets[$vertex1]->parent = $vertex2; 76 | } else { 77 | if($this->subsets[$vertex1]->parent === $this->subsets[$vertex2]->parent) { 78 | if($this->subsets[$vertex1]->parent === null) { 79 | $this->subsets[$vertex1]->parent = -1; 80 | } else { 81 | $this->subsets[$vertex1]->parent--; 82 | } 83 | } 84 | 85 | $this->subsets[$vertex2]->parent = $vertex1; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /DataStructures/Sets/TreeSet.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class AVLTree extends BinaryTreeAbstract { 25 | 26 | /** 27 | * Creates a AVLNode. 28 | * 29 | * @param int|string $key the key used to store. 30 | * @param mixed $data the data. 31 | * @param DataStructures\Trees\Nodes\AVLNode|null $parent the parent node. 32 | * @param DataStructures\Trees\Nodes\AVLNode|null $left the left child node. 33 | * @param DataStructures\Trees\Nodes\AVLNode|null $right the right child node. 34 | * 35 | * @return DataStructures\Trees\Nodes\AVLNode the new node created. 36 | */ 37 | public function createNode($key, $data, $parent = null, $left = null, $right = null) { 38 | return new AVLNode($key, $data, $parent, $left, $right); 39 | } 40 | 41 | /** 42 | * Does a right rotation. 43 | * 44 | * @param DataStructures\Trees\Nodes\AVLNode $node The node to be 45 | * rotated. 46 | * @return DataStructures\Trees\Nodes\AVLNode 47 | */ 48 | private function rightRotation(AVLNode &$node) { 49 | $temp = &$node->left; 50 | $temp->parent = &$node->parent; 51 | 52 | $node->left = &$temp->right; 53 | if ($node->left !== null) { 54 | $node->left->parent = &$node; 55 | } 56 | 57 | $temp->right = &$node; 58 | $node->parent = &$temp; 59 | 60 | // temp took over node's place so now its parent should point to temp 61 | if ($temp->parent !== null) { 62 | if ($node === $temp->parent->left) { 63 | $temp->parent->left = &$temp; 64 | } else { 65 | $temp->parent->right = &$temp; 66 | } 67 | } else { 68 | $this->root = &$temp; 69 | } 70 | 71 | return $temp; 72 | } 73 | 74 | /** 75 | * Does a right rotation. 76 | * 77 | * @param DataStructures\Trees\Nodes\AVLNode $node The node to be 78 | * rotated. 79 | * @return DataStructures\Trees\Nodes\AVLNode 80 | */ 81 | private function leftRotation(AVLNode &$node) { 82 | 83 | $temp = &$node->right; 84 | $temp->parent = &$node->parent; 85 | 86 | $node->right = &$temp->left; 87 | 88 | if ($node->right !== null) { 89 | $node->right->parent = &$node; 90 | } 91 | 92 | $temp->left = &$node; 93 | $node->parent = &$temp; 94 | 95 | // temp took over node's place so now its parent should point to temp 96 | if ($temp->parent !== null) { 97 | if ($node == $temp->parent->left) { 98 | $temp->parent->left = &$temp; 99 | } else { 100 | $temp->parent->right = &$temp; 101 | } 102 | } else { 103 | $this->root = &$temp; 104 | } 105 | 106 | return $temp; 107 | } 108 | 109 | /** 110 | * Double right rotation does first a left rotation of right child node 111 | * that detects the imbalance and finally does a right rotation 112 | * in the subtree root that detects the imbalance. 113 | * Case Right-Left. 114 | * 115 | * @param DataStructures\Trees\Nodes\AVLNode $node The node to be 116 | * rotated. 117 | * @return DataStructures\Trees\Nodes\AVLNode 118 | */ 119 | private function doubleRightRotation(AVLNode &$node) { 120 | $this->leftRotation($node->left); 121 | return $this->rightRotation($node); 122 | } 123 | 124 | /** 125 | * Double left rotation does first a right rotation of left child node 126 | * that detects the imbalance and finally does a left rotation 127 | * in the subtree root that detects the imbalance. 128 | * Case Left-Right. 129 | * 130 | * @param DataStructures\Trees\Nodes\AVLNode $node The node to be 131 | * rotated. 132 | * @return DataStructures\Trees\Nodes\AVLNode 133 | */ 134 | private function doubleLeftRotation(AVLNode &$node) { 135 | $this->rightRotation($node->right); 136 | return $this->leftRotation($node); 137 | } 138 | 139 | /** 140 | * {@inheritDoc} 141 | */ 142 | public function put($key, $data, $update = false) { 143 | $nodeInserted = parent::put($key, $data, $update); 144 | $this->rebalance($nodeInserted); 145 | 146 | return $nodeInserted; 147 | } 148 | 149 | /** 150 | * {@inheritDoc} 151 | */ 152 | public function delete($key) { 153 | $nodeDelete = $this->search($key); 154 | if($nodeDelete !== null) { 155 | $successorNode = parent::delete($key); 156 | if($successorNode !== null) { 157 | $minimumNode = ($successorNode->right !== null) ? 158 | $this->getMinNode($successorNode->right) : $successorNode; 159 | 160 | $this->recomputeHeight($minimumNode); 161 | $this->rebalance($minimumNode); 162 | } else { 163 | $this->recomputeHeight($nodeDelete->parent); 164 | $this->rebalance($nodeDelete->parent); 165 | } 166 | 167 | return $successorNode; 168 | } 169 | 170 | return null; 171 | } 172 | 173 | /** 174 | * Rebalance the tree if the difference of height is greater than 1 175 | * between the right and left subtree or reverse. Then it apply a rotation 176 | * depending on the type of unbalance. 177 | * 178 | * @param DataStructures\Trees\Nodes\AVLNode 179 | */ 180 | private function rebalance(&$node) { 181 | while($node !== null) { 182 | $parent = &$node->parent; 183 | 184 | $leftHeight = ($node->left === null) ? 0 : $node->left->height; 185 | $rightHeight = ($node->right === null) ? 0 : $node->right->height; 186 | $nodeBalance = $rightHeight - $leftHeight; 187 | 188 | if($nodeBalance >= 2) { 189 | if($node->right->right !== null) { 190 | $this->leftRotation($node); 191 | return; 192 | } else { 193 | $this->doubleLeftRotation($node); 194 | return; 195 | } 196 | } else if($nodeBalance <= -2) { 197 | if($node->left->left !== null) { 198 | $this->rightRotation($node); 199 | return; 200 | } else { 201 | $this->doubleRightRotation($node); 202 | return; 203 | } 204 | } else { 205 | $this->adjustHeight($node); 206 | } 207 | 208 | $node = &$parent; 209 | } 210 | } 211 | 212 | /** 213 | * Recomputes the height of the nodes ascending in the tree. 214 | * 215 | * @param DataStructures\Trees\Nodes\AVLNode 216 | */ 217 | private function recomputeHeight($node) { 218 | while($node !== null) { 219 | $node->height = $this->maxHeight($node->left, $node->right) + 1; 220 | $node = &$node->parent; 221 | } 222 | } 223 | 224 | /** 225 | * Returns the maximum height between two subtrees. 226 | * 227 | * @param DataStructures\Trees\Nodes\AVLNode|null 228 | * @param DataStructures\Trees\Nodes\AVLNode|null 229 | * @return int the maximum height 230 | */ 231 | private function maxHeight($node1, $node2) { 232 | if($node1 !== null && $node2 !== null) { 233 | return $node1->height > $node2->height ? $node1->height : $node2->height; 234 | } else if($node1 === null) { 235 | return $node2 !== null ? $node2->height : 0; 236 | } else if($node2 === null) { 237 | return $node1 !== null ? $node1->height : 0; 238 | } 239 | 240 | return 0; 241 | } 242 | 243 | /** 244 | * Adjust the height of the node. 245 | * 246 | * @param DataStructures\Trees\Nodes\AVLNode 247 | */ 248 | private function adjustHeight(&$node) { 249 | $leftHeight = ($node->left === null) ? 0 : $node->left->height; 250 | $rightHeight = ($node->right === null) ? 0 : $node->right->height; 251 | 252 | $node->height = 1 + max($leftHeight, $rightHeight); 253 | } 254 | } -------------------------------------------------------------------------------- /DataStructures/Trees/BinarySearchTree.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class BinarySearchTree extends BinaryTreeAbstract { 25 | 26 | public function __construct() { 27 | $this->root = null; 28 | $this->size = 0; 29 | } 30 | 31 | /** 32 | * Creates a BSTNode. 33 | * 34 | * @param int|string $key the key used to store. 35 | * @param mixed $data the data. 36 | * @param DataStructures\Trees\Nodes\BSTNode|null $parent the parent node. 37 | * @param DataStructures\Trees\Nodes\BSTNode|null $left the left child node. 38 | * @param DataStructures\Trees\Nodes\BSTNode|null $right the right child node. 39 | * 40 | * @return DataStructures\Trees\Nodes\BSTNode the new node created. 41 | */ 42 | public function createNode($key, $data, $parent = null, $left = null, $right = null) { 43 | return new BSTNode($key, $data, $parent, $left, $right); 44 | } 45 | } -------------------------------------------------------------------------------- /DataStructures/Trees/BinaryTreeAbstract.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | abstract class BinaryTreeAbstract implements TreeInterface { 24 | use CountTrait; 25 | protected $root; 26 | protected $size = 0; 27 | 28 | abstract public function createNode($key, $data, $parent = null, $left = null, $right = null); 29 | /** 30 | * Checks if the tree is empty. 31 | * 32 | * @return boolean true if is empty, else false. 33 | */ 34 | public function empty() : bool { 35 | return $this->root === null; 36 | } 37 | 38 | /** 39 | * Returns the tree size. 40 | * 41 | * @return int the length 42 | */ 43 | public function size() : int { 44 | return $this->size; 45 | } 46 | 47 | /** 48 | * Inserts data in the correct position. 49 | * 50 | * @param integer|string $key the key used to store. 51 | * @param mixed $data the data to store. 52 | * @param bool $update if false the node isn't updated 53 | * else if the key matches is updated. 54 | */ 55 | public function put($key, $data, $update = false) { 56 | $newNode = $this->createNode($key, $data); 57 | 58 | if($this->root === null) { 59 | $this->root = &$newNode; 60 | $this->size++; 61 | return; 62 | } 63 | 64 | $parentNode = null; 65 | $current = &$this->root; 66 | while($current !== null) { 67 | $parentNode = &$current; 68 | if($key < $current->key) { 69 | $current = &$current->left; 70 | } else if($key > $current->key) { 71 | $current = &$current->right; 72 | } else { 73 | if($update) { 74 | $current->data = $data; 75 | return $newNode; 76 | } 77 | return; 78 | } 79 | } 80 | 81 | $newNode->parent = &$parentNode; 82 | if($key < $parentNode->key) { 83 | $parentNode->left = &$newNode; 84 | } else { 85 | $parentNode->right = &$newNode; 86 | } 87 | $this->size++; 88 | 89 | return $newNode; 90 | } 91 | 92 | /** 93 | * Creates a new node or updates it if already exists. 94 | * 95 | * @param int|string $key the key. 96 | * @param mixed $data the data to be stored. 97 | */ 98 | public function putOrUpdate($key, $data) { 99 | $this->put($key, $data, true); 100 | } 101 | 102 | /** 103 | * Retrieve the data stored in the tree. 104 | * 105 | * @param int|string $key the key to identify the data. 106 | * @return mixed 107 | */ 108 | public function get($key) { 109 | if($this->root === null) { 110 | return null; 111 | } 112 | 113 | $node = $this->root; 114 | while($node !== null) { 115 | if($key < $node->key) { 116 | $node = $node->left; 117 | } else if($key > $node->key) { 118 | $node = $node->right; 119 | } else { 120 | return $node->data; 121 | } 122 | } 123 | 124 | return null; 125 | } 126 | 127 | /** 128 | * Returns the root node. 129 | * 130 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the root node. 131 | */ 132 | public function getRoot() { 133 | return $this->root; 134 | } 135 | 136 | /** 137 | * Looks for the node with the given key. 138 | * 139 | * @param int|string $key the key used to look for. 140 | * @return bool true if was found. 141 | */ 142 | public function exists($key) : bool { 143 | // $this->_exists($this->root, $key); for recursive search 144 | if($this->root === null) { 145 | return false; 146 | } 147 | 148 | if($this->root->key === $key) { 149 | return true; 150 | } else { 151 | $node = $this->root; 152 | while($node !== null) { 153 | if($key < $node->key) { 154 | $node = $node->left; 155 | } else if($key > $node->key) { 156 | $node = $node->right; 157 | } else { 158 | return true; 159 | } 160 | } 161 | } 162 | 163 | return false; 164 | } 165 | 166 | /** 167 | * Method that retrieves true if found a node with the specified key. 168 | * It's the recursive version of exists method and it's used internally 169 | * for traverse through a root node. 170 | * 171 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $node the root node. 172 | * @param int|string $key the key used to look for. 173 | * @return bool true if exists a node with that key. 174 | */ 175 | private function _exists($node, $key) : bool { 176 | if($node === null) { 177 | return false; 178 | } 179 | 180 | if($node->key === $key) { 181 | return true; 182 | } else if($key < $node->key) { 183 | return $this->_exists($node->left, $key); 184 | } else if($key > $node->key) { 185 | return $this->_exists($node->right, $key); 186 | } 187 | } 188 | 189 | public function floor($key){} 190 | public function ceil($key){} 191 | 192 | /** 193 | * Gets the node with the minimum key. The most left and more bottom. 194 | * 195 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the minum node or 196 | * null if the tree is empty. 197 | */ 198 | public function min() { 199 | if($this->root === null) { 200 | return null; 201 | } 202 | 203 | if($this->root->left === null) { 204 | return $this->root; 205 | } 206 | 207 | $current = $this->root; 208 | while($current->left !== null) { 209 | $current = $current->left; 210 | } 211 | 212 | return $current; 213 | } 214 | 215 | /** 216 | * Gets the node with the maximum key. The most right and more bottom. 217 | * 218 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the maximum node or 219 | * null if the tree is empty. 220 | */ 221 | public function max() { 222 | if($this->root === null) { 223 | return null; 224 | } 225 | 226 | if($this->root->right === null) { 227 | return $this->root; 228 | } 229 | 230 | $node = $this->root; 231 | while($node->right !== null) { 232 | $node = $node->right; 233 | } 234 | 235 | return $node; 236 | } 237 | 238 | /** 239 | * Returns the minimum node from a given node in position X. 240 | * 241 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface $node the start point. 242 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the minimum node. 243 | */ 244 | protected function getMinNode(BinaryNodeInterface $node = null) { 245 | if($node === null) { 246 | return null; 247 | } 248 | 249 | while($node->left !== null) { 250 | $node = $node->left; 251 | } 252 | 253 | return $node; 254 | } 255 | 256 | /** 257 | * Returns the maximum node from a given node in position X. 258 | * 259 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface $node the start point. 260 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the maximum node. 261 | */ 262 | protected function getMaxNode(BinaryNodeInterface $node = null) { 263 | if($node === null) { 264 | return null; 265 | } 266 | 267 | while($node->right !== null) { 268 | $node = $node->right; 269 | } 270 | 271 | return $node; 272 | } 273 | 274 | /** 275 | * Deletes the node with the minimum key and returns it. The most left and more bottom. 276 | * 277 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null if null takes the root. 278 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the minimum node or 279 | * null if the tree is empty. 280 | */ 281 | public function deleteMin(BinaryNodeInterface $node = null) { 282 | $node = $this->getMinNode($node ?? $this->root); 283 | if($node !== null) { 284 | $this->_delete($node); 285 | } 286 | 287 | return $node; 288 | } 289 | 290 | /** 291 | * Deletes the node with the maximum key and returns it. The most right and more bottom. 292 | * 293 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null if null takes the root. 294 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the maximum node or 295 | * null if the tree is empty. 296 | */ 297 | public function deleteMax(BinaryNodeInterface $node = null) { 298 | $node = $this->getMaxNode($node ?? $this->root); 299 | if($node !== null) { 300 | $this->_delete($node); 301 | } 302 | 303 | return $node; 304 | } 305 | 306 | /** 307 | * Deletes the node with the maximum key and returns it. The most right and more bottom. 308 | * 309 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null if null takes the root. 310 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the maximum node or 311 | * null if the tree is empty. 312 | */ 313 | public function delete($key) { 314 | $deleteNode = $this->search($key); 315 | if($deleteNode !== null) { 316 | $this->_delete($deleteNode); 317 | return $deleteNode; 318 | } 319 | 320 | return null; 321 | } 322 | 323 | /** 324 | * Deletes the selected node if is not null and returns the node 325 | * that replaces the deleted node. Also decrease the size of tree. 326 | * 327 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null The node to be deleted. 328 | * @return the node that replaces the deleted. 329 | */ 330 | protected function _delete(BinaryNodeInterface &$node) { 331 | if($node !== null) { 332 | $nodeToReturn = null; 333 | if($node->left === null) { 334 | $nodeToReturn = $this->replace($node, $node->right); 335 | } else if($node->right === null) { 336 | $nodeToReturn = $this->replace($node, $node->left); 337 | } else { 338 | $successorNode = $this->getMinNode($node->right); 339 | if($successorNode->parent !== $node) { 340 | $this->replace($successorNode, $successorNode->right); 341 | $successorNode->right = &$node->right; 342 | $successorNode->right->parent = &$successorNode; 343 | } 344 | 345 | $this->replace($node, $successorNode); 346 | $successorNode->left = &$node->left; 347 | $successorNode->left->parent = &$successorNode; 348 | $nodeToReturn = &$successorNode; 349 | } 350 | 351 | $this->size--; 352 | return $nodeToReturn; 353 | } 354 | 355 | return null; 356 | } 357 | 358 | /** 359 | * Replaces the node n to remove a new one k and links k with the parent 360 | * of n. 361 | * 362 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface $nodeToReplace the node to remove. 363 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $newNode the newNode 364 | * to link with the $nodeToReplace parent. 365 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface the new linked node. 366 | */ 367 | protected function replace(&$nodeToReplace, &$newNode) { 368 | if($nodeToReplace->parent === null) { 369 | $this->root = &$newNode; 370 | } else if($nodeToReplace === $nodeToReplace->parent->left) { 371 | $nodeToReplace->parent->left = &$newNode; 372 | } else if($nodeToReplace === $nodeToReplace->parent->right) { 373 | $nodeToReplace->parent->right = &$newNode; 374 | } 375 | 376 | if($newNode !== null) { 377 | $newNode->parent = &$nodeToReplace->parent; 378 | } 379 | 380 | return $newNode; 381 | } 382 | 383 | /** 384 | * Retrieves the node with the specified key. 385 | * 386 | * @param int|string $key the key used to store. 387 | * @return DataStructures\Trees\Nodes\BinaryNodeInterface|null the node or null. 388 | */ 389 | public function search($key) { 390 | if($this->root === null) { 391 | return null; 392 | } 393 | 394 | if($this->root->key === $key) { 395 | return $this->root; 396 | } else { 397 | $node = $this->root; 398 | while($node !== null) { 399 | if($key < $node->key) { 400 | $node = $node->left; 401 | } else if($key > $node->key) { 402 | $node = $node->right; 403 | } else { 404 | return $node; 405 | } 406 | } 407 | } 408 | 409 | return null; 410 | } 411 | 412 | /** 413 | * Returns true if is leaf the node. 414 | * 415 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $node default to null. 416 | * @return true if is leaf the node, is not null and their subtrees has no 417 | * pointers to successors. 418 | */ 419 | public function isLeaf($node) : bool { // BinaryTreeNode 420 | return ($node !== null && $node->left === null && $node->right === null); 421 | } 422 | 423 | /** 424 | * Checks if a node is root. New nodes that does not point to any other node 425 | * also are called a root node. 426 | * 427 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $node default to null. 428 | * @return true if is root the node, is not null and their subtrees has no 429 | * pointers to successors. 430 | */ 431 | public function isRoot($node) : bool { 432 | return $node !== null && $node->parent === null; 433 | } 434 | 435 | /** 436 | * Traverse in preorder. This is: first visit the root, then 437 | * the left subtree and finally the right subtree. 438 | * 439 | * @param Callable|null $callback the callback function to apply to each 440 | * node. 441 | */ 442 | public function preorder(Callable $callback = null) { 443 | $this->_preorder($this->root, $callback); 444 | } 445 | 446 | /** 447 | * Private preorder traverse method that is recursive and is called by 448 | * the public preorder method. 449 | * 450 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $node. 451 | * @param Callable|null $callback the callback function to apply to each 452 | * node. 453 | */ 454 | private function _preorder($node, Callable $callback = null) { 455 | if($node === null) { 456 | return; 457 | } 458 | if($callback !== null) { 459 | call_user_func($callback, $node); 460 | } 461 | $this->_preorder($node->left, $callback); 462 | $this->_preorder($node->right, $callback); 463 | } 464 | 465 | /** 466 | * Traverse in inorder. This is: first visit the left subtree, 467 | * then the root and finally the right subtree. 468 | * 469 | * @param Callable|null $callback the callback function to apply to each 470 | * node. 471 | */ 472 | public function inorder(Callable $callback = null) { 473 | $this->_inorder($this->root); 474 | } 475 | 476 | /** 477 | * Private inorder traverse method that is recursive and is called by 478 | * the public inorder method. 479 | * 480 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $node. 481 | * @param Callable|null $callback the callback function to apply to each 482 | * node. 483 | */ 484 | private function _inorder($node, Callable $callback = null) { 485 | if($node === null) { 486 | return; 487 | } 488 | 489 | $this->_inorder($node->left, $callback); 490 | if($callback !== null) { 491 | call_user_func($callback, $node); 492 | } 493 | $this->_inorder($node->right, $callback); 494 | } 495 | 496 | /** 497 | * Traverse in postorder. This is: first visit the left subtree, 498 | * then the right subtree and finally the root. 499 | * 500 | * @param Callable|null $callback the callback function to apply to each 501 | * node. 502 | */ 503 | public function postorder(Callable $callback = null) { 504 | $this->_postorder($this->root, $callback); 505 | } 506 | 507 | /** 508 | * Private postorder traverse method that is recursive and is called by 509 | * the public postorder method. 510 | * 511 | * @param DataStructures\Trees\Nodes\BinaryNodeInterface|null $node. 512 | * @param Callable|null $callback the callback function to apply to each 513 | * node. 514 | */ 515 | private function _postorder($node, Callable $callback = null) { 516 | if($node === null) { 517 | return; 518 | } 519 | $this->_postorder($node->left, $callback); 520 | $this->_postorder($node->right, $callback); 521 | if($callback !== null) { 522 | call_user_func($callback, $node); 523 | } 524 | } 525 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Interfaces/BinaryNodeInterface.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | interface BinaryNodeInterface {} -------------------------------------------------------------------------------- /DataStructures/Trees/Interfaces/ComparableInterface.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | interface Comparable { 19 | public function compareTo($item); 20 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Interfaces/TreeInterface.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | interface TreeInterface extends Countable { 21 | public function empty() : bool; 22 | public function size() : int; 23 | public function put($key, $data); 24 | public function putOrUpdate($key, $data); 25 | public function get($key); 26 | public function getRoot(); 27 | public function exists($key) : bool; 28 | public function floor($key); 29 | public function ceil($key); 30 | public function min(); 31 | public function max(); 32 | public function deleteMin(); 33 | public function deleteMax(); 34 | public function delete($key); 35 | public function search($key); 36 | public function isLeaf($node) : bool; 37 | public function isRoot($node) : bool; 38 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Nodes/AVLNode.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class AVLNode extends BSTNode { 23 | public $height; // the node height 24 | 25 | public function __construct($key, $data, $parent = null, $left = null, $right = null) { 26 | parent::__construct($key, $data, $parent, $left, $right); 27 | $this->height = 0; 28 | } 29 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Nodes/BSTNode.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class BSTNode implements BinaryNodeInterface { 21 | public $key; // key used to insert, remove and retrieve 22 | public $data; // associated data 23 | public $parent; // the parent node 24 | public $left; // left subtree 25 | public $right; // right subtree 26 | 27 | public function __construct($key, $data, $parent = null, $left = null, $right = null) { 28 | $this->key = $key; 29 | $this->data = $data; 30 | $this->parent = $parent; 31 | $this->left = $left; 32 | $this->right = $right; 33 | } 34 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Nodes/DisjointNode.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class DisjointNode { 23 | public $parent; 24 | public $data; 25 | 26 | public function __construct($data) { 27 | $this->parent = null; 28 | $this->data = $data; 29 | } 30 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Nodes/TrieNode.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class TrieNode implements Countable { 22 | public $char; 23 | public $isWord; 24 | public $children; 25 | public $parent; 26 | 27 | public function __construct($char = '', &$parent = null, $isWord = false) { 28 | $this->char = $char; 29 | $this->isWord = $isWord; 30 | $this->children = []; 31 | $this->parent = &$parent; 32 | } 33 | 34 | /** 35 | * Returns if the node has children. 36 | * 37 | * @return bool true if there is one or more children. 38 | */ 39 | public function hasChildren() : bool { 40 | return count($this->children) > 0; 41 | } 42 | 43 | /** 44 | * Returns if the node is leaf: it has not children nodes and 45 | * it is not root. 46 | * 47 | * @return bool true if is leaf. 48 | */ 49 | public function isLeaf() : bool { 50 | return $this->parent !== null && $this->hasChildren() === false; 51 | } 52 | 53 | /** 54 | * Checks if is the node is root. 55 | * 56 | * @return bool true is it has not parent node. 57 | */ 58 | public function isRoot() : bool { 59 | return $this->parent === null; 60 | } 61 | 62 | /** 63 | * Returns the number of children. Binds count(). 64 | * 65 | * @return int The node count. 66 | */ 67 | public function count() { 68 | return count($this->children); 69 | } 70 | } -------------------------------------------------------------------------------- /DataStructures/Trees/Traits/CountTrait.php: -------------------------------------------------------------------------------- 1 | tree->size(). 8 | * 9 | * @return integer the tree size. 0 if it is empty. 10 | */ 11 | public function count() { 12 | return $this->size; 13 | } 14 | } -------------------------------------------------------------------------------- /DataStructures/Trees/TrieTree.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class TrieTree implements Countable { 24 | private $root; 25 | private $numWords; 26 | private $size; 27 | 28 | public function __construct() { 29 | $this->root = new TrieNode(); 30 | $this->numWords = 0; 31 | $this->size = 0; 32 | } 33 | 34 | /** 35 | * Returns true if the tree is empty. Number of prefixes is 0. 36 | * 37 | * @return bool true if empty. 38 | */ 39 | public function empty() : bool { 40 | return $this->size === 0; 41 | } 42 | 43 | /** 44 | * Returns the number of prefixes that are contained in the trie. 45 | * 46 | * @return int the num of prefixes. 47 | */ 48 | public function size() : int { 49 | return $this->size; 50 | } 51 | 52 | /** 53 | * Inserts a new word in the tree. If the word exists it doesn't do nothing. 54 | * 55 | * @param string $word the word to be added. 56 | */ 57 | public function add($word) { 58 | $word = trim($word); 59 | $wordLength = mb_strlen($word); 60 | if($wordLength === 0) { 61 | return; 62 | } 63 | 64 | $current = &$this->root; 65 | for($i = 0; $i < $wordLength; $i++) { 66 | $char = mb_substr($word, $i, 1, 'UTF-8'); 67 | if(!isset($current->children[$char])) { 68 | if($i === $wordLength - 1) { 69 | $current->children[$char] = new TrieNode($char, $current, true); 70 | $this->numWords++; 71 | } else { 72 | $current->children[$char] = new TrieNode($char, $current, false); 73 | } 74 | $this->size++; 75 | } else { 76 | if($i === $wordLength - 1) { 77 | if($current->children[$char]->isWord === false) { 78 | $current->children[$char]->isWord = true; 79 | $this->numWords++; 80 | } 81 | } 82 | } 83 | 84 | $current = &$current->children[$char]; 85 | } 86 | } 87 | 88 | /** 89 | * Returns true if the word is stored in the tree. 90 | * 91 | * @param string $word The word to check if exists. 92 | * @param bool true if is contained. 93 | */ 94 | public function contains($word) : bool { 95 | $word = trim($word); 96 | $wordLength = mb_strlen($word); 97 | if($wordLength === 0) { 98 | return true; 99 | } 100 | 101 | $i = 0; 102 | $found = true; 103 | $current = $this->root; 104 | while($found && $i < $wordLength) { 105 | $char = mb_substr($word, $i, 1); 106 | if(isset($current->children[$char])) { 107 | $current = $current->children[$char]; 108 | } else { 109 | $found = false; 110 | } 111 | $i++; 112 | } 113 | 114 | if($found && $current->isWord) { 115 | return true; 116 | } 117 | 118 | return false; 119 | } 120 | 121 | /** 122 | * Removes a word from the tree. 123 | * 124 | * @param string $word the word to delete if it is contained in the trie. 125 | */ 126 | public function delete($word) { 127 | $wordLength = mb_strlen($word); 128 | $i = 0; 129 | $current = $this->root; 130 | 131 | while($i < $wordLength) { 132 | if($current->hasChildren()) { 133 | $char = mb_substr($word, $i, 1, 'UTF-8'); 134 | if(isset($current->children[$char])) { 135 | $node = &$current->children[$char]; 136 | if(($i === $wordLength - 1) && $node->isWord) { 137 | if($node->hasChildren()) { 138 | $node->isWord = false; 139 | } else { 140 | $parentNode = &$node->parent; 141 | unset($parentNode->children[$char]); 142 | $this->size--; 143 | $j = $i - 1; 144 | 145 | while($j >= 0 && $parentNode->isLeaf() && !$parentNode->isWord) { 146 | $char = mb_substr($word, $j, 1, 'UTF-8'); 147 | $parentNode = &$parentNode->parent; 148 | unset($parentNode->children[$char]); 149 | $this->size--; 150 | $j--; 151 | } 152 | } 153 | 154 | $this->numWords--; 155 | } 156 | } 157 | } else { 158 | return; 159 | } 160 | 161 | if(isset($current->children[$char])) { 162 | $current = $current->children[$char]; 163 | } else { 164 | $current = $current->parent; //TODO need to delete all leaf nodes 165 | } 166 | $i++; 167 | } 168 | } 169 | 170 | /** 171 | * Removes all nodes (and words) in the tree and resets the size 172 | * and word count. 173 | */ 174 | public function clear() { 175 | foreach($this->root->children as $key => &$node) { 176 | $this->_clear($node); 177 | unset($this->root->children[$key]); 178 | } 179 | 180 | $this->size = 0; 181 | $this->numWords = 0; 182 | } 183 | 184 | /** 185 | * Recursive clear that removes all nodes, included leaf, except 186 | * the references to the first children. 187 | * 188 | * @param DataStructures\Trees\Nodes\TrieNode|null the node to traverse. 189 | * @return DataStructures\Trees\Nodes\TrieNode|null the next node to delete. 190 | */ 191 | private function _clear(TrieNode &$node = null) { 192 | foreach($node->children as $char => &$n) { 193 | $aux = $node->children[$char]; 194 | unset($node->children[$char]); 195 | $node = null; 196 | return $this->_clear($aux); 197 | } 198 | } 199 | 200 | /** 201 | * Returns an array containing all words stored in the tree. 202 | * It starts to search from the root that contains an empty string 203 | * using the withPrefix method. 204 | * 205 | * @return array an empty array if there are not words in the tree. 206 | */ 207 | public function getWords() : array { 208 | if($this->size === 0) { 209 | return []; 210 | } 211 | 212 | return $this->withPrefix(''); 213 | } 214 | 215 | /** 216 | * Checks if there is any word that starts with a concrete prefix. 217 | * 218 | * @param string $prefix The prefix to look up. 219 | * @return true if there are words that start with the especified prefix. 220 | */ 221 | public function startsWith($prefix) : bool { 222 | return $this->getNodeFromPrefix($prefix) !== null; 223 | } 224 | 225 | /** 226 | * Returns an array with all words that has the especified prefix. 227 | * For example, with prefix 'he' it will retrieve: hell, hello, .... 228 | * 229 | * @param string $prefix The prefix that must have all words. 230 | * @return array All words that contains the prefix. 231 | */ 232 | public function withPrefix($prefix) : array { 233 | $node = $this->getNodeFromPrefix($prefix); 234 | $words = []; 235 | if($node !== null) { 236 | if($node->isWord) { 237 | $words[] = $prefix; 238 | } 239 | foreach($node->children as $char => $n) { 240 | $words = $this->_traverseWithPrefix($node->children[$char], $words, $prefix . $char); 241 | } 242 | } 243 | 244 | return $words; 245 | } 246 | 247 | /** 248 | * 249 | */ 250 | private function _traverseWithPrefix(TrieNode $node = null, $words = [], $word) { 251 | if($node->isWord) { 252 | $words[] = $word; 253 | } 254 | 255 | if(empty($node->children)) { 256 | return $words; 257 | } 258 | 259 | foreach($node->children as $char => &$n) { 260 | $words = $this->_traverseWithPrefix($node->children[$char], $words, $word . $char); 261 | } 262 | 263 | return $words; 264 | } 265 | 266 | /** 267 | * Retrieves the node where ends the prefix especified. 268 | * 269 | * @param string $prefix The prefix to look for. 270 | * @return DataStructures\Trees\Nodes\TrieNode|null null if not found. 271 | */ 272 | private function getNodeFromPrefix($prefix) { 273 | if($this->size === 0) { 274 | return null; 275 | } 276 | 277 | $i = 0; 278 | $current = $this->root; 279 | $prefixLength = mb_strlen(trim($prefix)); 280 | while($i < $prefixLength) { 281 | $char = mb_substr($prefix, $i, 1, 'UTF-8'); 282 | if(!isset($current->children[$char])) { 283 | return null; 284 | } 285 | $current = $current->children[$char]; 286 | $i++; 287 | } 288 | 289 | return $current; 290 | } 291 | 292 | /** 293 | * Gets the number of words stored in the tree. 294 | * 295 | * @return int The word count. 296 | */ 297 | public function wordCount() : int { 298 | return $this->numWords; 299 | } 300 | 301 | /** 302 | * Gets the number of prefixes stored in the tree. 303 | * 304 | * @return int The word count. 305 | */ 306 | public function count() : int { 307 | return $this->size(); 308 | } 309 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Siro Díaz Palazón 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DataStructures [![Build Status](https://travis-ci.org/SiroDiaz/DataStructures.svg?branch=master)](https://travis-ci.org/SiroDiaz/DataStructures) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/SiroDiaz/DataStructures/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/SiroDiaz/DataStructures/?branch=master) 2 | Data structures for PHP >= 7.0. Use data structures in your project using this library. 3 | 4 | ## Index 5 | 6 | [Install](https://github.com/SiroDiaz/DataStructures#install)
7 | **[API](https://github.com/SiroDiaz/DataStructures#api)**
8 | 9 | *[List implementations](https://github.com/SiroDiaz/DataStructures#lists)*
10 | - [Singly linked list](https://github.com/SiroDiaz/DataStructures#singly-linked-list)
11 | - [Singly circular linked list](https://github.com/SiroDiaz/DataStructures#circular-linked-list)
12 | - [Doubly circular linked list](https://github.com/SiroDiaz/DataStructures#doubly-circular-linked-list)
13 | - [Array list](https://github.com/SiroDiaz/DataStructures#array-list)
14 | - [Stack](https://github.com/SiroDiaz/DataStructures#stack)
15 | - [Queue](https://github.com/SiroDiaz/DataStructures#queue)
16 | *[Tree implementations](https://github.com/SiroDiaz/DataStructures#trees)*
17 | - [Trie tree](https://github.com/SiroDiaz/DataStructures#trie-tree)
18 | - [Binary search tree](https://github.com/SiroDiaz/DataStructures#binary-search-tree)
19 | - [AVL Tree](https://github.com/SiroDiaz/DataStructures#avl-tree)
20 | ## Install 21 | 22 | Via Composer just copy and paste: 23 | ```sh 24 | composer require siro-diaz/data-structures:"dev-master" 25 | ``` 26 | ## API 27 | ### Lists 28 | 29 | The list data structures supported are the following: 30 | 31 | *list type*: **class** 32 | 33 | - *Singly linked list*: **SinglyLinkedList** 34 | - *Circular singly linked list*: **CircularLinkedList** 35 | - *Circular doubly linked list*: **DoublyLinkedList** 36 | - *Array list*: **ArrayList** 37 | - *Stack*: **Stack** 38 | - *Queue*: **Queue** 39 | 40 | 41 | #### Singly linked list 42 | ##### Introduction 43 | Singly linked list is the simplest linked list that can be created. It has a pointer 44 | to the next node in the list and the last node points to null. 45 | All lists except stacks and queues have the same methods because they implements the same 46 | interface. 47 | ##### Methods 48 | - size() 49 | - empty() 50 | - get($index) 51 | - getAll() 52 | - getLast() 53 | - delete($index) 54 | - clear() 55 | - pop() 56 | - insert($index, $data) 57 | - push($data) 58 | - unshift($data) 59 | - shift() 60 | - toArray() 61 | ##### Example 62 | ```php 63 | use DataStructures\Lists\SinglyLinkedList; 64 | 65 | $myList = new SinglyLinkedList(); 66 | $myList->push(20); 67 | echo "Size of : ". $myList->size(); 68 | $myList->unshift(100); 69 | echo "Item at the beginnig: ". $myList->get(0); 70 | ``` 71 | 72 | 73 | #### Circular linked list 74 | 75 | ##### Introduction 76 | Circular linked list is a singly linked list that has a pointer to the last and first node. 77 | All lists except stacks and queues have the same methods because they implements the same 78 | interface. 79 | 80 | ##### Methods 81 | - size() 82 | - empty() 83 | - get($index) 84 | - getAll() 85 | - getLast() 86 | - delete($index) 87 | - clear() 88 | - pop() 89 | - insert($index, $data) 90 | - push($data) 91 | - unshift($data) 92 | - shift() 93 | - toArray() 94 | ##### Example 95 | ```php 96 | use DataStructures\Lists\CircularLinkedList; 97 | 98 | $myList = new CircularLinkedList(); 99 | $myList->push(20); 100 | $myList->push(10); 101 | echo "Size of : ". $myList->size(); 102 | $myList->unshift(100); 103 | echo "Item at the beginnig: ". $myList->get(0); 104 | echo "Last item: ". $myList->getLast(); 105 | ``` 106 | 107 | 108 | #### Doubly circular linked list 109 | 110 | ##### Introduction 111 | Doubly circular linked list is a doubly linked list that each node contained in the list 112 | contains a pointer to the next and previous node. In the of the first node it is going to point 113 | to the last node. It uses some performance tricks for insert, get, and delete operations. 114 | 115 | ##### Methods 116 | - size() 117 | - empty() 118 | - get($index) 119 | - getAll() 120 | - getLast() 121 | - delete($index) 122 | - clear() 123 | - pop() 124 | - insert($index, $data) 125 | - push($data) 126 | - unshift($data) 127 | - shift() 128 | - toArray() 129 | 130 | ##### Example 131 | ```php 132 | use DataStructures\Lists\DoublyLinkedList; 133 | 134 | $myList = new DoublyLinkedList(); 135 | $myList->push(20); 136 | $myList->push(10); 137 | echo "Size of : ". $myList->size(); 138 | $myList->unshift(100); 139 | echo "Item at position 1: ". $myList->get(1); 140 | echo "Last item: ". $myList->getLast(); 141 | ``` 142 | 143 | #### Array list 144 | 145 | ##### Introduction 146 | Array list uses the built in arrays as lists. In PHP all uses hash tables and it give 147 | array lists some performance advantages in operations like get that will be O(1). 148 | Array list auto increments their size internally, without the necessity of increment it 149 | manually. It is translates in a very easy way to implement this type of list. 150 | 151 | ##### Methods 152 | - size() 153 | - empty() 154 | - get($index) 155 | - getAll() 156 | - getLast() 157 | - delete($index) 158 | - clear() 159 | - pop() 160 | - insert($index, $data) 161 | - push($data) 162 | - unshift($data) 163 | - shift() 164 | - toArray() 165 | 166 | ##### Example 167 | ```php 168 | use DataStructures\Lists\ArrayList; 169 | 170 | $myList = new ArrayList(); 171 | $myList->push(20); 172 | $myList->push(10); 173 | echo "Size of : ". $myList->size(); 174 | $myList->unshift(100); 175 | echo "Item at position 1: ". $myList->get(1); 176 | echo "Last item: ". $myList->getLast(); 177 | ``` 178 | 179 | 180 | #### Stack 181 | 182 | ##### Introduction 183 | Stack is a LIFO (Last In First Out) data structure that works like a stack (as its name 184 | said). Last element that is inserted will be the first in going out. 185 | The implementation used in this library allow to especify a maximum size, in other words, 186 | it can be a limited stack. When limited stack is been used is important check if it is full 187 | before insert a new element. 188 | 189 | ##### Methods 190 | - size() 191 | - empty() 192 | - isFull() 193 | - peek() 194 | - pop() 195 | - push($data) 196 | 197 | ##### Example 198 | ```php 199 | use DataStructures\Lists\Stack; 200 | 201 | $myStack = new Stack(); // unlimited stack. 202 | // new Stack(5) will contain a maximum of 5 elements. 203 | $myStack->push(20); 204 | $myStack->push(10); 205 | echo "Size of : ". $myStack->size(); 206 | echo "Front element: ". $myStack->peek(1); 207 | echo "Last element inserted and being removed: ". $myStack->pop(); 208 | ``` 209 | 210 | ### Trees 211 | 212 | The tree data structures supported are the following: 213 | 214 | *tree type*: **class** 215 | 216 | - *Trie tree*: **TrieTree** 217 | - *Binary search tree*: **BinarySearchTree** 218 | - *AVL tree*: **AVLTree** 219 | 220 | #### Trie tree 221 | ##### Introduction 222 | Singly linked list is the simplest linked list that can be created. It has a pointer 223 | to the next node in the list and the last node points to null. 224 | All lists except stacks and queues have the same methods because they implements the same 225 | interface. 226 | ##### Methods 227 | - size() 228 | - empty() 229 | - wordCount() 230 | - withPrefix($prefix) 231 | - startsWith($prefix) 232 | - getWords() 233 | - clear() 234 | - delete($word) 235 | - contains($word) 236 | - add($word) 237 | ##### Example 238 | ```php 239 | use DataStructures\Trees\TrieTree; 240 | 241 | $trie = new TrieTree(); 242 | $trie->add('hello'); 243 | $trie->add('hell'); 244 | $trie->add('world'); 245 | echo "Size of : ". $trie->wordCount(); // 3 246 | $trie->contains('hell'); // true 247 | 248 | echo "There are words that start with 'he': ". $trie->startsWith('he'); // true 249 | ``` 250 | 251 | #### Binary Search Tree 252 | ##### Introduction 253 | Binary search tree (BST) is a data structure which has a root node that may have up to 2 siblings. 254 | Each sibling also can have a maximum of 2 siblings. If the node have not siblings it is called leaf node. 255 | The left sibling is lower than the parent node and right sibling is grater than it parent. 256 | ##### Methods 257 | - size() 258 | - empty() 259 | - delete($key) 260 | - put($key, $data, $update = false) 261 | - putOrUpdate($key, $data) 262 | - get($key) 263 | - getRoot() 264 | - exists($key) 265 | - min() 266 | - max() 267 | - deleteMin(BinaryNodeInterface $node = null) 268 | - deleteMax(BinaryNodeInterface $node = null) 269 | - search($key) 270 | - isLeaf($node) 271 | - isRoot($node) 272 | - preorder(Callable $callback = null) 273 | - inorder(Callable $callback = null) 274 | - postorder(Callable $callback = null) 275 | ##### Example 276 | ```php 277 | use DataStructures\Trees\BinarySearchTree; 278 | 279 | $bst = new BinarySearchTree(); 280 | $bst->put(4, 10); 281 | $bst->put(2, 100); 282 | $bst->put(10, 1000); 283 | echo "Size of : ". $bst->size(); 284 | $bst->exists(100); // false 285 | echo "Is leaf?: ". $bst->isLeaf($bst->min()); // true 286 | ``` 287 | 288 | #### AVL Tree 289 | ##### Introduction 290 | AVL Tree is a binary search tree that has a balance condition. The condition consists in each 291 | subnode can have a maximum height of one respect the oposite side subtree (it means that right subtree of a node can't be higher than one, compared with the left subtree). If it has a height of two or more then is rebalanced the tree using a single left rotation, single right rotation, double left rotation or a double right rotation. 292 | ##### Methods 293 | Same method that binary search tree. 294 | ##### Example 295 | ```php 296 | use DataStructures\Trees\AVLTree; 297 | 298 | $avl = new BinarySearchTree(); 299 | $avl->put(4, 10); 300 | $avl->put(2, 100); 301 | $avl->put(10, 1000); 302 | echo "Size of : ". $avl->size(); 303 | $avl->exists(100); // false 304 | echo "Is leaf?: ". $avl->isLeaf($avl->min()); // true 305 | ``` 306 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "siro-diaz/data-structures", 3 | "description": "Data structures for PHP language", 4 | "type": "library", 5 | "license": "MIT", 6 | "keywords": [ 7 | "data structure", 8 | "AVL", 9 | "BST", 10 | "trie", 11 | "list", 12 | "tree", 13 | "stack", 14 | "hash table", 15 | "hash set", 16 | "hash map", 17 | "disjoint set", 18 | "set", 19 | "ordered list", 20 | "unordered list", 21 | "dictionary", 22 | "php7", 23 | "algorithm" 24 | ], 25 | "authors": [ 26 | { 27 | "name": "Siro Diaz Palazon", 28 | "email": "siro_diaz@yahoo.com" 29 | } 30 | ], 31 | "require": { 32 | "php": ">=7.0" 33 | }, 34 | "require-dev": { 35 | "phpunit/phpunit": "~5.7" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "DataStructures\\": "DataStructures" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "DataStructures\\Tests\\": "tests/" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | ./tests/Lists 10 | 11 | 12 | ./tests/Trees 13 | 14 | 15 | ./tests/Sets 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/Lists/ArrayListTest.php: -------------------------------------------------------------------------------- 1 | list = new ArrayList(); 14 | } 15 | 16 | public function testSize() { 17 | $this->assertEquals($this->list->size(), 0); 18 | $this->list->push(true); 19 | $this->list->push(2); 20 | $this->assertEquals($this->list->size(), 2); 21 | } 22 | 23 | public function testEmpty() { 24 | $this->assertTrue($this->list->empty()); 25 | } 26 | 27 | public function testPush() { 28 | $this->list->push(20); 29 | $this->assertEquals(1, $this->list->size()); 30 | $this->assertEquals(20, $this->list->get(0)); 31 | $this->list->push(true); 32 | 33 | $this->assertEquals(2, $this->list->size()); 34 | 35 | $this->assertTrue($this->list->get(1)); 36 | 37 | $this->list->push(30); 38 | $this->assertEquals(20, $this->list->get(0)); 39 | $this->assertEquals(30, $this->list->get(1)); 40 | $this->assertEquals(true, $this->list->get(2)); 41 | } 42 | 43 | public function testGetLast() { 44 | // TODO change assert null 45 | $this->assertNull($this->list->getLast()); 46 | $this->list->push(true); 47 | $this->list->push(50); 48 | $this->list->push("string"); 49 | $this->assertEquals("string", $this->list->getLast()); 50 | } 51 | 52 | public function testInsert() { 53 | $this->list->insert(0, 100); 54 | $this->assertEquals(100, $this->list->get(0)); 55 | $this->assertEquals(1, $this->list->size()); 56 | 57 | $this->list->insert(0, 200); 58 | $this->assertEquals(2, $this->list->size()); 59 | $this->assertEquals(200, $this->list->get(0)); 60 | $this->assertEquals(100, $this->list->get(1)); 61 | $this->assertEquals(100, $this->list->getLast()); 62 | 63 | $this->list->insert(1, 300); 64 | $this->assertEquals(3, $this->list->size()); 65 | $this->assertEquals(200, $this->list->get(0)); 66 | $this->assertEquals(300, $this->list->get(1)); 67 | $this->assertEquals(100, $this->list->get(2)); 68 | $this->list->insert(2, 1000); 69 | $this->assertEquals(1000, $this->list->get(2)); 70 | $this->assertEquals(100, $this->list->get(3)); 71 | $this->list->insert(6, true); 72 | $this->assertTrue($this->list->get(4)); 73 | $this->assertEquals(5, $this->list->size()); 74 | } 75 | 76 | public function testDeleteException() { 77 | $this->expectException(\OutOfBoundsException::class); 78 | $this->list->delete(10); 79 | } 80 | 81 | public function testDelete() { 82 | $this->list->push(20); 83 | $this->list->push(true); 84 | $this->list->push(15); 85 | $this->list->push(3.14); 86 | $this->list->push("string"); 87 | 88 | $this->assertEquals($this->list->delete(4), "string"); 89 | $this->assertEquals(3.14, $this->list->delete(3)); 90 | 91 | $this->assertEquals(3, $this->list->size()); 92 | $this->assertEquals(true, $this->list->delete(1)); 93 | $this->assertEquals(false, $this->list->empty()); 94 | $this->assertEquals(20, $this->list->delete(0)); 95 | } 96 | 97 | public function testShift() { 98 | $this->list->push(20); 99 | $this->list->push(true); 100 | $this->assertEquals(20, $this->list->shift()); 101 | $this->assertEquals(1, $this->list->size()); 102 | $this->assertEquals(true, $this->list->shift()); 103 | $this->assertEquals(true, $this->list->empty()); 104 | } 105 | 106 | public function testPop() { 107 | $this->list->push(20); 108 | $this->list->push(true); 109 | $this->list->push(15); 110 | $this->list->push(3.14); 111 | $this->list->push("string"); 112 | 113 | $this->assertEquals($this->list->pop(), "string"); 114 | $this->assertEquals(3.14, $this->list->pop()); 115 | $this->list->insert(1, ['hello']); 116 | $this->assertEquals(15, $this->list->pop()); 117 | $this->assertTrue($this->list->pop()); 118 | $this->assertSame($this->list->pop(), 'hello'); 119 | $this->assertSame($this->list->pop(), 20); 120 | $this->assertTrue($this->list->empty()); 121 | } 122 | 123 | public function testPopException() { 124 | $this->expectException(\OutOfBoundsException::class); 125 | $this->list->pop(); 126 | } 127 | 128 | public function testUnshift() { 129 | $this->list->unshift(999); 130 | $this->assertEquals(1, $this->list->size()); 131 | $this->assertEquals(999, $this->list->get(0)); 132 | $this->assertEquals($this->list->getLast(), 999); 133 | $this->list->unshift(888); 134 | $this->assertEquals(2, $this->list->size()); 135 | $this->assertEquals(888, $this->list->get(0)); 136 | $this->assertEquals(999, $this->list->get(1)); 137 | $this->assertEquals($this->list->getLast(), 999); 138 | $this->list->unshift(777); 139 | $this->assertEquals(3, $this->list->size()); 140 | $this->assertEquals(777, $this->list->get(0)); 141 | $this->assertEquals(888, $this->list->get(1)); 142 | $this->assertEquals(999, $this->list->get(2)); 143 | $this->assertEquals($this->list->getLast(), 999); 144 | } 145 | 146 | public function testGet() { 147 | // TODO add NotFoundException 148 | $this->list->push(20); 149 | $this->list->push(true); 150 | $this->list->push(15); 151 | $this->list->push(3.14); 152 | $this->list->push("string"); 153 | $this->assertEquals($this->list->get(0), 20); 154 | $this->assertTrue($this->list->get(1)); 155 | $this->assertEquals($this->list->get(2), 15); 156 | $this->assertEquals($this->list->get(3), 3.14); 157 | $this->assertEquals($this->list->get(4), "string"); 158 | } 159 | 160 | public function testGetWithException() { 161 | $this->expectException(\OutOfBoundsException::class); 162 | $this->list->get(5); 163 | } 164 | 165 | public function testGetOutOfBound() { 166 | $this->expectException(\OutOfBoundsException::class); 167 | $this->list->get(-1); 168 | } 169 | 170 | public function testGetAll() { 171 | // TODO change assert null 172 | foreach($this->list->getAll() as $node) { 173 | $this->assertNull($node); 174 | } 175 | 176 | $this->list->push(20); 177 | $this->list->push(true); 178 | $this->list->push(15); 179 | $this->list->push(3.14); 180 | $this->list->push("string"); 181 | $data = []; 182 | foreach($this->list->getAll() as $node) { 183 | $data[] = $node; 184 | } 185 | $this->assertCount(5, $data); 186 | $this->assertSame([20, true, 15, 3.14, "string"], $data); 187 | } 188 | 189 | public function testContains() { 190 | $this->assertFalse($this->list->contains(999)); 191 | $this->list->unshift(999); 192 | $this->assertTrue($this->list->contains(999)); 193 | $this->list->unshift(888); 194 | $this->assertTrue($this->list->contains(888)); 195 | $this->list->unshift(777); 196 | $this->assertTrue($this->list->contains(777)); 197 | $this->list->pop(); 198 | $this->assertFalse($this->list->contains(999)); 199 | } 200 | 201 | public function testIndexOf() { 202 | $this->assertFalse($this->list->indexOf(999)); 203 | $this->list->unshift(999); 204 | $this->assertEquals(0, $this->list->indexOf(999)); 205 | $this->list->unshift(888); 206 | $this->assertEquals(0, $this->list->indexOf(888)); 207 | $this->assertEquals(1, $this->list->indexOf(999)); 208 | $this->list->unshift(777); 209 | $this->assertEquals(0, $this->list->indexOf(777)); 210 | $this->assertEquals(1, $this->list->indexOf(888)); 211 | $this->assertEquals(2, $this->list->indexOf(999)); 212 | $this->list->pop(); 213 | $this->assertFalse($this->list->indexOf(999)); 214 | } 215 | 216 | public function testLastIndexOf() { 217 | $this->assertFalse($this->list->lastIndexOf(999)); 218 | $this->list->unshift(999); 219 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 220 | $this->list->unshift(888); 221 | 222 | $this->assertEquals(0, $this->list->lastIndexOf(888)); 223 | $this->assertEquals(1, $this->list->lastIndexOf(999)); 224 | $this->list->unshift(999); 225 | $this->assertFalse($this->list->lastIndexOf(777)); 226 | $this->assertEquals(1, $this->list->lastIndexOf(888)); 227 | $this->assertEquals(2, $this->list->lastIndexOf(999)); 228 | $this->list->pop(); 229 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 230 | } 231 | 232 | public function testRemoveEmpty() { 233 | $this->expectException(NotFoundException::class); 234 | $this->list->remove('string'); 235 | } 236 | 237 | public function testRemove() { 238 | $this->list->push(3.14); 239 | $this->list->push(true); 240 | $this->list->push('string'); 241 | $this->list->push(3.14); 242 | $this->assertEquals(3.14, $this->list->remove(3.14)); 243 | $this->assertTrue($this->list->contains(3.14)); 244 | $this->assertEquals(2, $this->list->indexOf(3.14)); 245 | } 246 | 247 | public function testRemoveAllAndWhenEmpty() { 248 | $this->expectException(NotFoundException::class); 249 | $this->list->push(3.14); 250 | $this->list->push(true); 251 | $this->list->push('string'); 252 | $this->list->push(3.14); 253 | 254 | $this->list->remove(3.14); 255 | $this->list->remove(true); 256 | $this->list->remove('string'); 257 | $this->list->remove(3.14); 258 | 259 | $this->list->remove('string'); 260 | } 261 | 262 | public function testToArray() { 263 | $this->list->push(20); 264 | $this->list->push(true); 265 | $this->list->push(15); 266 | $this->list->push(3.14); 267 | $this->list->push("string"); 268 | $this->list->pop(); 269 | $nodes = $this->list->toArray(); 270 | $this->assertSame([20, true, 15, 3.14], $nodes); 271 | } 272 | 273 | public function testClear() { 274 | $this->list->push(20); 275 | $this->list->push(true); 276 | $this->list->push(15); 277 | $this->list->push(3.14); 278 | $this->list->push("string"); 279 | $this->list->clear(); 280 | $this->assertEmpty($this->list->toArray()); 281 | $this->assertEquals($this->list->size(), 0); 282 | } 283 | 284 | public function testGetAsArray() { 285 | $this->list->push(20); 286 | $this->list->push(true); 287 | $this->list->push(15); 288 | $this->list->push(3.14); 289 | $this->list->push("string"); 290 | $this->assertEquals(20, $this->list[0]); 291 | $this->assertEquals(true, $this->list[1]); 292 | $this->assertEquals(15, $this->list[2]); 293 | $this->assertEquals(3.14, $this->list[3]); 294 | $this->assertEquals('string', $this->list[4]); 295 | } 296 | 297 | public function testOffsetUnset() { 298 | $this->list->push(20); 299 | $this->list->push(true); 300 | $this->list->push(15); 301 | $this->list->push(3.14); 302 | $this->list->push("string"); 303 | unset($this->list[0]); 304 | unset($this->list[1]); 305 | unset($this->list[1]); 306 | unset($this->list[1]); 307 | unset($this->list[0]); 308 | $this->assertEquals(0, $this->list->size()); 309 | } 310 | 311 | public function testIsset() { 312 | $this->list->push(20); 313 | $this->list->push(true); 314 | $this->assertTrue(isset($this->list[0])); 315 | $this->assertTrue(isset($this->list[1])); 316 | } 317 | 318 | public function testIssetFail() { 319 | $this->list->push(20); 320 | $this->list->push(true); 321 | $this->assertFalse(isset($this->list[2])); 322 | } 323 | 324 | public function testOffsetSet() { 325 | $this->list->push(20); 326 | $this->list[1] = 30; 327 | $this->list[] = 40; 328 | $this->list[] = 'string'; 329 | $this->assertEquals(4, $this->list->size()); 330 | } 331 | 332 | public function testIterator() { 333 | $this->list->push(20); 334 | $this->list->push(true); 335 | $this->list->push(15); 336 | $this->list->push(3.14); 337 | $this->list->push("string"); 338 | 339 | $this->list->shift(); 340 | $this->assertEquals(4, count($this->list)); 341 | $expectedResult = []; 342 | $result = []; 343 | foreach($this->list->getAll() as $node) { 344 | $expectedResult[] = $node; 345 | } 346 | 347 | foreach($this->list as $index => $val) { 348 | $result[] = $val; 349 | } 350 | 351 | $this->assertSame($expectedResult, $result); 352 | } 353 | } -------------------------------------------------------------------------------- /tests/Lists/CircularLinkedListTest.php: -------------------------------------------------------------------------------- 1 | list = new CircularLinkedList(); 14 | } 15 | 16 | public function testSize() { 17 | $this->assertEquals($this->list->size(), 0); 18 | $this->list->push(true); 19 | $this->list->push(2); 20 | $this->assertEquals($this->list->size(), 2); 21 | } 22 | 23 | public function testEmpty() { 24 | $this->assertTrue($this->list->empty()); 25 | } 26 | 27 | public function testPush() { 28 | $this->list->push(20); 29 | $this->assertEquals(1, $this->list->size()); 30 | $this->assertEquals(20, $this->list->get(0)); 31 | $this->list->push(true); 32 | 33 | $this->assertEquals(2, $this->list->size()); 34 | 35 | $this->assertTrue($this->list->get(1)); 36 | 37 | $this->list->push(30); 38 | $this->assertEquals(20, $this->list->get(0)); 39 | $this->assertEquals(30, $this->list->get(1)); 40 | $this->assertEquals(true, $this->list->get(2)); 41 | } 42 | 43 | public function testGetLast() { 44 | $this->assertNull($this->list->getLast()); 45 | $this->list->push(true); 46 | $this->list->push(50); 47 | $this->list->push("string"); 48 | $this->assertEquals("string", $this->list->getLast()); 49 | } 50 | 51 | public function testInsert() { 52 | $this->list->insert(0, 100); 53 | $this->assertEquals(100, $this->list->get(0)); 54 | $this->assertEquals(1, $this->list->size()); 55 | 56 | $this->list->insert(0, 200); 57 | $this->assertEquals(2, $this->list->size()); 58 | $this->assertEquals(200, $this->list->get(0)); 59 | $this->assertEquals(100, $this->list->get(1)); 60 | $this->assertEquals($this->list->getLast(), 100); 61 | 62 | $this->list->insert(1, 300); 63 | $this->assertEquals(3, $this->list->size()); 64 | $this->assertEquals(200, $this->list->get(0)); 65 | $this->assertEquals(300, $this->list->get(1)); 66 | $this->assertEquals(100, $this->list->get(2)); 67 | $this->list->insert(2, 1000); 68 | $this->assertEquals($this->list->get(2), 1000); 69 | $this->assertEquals($this->list->get(3), 100); 70 | $this->list->insert(6, true); 71 | $this->assertTrue($this->list->get(4)); 72 | } 73 | 74 | public function testDeleteException() { 75 | $this->expectException(\OutOfBoundsException::class); 76 | $this->list->delete(10); 77 | } 78 | 79 | public function testDelete() { 80 | $this->list->push(20); 81 | $this->list->push(true); 82 | $this->list->push(15); 83 | $this->list->push(3.14); 84 | $this->list->push("string"); 85 | 86 | // echo $this->list->get(0) . $this->list->get(1) . $this->list->get(2) . $this->list->get(3) . $this->list->get(4) . PHP_EOL; 87 | $this->assertEquals($this->list->delete(4), "string"); 88 | $this->assertEquals(3.14, $this->list->delete(3)); 89 | 90 | $this->assertEquals(3, $this->list->size()); 91 | $this->assertEquals(true, $this->list->delete(1)); 92 | $this->assertEquals(false, $this->list->empty()); 93 | $this->assertEquals(20, $this->list->delete(0)); 94 | } 95 | 96 | public function testShift() { 97 | $this->assertNull($this->list->shift()); 98 | $this->list->push(20); 99 | $this->list->push(true); 100 | $this->assertEquals(20, $this->list->shift()); 101 | $this->assertEquals(1, $this->list->size()); 102 | $this->assertEquals(true, $this->list->shift()); 103 | $this->assertEquals(true, $this->list->empty()); 104 | $this->assertNull($this->list->shift()); 105 | } 106 | 107 | public function testPop() { 108 | $this->list->push(20); 109 | $this->list->push(true); 110 | $this->list->push(15); 111 | $this->list->push(3.14); 112 | $this->list->push("string"); 113 | 114 | $this->assertEquals($this->list->pop(), "string"); 115 | $this->assertEquals(3.14, $this->list->pop()); 116 | $this->list->insert(1, ['hello']); 117 | $this->assertEquals(15, $this->list->pop()); 118 | $this->assertTrue($this->list->pop()); 119 | $this->assertSame($this->list->pop(), ['hello']); 120 | $this->assertSame($this->list->pop(), 20); 121 | $this->assertTrue($this->list->empty()); 122 | } 123 | 124 | public function testPopException() { 125 | $this->expectException(\OutOfBoundsException::class); 126 | $this->list->pop(); 127 | } 128 | 129 | public function testUnshift() { 130 | $this->list->unshift(999); 131 | $this->assertEquals(1, $this->list->size()); 132 | $this->assertEquals(999, $this->list->get(0)); 133 | $this->assertEquals($this->list->getLast(), 999); 134 | $this->list->unshift(888); 135 | $this->assertEquals(2, $this->list->size()); 136 | $this->assertEquals(888, $this->list->get(0)); 137 | $this->assertEquals(999, $this->list->get(1)); 138 | $this->assertEquals($this->list->getLast(), 999); 139 | $this->list->unshift(777); 140 | $this->assertEquals(3, $this->list->size()); 141 | $this->assertEquals(777, $this->list->get(0)); 142 | $this->assertEquals(888, $this->list->get(1)); 143 | $this->assertEquals(999, $this->list->get(2)); 144 | $this->assertEquals($this->list->getLast(), 999); 145 | } 146 | 147 | public function testContains() { 148 | $this->assertFalse($this->list->contains(999)); 149 | $this->list->unshift(999); 150 | $this->assertTrue($this->list->contains(999)); 151 | $this->list->unshift(888); 152 | $this->assertTrue($this->list->contains(888)); 153 | $this->list->unshift(777); 154 | $this->assertTrue($this->list->contains(777)); 155 | $this->list->push(1000000); 156 | $this->assertTrue($this->list->contains(1000000)); 157 | $this->list->pop(); 158 | $this->assertFalse($this->list->contains(1000000)); 159 | } 160 | 161 | public function testIndexOf() { 162 | $this->assertFalse($this->list->indexOf(999)); 163 | $this->list->unshift(999); 164 | $this->assertEquals(0, $this->list->indexOf(999)); 165 | $this->list->unshift(888); 166 | 167 | $this->assertEquals(0, $this->list->indexOf(888)); 168 | $this->assertEquals(1, $this->list->indexOf(999)); 169 | $this->list->unshift(777); 170 | $this->assertEquals(0, $this->list->indexOf(777)); 171 | $this->assertEquals(1, $this->list->indexOf(888)); 172 | $this->assertEquals(2, $this->list->indexOf(999)); 173 | $this->list->pop(); 174 | $this->assertFalse($this->list->indexOf(999)); 175 | } 176 | 177 | public function testRemoveEmpty() { 178 | $this->expectException(NotFoundException::class); 179 | $this->list->remove('string'); 180 | } 181 | 182 | public function testRemove() { 183 | $this->list->push(3.14); 184 | $this->list->push(true); 185 | $this->list->push('string'); 186 | $this->list->push(3.14); 187 | 188 | $this->assertEquals(3.14, $this->list->remove(3.14)); 189 | $this->assertTrue($this->list->contains(3.14)); 190 | $this->assertEquals(2, $this->list->indexOf(3.14)); 191 | } 192 | 193 | public function testRemoveAllAndWhenEmpty() { 194 | $this->expectException(NotFoundException::class); 195 | $this->list->push(3.14); 196 | $this->list->push(true); 197 | $this->list->push('string'); 198 | $this->list->push(3.14); 199 | 200 | $this->list->remove(3.14); 201 | $this->list->remove(true); 202 | $this->list->remove('string'); 203 | $this->list->remove(3.14); 204 | 205 | $this->list->remove('string'); 206 | } 207 | 208 | public function testLastIndexOf() { 209 | $this->assertFalse($this->list->lastIndexOf(999)); 210 | $this->list->unshift(999); 211 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 212 | $this->list->unshift(888); 213 | 214 | $this->assertEquals(0, $this->list->lastIndexOf(888)); 215 | $this->assertEquals(1, $this->list->lastIndexOf(999)); 216 | $this->list->unshift(999); 217 | $this->assertFalse($this->list->lastIndexOf(777)); 218 | $this->assertEquals(1, $this->list->lastIndexOf(888)); 219 | $this->assertEquals(2, $this->list->lastIndexOf(999)); 220 | $this->list->pop(); 221 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 222 | } 223 | 224 | public function testGet() { 225 | $this->list->push(20); 226 | $this->list->push(true); 227 | $this->list->push(15); 228 | $this->list->push(3.14); 229 | $this->list->push("string"); 230 | $this->assertEquals($this->list->get(0), 20); 231 | $this->assertTrue($this->list->get(1)); 232 | $this->assertEquals($this->list->get(2), 15); 233 | $this->assertEquals($this->list->get(3), 3.14); 234 | $this->assertEquals($this->list->get(4), "string"); 235 | } 236 | 237 | public function testGetWithException() { 238 | $this->expectException(\OutOfBoundsException::class); 239 | $this->list->get(5); 240 | } 241 | 242 | public function testGetOutOfBound() { 243 | $this->expectException(\OutOfBoundsException::class); 244 | $this->list->get(-1); 245 | } 246 | 247 | public function testGetAll() { 248 | foreach($this->list->getAll() as $node) { 249 | $this->assertNull($node); 250 | } 251 | 252 | $this->list->push(20); 253 | $this->list->push(true); 254 | $this->list->push(15); 255 | $this->list->push(3.14); 256 | $this->list->push("string"); 257 | $data = []; 258 | foreach($this->list->getAll() as $node) { 259 | $data[] = $node; 260 | } 261 | $this->assertCount(5, $data); 262 | $this->assertSame([20, true, 15, 3.14, "string"], $data); 263 | } 264 | 265 | public function testToArray() { 266 | $this->list->push(20); 267 | $this->list->push(true); 268 | $this->list->push(15); 269 | $this->list->push(3.14); 270 | $this->list->push("string"); 271 | $this->list->pop(); 272 | 273 | $nodes = $this->list->toArray(); 274 | $this->assertCount(4, $nodes); 275 | $this->assertSame([20, true, 15, 3.14], $nodes); 276 | } 277 | 278 | public function testClear() { 279 | $this->list->push(20); 280 | $this->list->push(true); 281 | $this->list->push(15); 282 | $this->list->push(3.14); 283 | $this->list->push("string"); 284 | 285 | $this->list->clear(); 286 | $this->assertEmpty($this->list->toArray()); 287 | $this->assertEquals(0, $this->list->size()); 288 | } 289 | 290 | public function testGetAsArray() { 291 | $this->list->push(20); 292 | $this->list->push(true); 293 | $this->list->push(15); 294 | $this->list->push(3.14); 295 | $this->list->push("string"); 296 | $this->assertEquals(20, $this->list[0]); 297 | $this->assertEquals(true, $this->list[1]); 298 | $this->assertEquals(15, $this->list[2]); 299 | $this->assertEquals(3.14, $this->list[3]); 300 | $this->assertEquals('string', $this->list[4]); 301 | } 302 | 303 | public function testOffsetUnset() { 304 | $this->list->push(20); 305 | $this->list->push(true); 306 | $this->list->push(15); 307 | $this->list->push(3.14); 308 | $this->list->push("string"); 309 | unset($this->list[0]); 310 | unset($this->list[1]); 311 | unset($this->list[1]); 312 | unset($this->list[1]); 313 | unset($this->list[0]); 314 | $this->assertEquals(0, $this->list->size()); 315 | } 316 | 317 | public function testIsset() { 318 | $this->list->push(20); 319 | $this->list->push(true); 320 | $this->assertTrue(isset($this->list[0])); 321 | $this->assertTrue(isset($this->list[1])); 322 | $this->assertFalse(isset($this->list[2])); 323 | } 324 | 325 | public function testOffsetSet() { 326 | $this->list->push(20); 327 | $this->list[1] = 30; 328 | $this->list[] = 40; 329 | $this->list[] = 'string'; 330 | $this->assertEquals(4, $this->list->size()); 331 | } 332 | } -------------------------------------------------------------------------------- /tests/Lists/DoublyLinkedListTest.php: -------------------------------------------------------------------------------- 1 | list = new DoublyLinkedList(); 14 | } 15 | 16 | public function testSize() { 17 | $this->assertEquals($this->list->size(), 0); 18 | $this->list->push(true); 19 | $this->list->push(2); 20 | $this->assertEquals($this->list->size(), 2); 21 | } 22 | 23 | public function testEmpty() { 24 | $this->assertTrue($this->list->empty()); 25 | } 26 | 27 | public function testPush() { 28 | $this->list->push(20); 29 | $this->assertEquals(1, $this->list->size()); 30 | $this->assertEquals(20, $this->list->get(0)); 31 | $this->list->push(true); 32 | 33 | $this->assertEquals(2, $this->list->size()); 34 | 35 | $this->assertTrue($this->list->get(1)); 36 | 37 | $this->list->push(30); 38 | $this->assertEquals(20, $this->list->get(0)); 39 | $this->assertEquals(30, $this->list->get(1)); 40 | $this->assertEquals(true, $this->list->get(2)); 41 | } 42 | 43 | public function testGetLast() { 44 | $this->assertNull($this->list->getLast()); 45 | $this->list->push(true); 46 | $this->list->push(50); 47 | $this->list->push("string"); 48 | $this->assertEquals("string", $this->list->getLast()); 49 | } 50 | 51 | public function testInsert() { 52 | $this->list->insert(0, 100); 53 | $this->assertEquals(100, $this->list->get(0)); 54 | $this->assertEquals(1, $this->list->size()); 55 | 56 | $this->list->insert(0, 200); 57 | $this->assertEquals(2, $this->list->size()); 58 | $this->assertEquals(200, $this->list->get(0)); 59 | $this->assertEquals(100, $this->list->get(1)); 60 | $this->assertEquals(100, $this->list->getLast()); 61 | 62 | $this->list->insert(1, 300); 63 | $this->assertEquals(3, $this->list->size()); 64 | $this->assertEquals(200, $this->list->get(0)); 65 | $this->assertEquals(300, $this->list->get(1)); 66 | $this->assertEquals(100, $this->list->get(2)); 67 | $this->list->insert(2, 1000); 68 | $this->assertEquals(1000, $this->list->get(2)); 69 | $this->assertEquals(100, $this->list->get(3)); 70 | $this->list->insert(6, true); 71 | $this->assertTrue($this->list->get(4)); 72 | $this->assertEquals(5, $this->list->size()); 73 | } 74 | 75 | public function testDeleteException() { 76 | $this->expectException(\OutOfBoundsException::class); 77 | $this->list->delete(10); 78 | } 79 | 80 | public function testDelete() { 81 | $this->list->push(20); 82 | $this->list->push(true); 83 | $this->list->push(15); 84 | $this->list->push(3.14); 85 | $this->list->push("string"); 86 | 87 | $this->assertEquals($this->list->delete(4), "string"); 88 | $this->assertEquals(3.14, $this->list->delete(3)); 89 | 90 | $this->assertEquals(3, $this->list->size()); 91 | $this->assertEquals(true, $this->list->delete(1)); 92 | $this->assertEquals(false, $this->list->empty()); 93 | $this->assertEquals(20, $this->list->delete(0)); 94 | } 95 | 96 | public function testShift() { 97 | $this->assertNull($this->list->shift()); 98 | $this->list->push(20); 99 | $this->list->push(true); 100 | $this->assertEquals(20, $this->list->shift()); 101 | $this->assertEquals(1, $this->list->size()); 102 | $this->assertEquals(true, $this->list->shift()); 103 | $this->assertEquals(true, $this->list->empty()); 104 | $this->assertNull($this->list->shift()); 105 | } 106 | 107 | public function testPop() { 108 | $this->list->push(20); 109 | $this->list->push(true); 110 | $this->list->push(15); 111 | $this->list->push(3.14); 112 | $this->list->push("string"); 113 | 114 | $this->assertEquals($this->list->pop(), "string"); 115 | $this->assertEquals(3.14, $this->list->pop()); 116 | $this->list->insert(1, ['hello']); 117 | $this->assertEquals(15, $this->list->pop()); 118 | $this->assertTrue($this->list->pop()); 119 | $this->assertSame($this->list->pop(), ['hello']); 120 | $this->assertSame($this->list->pop(), 20); 121 | $this->assertTrue($this->list->empty()); 122 | } 123 | 124 | public function testPopException() { 125 | $this->expectException(\OutOfBoundsException::class); 126 | $this->list->pop(); 127 | } 128 | 129 | public function testUnshift() { 130 | $this->list->unshift(999); 131 | $this->assertEquals(1, $this->list->size()); 132 | $this->assertEquals(999, $this->list->get(0)); 133 | $this->assertEquals($this->list->getLast(), 999); 134 | $this->list->unshift(888); 135 | $this->assertEquals(2, $this->list->size()); 136 | $this->assertEquals(888, $this->list->get(0)); 137 | $this->assertEquals(999, $this->list->get(1)); 138 | $this->assertEquals($this->list->getLast(), 999); 139 | $this->list->unshift(777); 140 | $this->assertEquals(3, $this->list->size()); 141 | $this->assertEquals(777, $this->list->get(0)); 142 | $this->assertEquals(888, $this->list->get(1)); 143 | $this->assertEquals(999, $this->list->get(2)); 144 | $this->assertEquals($this->list->getLast(), 999); 145 | } 146 | 147 | public function testGet() { 148 | $this->list->push(20); 149 | $this->list->push(true); 150 | $this->list->push(15); 151 | $this->list->push(3.14); 152 | $this->list->push("string"); 153 | $this->assertEquals($this->list->get(0), 20); 154 | $this->assertTrue($this->list->get(1)); 155 | $this->assertEquals($this->list->get(2), 15); 156 | $this->assertEquals($this->list->get(3), 3.14); 157 | $this->assertEquals($this->list->get(4), "string"); 158 | } 159 | 160 | public function testGetWithException() { 161 | $this->expectException(\OutOfBoundsException::class); 162 | $this->list->get(5); 163 | } 164 | 165 | public function testGetOutOfBound() { 166 | $this->expectException(\OutOfBoundsException::class); 167 | $this->list->get(-1); 168 | } 169 | 170 | public function testGetAll() { 171 | foreach($this->list->getAll() as $node) { 172 | $this->assertNull($node); 173 | } 174 | 175 | $this->list->push(20); 176 | $this->list->push(true); 177 | $this->list->push(15); 178 | $this->list->push(3.14); 179 | $this->list->push("string"); 180 | $data = []; 181 | foreach($this->list->getAll() as $node) { 182 | $data[] = $node; 183 | } 184 | $this->assertCount(5, $data); 185 | $this->assertSame([20, true, 15, 3.14, "string"], $data); 186 | } 187 | 188 | public function testToArray() { 189 | $this->list->push(20); 190 | $this->list->push(true); 191 | $this->list->push(15); 192 | $this->list->push(3.14); 193 | $this->list->push("string"); 194 | $this->list->pop(); 195 | $nodes = $this->list->toArray(); 196 | $this->assertSame([20, true, 15, 3.14], $nodes); 197 | } 198 | 199 | public function testClear() { 200 | $this->list->push(20); 201 | $this->list->push(true); 202 | $this->list->push(15); 203 | $this->list->push(3.14); 204 | $this->list->push("string"); 205 | $this->list->clear(); 206 | $this->assertEmpty($this->list->toArray()); 207 | $this->assertEquals($this->list->size(), 0); 208 | } 209 | 210 | public function testGetAsArray() { 211 | $this->list->push(20); 212 | $this->list->push(true); 213 | $this->list->push(15); 214 | $this->list->push(3.14); 215 | $this->list->push("string"); 216 | $this->assertEquals(20, $this->list[0]); 217 | $this->assertEquals(true, $this->list[1]); 218 | $this->assertEquals(15, $this->list[2]); 219 | $this->assertEquals(3.14, $this->list[3]); 220 | $this->assertEquals('string', $this->list[4]); 221 | } 222 | 223 | public function testOffsetUnset() { 224 | $this->list->push(20); 225 | $this->list->push(true); 226 | $this->list->push(15); 227 | $this->list->push(3.14); 228 | $this->list->push("string"); 229 | unset($this->list[0]); 230 | unset($this->list[1]); 231 | unset($this->list[1]); 232 | unset($this->list[1]); 233 | unset($this->list[0]); 234 | $this->assertEquals(0, $this->list->size()); 235 | } 236 | 237 | public function testContains() { 238 | $this->assertFalse($this->list->contains(999)); 239 | $this->list->unshift(999); 240 | $this->assertTrue($this->list->contains(999)); 241 | $this->list->unshift(888); 242 | $this->assertTrue($this->list->contains(888)); 243 | $this->list->unshift(777); 244 | $this->assertTrue($this->list->contains(777)); 245 | $this->list->push(1000000); 246 | $this->assertTrue($this->list->contains(1000000)); 247 | $this->list->pop(); 248 | $this->assertFalse($this->list->contains(1000000)); 249 | } 250 | 251 | public function testIndexOf() { 252 | $this->assertFalse($this->list->indexOf(999)); 253 | $this->list->unshift(999); 254 | $this->assertEquals(0, $this->list->indexOf(999)); 255 | $this->list->unshift(888); 256 | 257 | $this->assertEquals(0, $this->list->indexOf(888)); 258 | $this->assertEquals(1, $this->list->indexOf(999)); 259 | $this->list->unshift(777); 260 | $this->assertEquals(0, $this->list->indexOf(777)); 261 | $this->assertEquals(1, $this->list->indexOf(888)); 262 | $this->assertEquals(2, $this->list->indexOf(999)); 263 | $this->list->pop(); 264 | $this->assertFalse($this->list->indexOf(999)); 265 | } 266 | 267 | public function testLastIndexOf() { 268 | $this->assertFalse($this->list->lastIndexOf(999)); 269 | $this->list->unshift(999); 270 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 271 | $this->list->unshift(888); 272 | 273 | $this->assertEquals(0, $this->list->lastIndexOf(888)); 274 | $this->assertEquals(1, $this->list->lastIndexOf(999)); 275 | $this->list->unshift(999); 276 | $this->assertFalse($this->list->lastIndexOf(777)); 277 | $this->assertEquals(1, $this->list->lastIndexOf(888)); 278 | $this->assertEquals(2, $this->list->lastIndexOf(999)); 279 | $this->list->pop(); 280 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 281 | } 282 | 283 | public function testRemoveEmpty() { 284 | $this->expectException(NotFoundException::class); 285 | $this->list->remove('string'); 286 | } 287 | 288 | public function testRemove() { 289 | $this->list->push(3.14); 290 | $this->list->push(true); 291 | $this->list->push('string'); 292 | $this->list->push(3.14); 293 | 294 | $this->assertEquals(3.14, $this->list->remove(3.14)); 295 | $this->assertTrue($this->list->contains(3.14)); 296 | $this->assertEquals(2, $this->list->indexOf(3.14)); 297 | } 298 | 299 | public function testRemoveAllAndWhenEmpty() { 300 | $this->expectException(NotFoundException::class); 301 | $this->list->push(3.14); 302 | $this->list->push(true); 303 | $this->list->push('string'); 304 | $this->list->push(3.14); 305 | 306 | $this->list->remove(3.14); 307 | $this->list->remove(true); 308 | $this->list->remove('string'); 309 | $this->list->remove(3.14); 310 | 311 | $this->list->remove('string'); 312 | } 313 | 314 | public function testIsset() { 315 | $this->list->push(20); 316 | $this->list->push(true); 317 | $this->assertTrue(isset($this->list[0])); 318 | $this->assertTrue(isset($this->list[1])); 319 | $this->assertFalse(isset($this->list[2])); 320 | } 321 | 322 | public function testOffsetSet() { 323 | $this->list->push(20); 324 | $this->list[1] = 30; 325 | $this->list[] = 40; 326 | $this->list[] = 'string'; 327 | $this->assertEquals(4, $this->list->size()); 328 | } 329 | 330 | public function testIterator() { 331 | $this->list->push(20); 332 | $this->list->push(true); 333 | $this->list->push(15); 334 | $this->list->push(3.14); 335 | $this->list->push("string"); 336 | 337 | $this->list->shift(); 338 | $expectedResult = []; 339 | $result = []; 340 | foreach($this->list->getAll() as $node) { 341 | $expectedResult[] = $node; 342 | } 343 | foreach($this->list as $index => $val) { 344 | $result[] = $val; 345 | } 346 | 347 | $this->assertSame($expectedResult, $result); 348 | } 349 | } -------------------------------------------------------------------------------- /tests/Lists/QueueTest.php: -------------------------------------------------------------------------------- 1 | expectException(\InvalidArgumentException::class); 14 | $this->queue = new Queue(-1); 15 | } 16 | 17 | public function testEnqueueWithMaxSize() { 18 | $this->queue = new Queue(5); 19 | $this->queue->enqueue(1); 20 | $this->assertEquals($this->queue->peek(), 1); 21 | $this->queue->enqueue(2); 22 | $this->assertEquals($this->queue->peek(), 1); 23 | $this->queue->enqueue(3); 24 | $this->assertEquals($this->queue->peek(), 1); 25 | $this->queue->enqueue(4); 26 | $this->queue->enqueue(5); 27 | $this->expectException(FullException::class); 28 | $this->queue->enqueue(6); 29 | } 30 | 31 | public function testEnqueue() { 32 | $this->queue = new Queue(); 33 | $this->queue->enqueue(1); 34 | $this->queue->enqueue(2); 35 | $this->queue->enqueue(3); 36 | $this->queue->enqueue(4); 37 | $this->queue->enqueue(5); 38 | $this->assertEquals($this->queue->size(), 5); 39 | $this->assertEquals($this->queue->peek(), 1); 40 | } 41 | 42 | public function testDequeue() { 43 | $this->queue = new Queue(); 44 | $this->assertNull($this->queue->dequeue()); 45 | $this->queue->enqueue(1); 46 | $this->queue->enqueue(2); 47 | $this->queue->enqueue(3); 48 | $this->queue->enqueue(4); 49 | $this->queue->enqueue(5); 50 | $this->assertEquals($this->queue->dequeue(), 1); 51 | $this->assertEquals($this->queue->dequeue(), 2); 52 | $this->assertEquals($this->queue->dequeue(), 3); 53 | $this->assertEquals($this->queue->dequeue(), 4); 54 | $this->assertEquals($this->queue->dequeue(), 5); 55 | } 56 | 57 | public function testSize() { 58 | $this->queue = new Queue(); 59 | $this->assertEquals($this->queue->size(), 0); 60 | $this->queue->enqueue(1); 61 | $this->assertEquals($this->queue->size(), 1); 62 | $this->queue->enqueue(2); 63 | $this->assertEquals($this->queue->size(), 2); 64 | $this->queue->enqueue(3); 65 | $this->assertEquals($this->queue->size(), 3); 66 | $this->queue->enqueue(4); 67 | $this->assertEquals($this->queue->size(), 4); 68 | $this->queue->enqueue(5); 69 | $this->assertEquals($this->queue->size(), 5); 70 | } 71 | 72 | public function testEmpty() { 73 | $this->queue = new Queue(); 74 | $this->assertTrue($this->queue->empty()); 75 | $this->queue->enqueue(true); 76 | $this->assertFalse($this->queue->empty()); 77 | $this->queue->enqueue("string"); 78 | $this->queue->enqueue(3.14); 79 | $this->queue->dequeue(); 80 | $this->queue->dequeue(); 81 | $this->queue->dequeue(); 82 | $this->assertTrue($this->queue->empty()); 83 | } 84 | 85 | public function testPeek() { 86 | $this->queue = new Queue(); 87 | $this->assertNull($this->queue->peek()); 88 | $this->queue->enqueue(1000); 89 | $this->assertEquals($this->queue->peek(), 1000); 90 | $this->queue->enqueue(false); 91 | $this->assertEquals($this->queue->peek(), 1000); 92 | $this->queue->enqueue(3.14); 93 | $this->assertEquals($this->queue->peek(), 1000); 94 | $this->queue->enqueue(4); 95 | $this->assertEquals($this->queue->dequeue(), 1000); 96 | $this->assertEquals($this->queue->peek(), false); 97 | $this->assertEquals($this->queue->dequeue(), false); 98 | $this->assertEquals($this->queue->peek(), 3.14); 99 | $this->assertEquals($this->queue->dequeue(), 3.14); 100 | $this->assertEquals($this->queue->peek(), 4); 101 | $this->assertEquals($this->queue->dequeue(), 4); 102 | $this->assertNull($this->queue->peek()); 103 | } 104 | 105 | public function testIsFull() { 106 | $this->queue = new Queue(); 107 | $this->assertFalse($this->queue->isFull()); 108 | $this->queue->enqueue(['hello']); 109 | $this->queue->enqueue(false); 110 | $this->queue->enqueue(['hello']); 111 | $this->queue->enqueue(false); 112 | $this->queue->enqueue(['hello']); 113 | $this->queue->enqueue(false); 114 | $this->queue->enqueue(['hello']); 115 | $this->queue->enqueue(false); 116 | $this->assertFalse($this->queue->isFull()); 117 | } 118 | 119 | public function testIsFullWithMaxSize() { 120 | $this->queue = new Queue(2); 121 | $this->queue->enqueue(['hello']); 122 | $this->queue->enqueue(false); 123 | $this->assertTrue($this->queue->isFull()); 124 | $this->queue->dequeue(); 125 | $this->assertFalse($this->queue->isFull()); 126 | } 127 | } -------------------------------------------------------------------------------- /tests/Lists/SinglyLinkedListTest.php: -------------------------------------------------------------------------------- 1 | list = new SinglyLinkedList(); 14 | } 15 | 16 | public function testSize() { 17 | $this->assertEquals($this->list->size(), 0); 18 | } 19 | 20 | public function testEmpty() { 21 | $this->assertTrue($this->list->empty()); 22 | } 23 | 24 | public function testPush() { 25 | $this->list->push(20); 26 | $this->assertEquals(1, $this->list->size()); 27 | $this->assertEquals(20, $this->list->get(0)); 28 | $this->list->push(true); 29 | $this->assertEquals(2, $this->list->size()); 30 | $this->assertTrue($this->list->get(1)); 31 | $this->list->push(30); 32 | $this->assertEquals(30, $this->list->get(2)); 33 | } 34 | 35 | public function testInsert() { 36 | $this->list->insert(0, 100); 37 | $this->assertEquals(100, $this->list->get(0)); 38 | $this->assertEquals(1, $this->list->size()); 39 | 40 | $this->list->insert(0, 200); 41 | $this->assertEquals(2, $this->list->size()); 42 | $this->assertEquals(200, $this->list->get(0)); 43 | $this->assertEquals(100, $this->list->get(1)); 44 | 45 | $this->list->insert(1, 300); 46 | $this->assertEquals(3, $this->list->size()); 47 | $this->assertEquals(200, $this->list->get(0)); 48 | $this->assertEquals(300, $this->list->get(1)); 49 | $this->assertEquals(100, $this->list->get(2)); 50 | 51 | /* 52 | for($i = 0; $i < 100; $i++) { 53 | $this->list->insert($i, $i + 1); 54 | $this->assertEquals($i + 1, $this->list->get($i)); 55 | } 56 | */ 57 | } 58 | 59 | public function testDelete() { 60 | $this->assertNull($this->list->delete(10)); 61 | $this->list->push(20); 62 | $this->list->push(true); 63 | $this->list->push(15); 64 | $this->list->push(3.14); 65 | $this->list->push("string"); 66 | $this->assertEquals(3.14, $this->list->delete(3)); 67 | $this->assertEquals(4, $this->list->size()); 68 | $this->assertEquals(true, $this->list->delete(1)); 69 | $this->assertEquals(false, $this->list->empty()); 70 | $this->assertEquals(20, $this->list->delete(0)); 71 | } 72 | 73 | public function testShift() { 74 | $this->assertNull($this->list->shift()); 75 | $this->list->push(20); 76 | $this->list->push(true); 77 | $this->assertEquals(20, $this->list->shift()); 78 | $this->assertEquals(1, $this->list->size()); 79 | $this->assertEquals(true, $this->list->shift()); 80 | $this->assertEquals(true, $this->list->empty()); 81 | $this->assertNull($this->list->shift()); 82 | } 83 | 84 | public function testPop() { 85 | $this->list->push(20); 86 | $this->list->push(true); 87 | $this->list->push(15); 88 | $this->list->push(3.14); 89 | $this->list->push("string"); 90 | 91 | $this->assertEquals("string", $this->list->pop()); 92 | $this->assertEquals(3.14, $this->list->pop()); 93 | $this->list->insert(1, ['hello']); 94 | $this->assertEquals(15, $this->list->pop()); 95 | $this->assertTrue($this->list->pop()); 96 | $this->assertSame($this->list->pop(), ['hello']); 97 | $this->assertSame($this->list->pop(), 20); 98 | $this->assertTrue($this->list->empty()); 99 | } 100 | 101 | public function testUnshift() { 102 | $this->list->unshift(999); 103 | $this->assertEquals(1, $this->list->size()); 104 | $this->assertEquals(999, $this->list->get(0)); 105 | $this->list->unshift(888); 106 | $this->assertEquals(2, $this->list->size()); 107 | $this->assertEquals(888, $this->list->get(0)); 108 | $this->assertEquals(999, $this->list->get(1)); 109 | $this->list->unshift(777); 110 | $this->assertEquals(3, $this->list->size()); 111 | $this->assertEquals(777, $this->list->get(0)); 112 | $this->assertEquals(888, $this->list->get(1)); 113 | $this->assertEquals(999, $this->list->get(2)); 114 | } 115 | 116 | public function testGetEmpty() { 117 | $this->expectException(\OutOfBoundsException::class); 118 | $this->list->get('anything'); 119 | } 120 | 121 | public function testGet() { 122 | $this->list->push(20); 123 | $this->list->push(true); 124 | $this->list->push(15); 125 | $this->list->push(3.14); 126 | $this->list->push("string"); 127 | $this->assertEquals(20, $this->list->get(0)); 128 | $this->assertTrue($this->list->get(1)); 129 | $this->assertEquals(15, $this->list->get(2)); 130 | $this->assertEquals(3.14, $this->list->get(3)); 131 | $this->assertEquals('string', $this->list->get(4)); 132 | } 133 | 134 | public function testGetAfterClear() { 135 | $this->list->push(20); 136 | $this->list->push(true); 137 | $this->list->push(15); 138 | $this->list->push(3.14); 139 | $this->list->push("string"); 140 | $this->list->clear(); 141 | $this->expectException(\OutOfBoundsException::class); 142 | $this->list->get(0); 143 | } 144 | 145 | public function testGetLast() { 146 | $this->assertNull($this->list->getLast()); 147 | $this->list->push(true); 148 | $this->list->push(50); 149 | $this->list->push("string"); 150 | $this->assertEquals("string", $this->list->getLast()); 151 | $this->list->push([]); 152 | $this->assertEquals([], $this->list->getLast()); 153 | } 154 | 155 | public function testGetAll() { 156 | $this->list->push(20); 157 | $this->list->push(true); 158 | $this->list->push(15); 159 | $this->list->push(3.14); 160 | $this->list->push("string"); 161 | $data = []; 162 | foreach($this->list->getAll() as $node) { 163 | $data[] = $node; 164 | } 165 | $this->assertCount(5, $data); 166 | $this->assertSame([20, true, 15, 3.14, "string"], $data); 167 | } 168 | 169 | public function testContains() { 170 | $this->assertFalse($this->list->contains(999)); 171 | $this->list->unshift(999); 172 | $this->assertTrue($this->list->contains(999)); 173 | $this->list->unshift(888); 174 | $this->assertTrue($this->list->contains(888)); 175 | $this->list->unshift(777); 176 | $this->assertTrue($this->list->contains(777)); 177 | $this->list->pop(); 178 | $this->assertFalse($this->list->contains(999)); 179 | } 180 | 181 | public function testIndexOf() { 182 | $this->assertFalse($this->list->indexOf(999)); 183 | $this->list->unshift(999); 184 | $this->assertEquals(0, $this->list->indexOf(999)); 185 | $this->list->unshift(888); 186 | $this->assertEquals(0, $this->list->indexOf(888)); 187 | $this->assertEquals(1, $this->list->indexOf(999)); 188 | $this->list->unshift(777); 189 | $this->assertEquals(0, $this->list->indexOf(777)); 190 | $this->assertEquals(1, $this->list->indexOf(888)); 191 | $this->assertEquals(2, $this->list->indexOf(999)); 192 | $this->list->pop(); 193 | $this->assertFalse($this->list->indexOf(999)); 194 | } 195 | 196 | public function testLastIndexOf() { 197 | $this->assertFalse($this->list->lastIndexOf(999)); 198 | $this->list->unshift(999); 199 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 200 | $this->list->unshift(888); 201 | 202 | $this->assertEquals(0, $this->list->lastIndexOf(888)); 203 | $this->assertEquals(1, $this->list->lastIndexOf(999)); 204 | $this->list->unshift(999); 205 | $this->assertFalse($this->list->lastIndexOf(777)); 206 | $this->assertEquals(1, $this->list->lastIndexOf(888)); 207 | $this->assertEquals(2, $this->list->lastIndexOf(999)); 208 | $this->list->pop(); 209 | $this->assertEquals(0, $this->list->lastIndexOf(999)); 210 | } 211 | 212 | public function testRemoveEmpty() { 213 | $this->expectException(NotFoundException::class); 214 | $this->list->remove('string'); 215 | } 216 | 217 | public function testRemove() { 218 | $this->list->push(3.14); 219 | $this->list->push(true); 220 | $this->list->push('string'); 221 | $this->list->push(3.14); 222 | 223 | $this->assertEquals(3.14, $this->list->remove(3.14)); 224 | $this->assertTrue($this->list->contains(3.14)); 225 | $this->assertEquals(2, $this->list->indexOf(3.14)); 226 | } 227 | 228 | public function testRemoveAllAndWhenEmpty() { 229 | $this->expectException(NotFoundException::class); 230 | $this->list->push(3.14); 231 | $this->list->push(true); 232 | $this->list->push('string'); 233 | $this->list->push(3.14); 234 | 235 | $this->list->remove(3.14); 236 | $this->list->remove(true); 237 | $this->list->remove('string'); 238 | $this->list->remove(3.14); 239 | 240 | $this->list->remove('string'); 241 | } 242 | 243 | public function testToArray() { 244 | $this->list->push(20); 245 | $this->list->push(true); 246 | $this->list->push(15); 247 | $this->list->push(3.14); 248 | $this->list->push("string"); 249 | $this->list->pop(); 250 | $nodes = $this->list->toArray(); 251 | $this->assertSame([20, true, 15, 3.14], $nodes); 252 | } 253 | 254 | public function testClear() { 255 | $this->list->push(20); 256 | $this->list->push(true); 257 | $this->list->push(15); 258 | $this->list->push(3.14); 259 | $this->list->push("string"); 260 | $this->list->clear(); 261 | $this->assertEmpty($this->list->toArray()); 262 | $this->assertEquals($this->list->size(), 0); 263 | } 264 | 265 | public function testGetAsArray() { 266 | $this->list->push(20); 267 | $this->list->push(true); 268 | $this->list->push(15); 269 | $this->list->push(3.14); 270 | $this->list->push("string"); 271 | $this->assertEquals(20, $this->list[0]); 272 | $this->assertEquals(true, $this->list[1]); 273 | $this->assertEquals(15, $this->list[2]); 274 | $this->assertEquals(3.14, $this->list[3]); 275 | $this->assertEquals('string', $this->list[4]); 276 | } 277 | 278 | public function testOffsetUnset() { 279 | $this->list->push(20); 280 | $this->list->push(true); 281 | $this->list->push(15); 282 | $this->list->push(3.14); 283 | $this->list->push("string"); 284 | unset($this->list[0]); 285 | unset($this->list[1]); 286 | unset($this->list[1]); 287 | unset($this->list[1]); 288 | unset($this->list[0]); 289 | $this->assertEquals(0, $this->list->size()); 290 | } 291 | 292 | public function testIsset() { 293 | $this->list->push(20); 294 | $this->list->push(true); 295 | $this->assertTrue(isset($this->list[0])); 296 | $this->assertTrue(isset($this->list[1])); 297 | $this->assertFalse(isset($this->list[2])); 298 | } 299 | 300 | public function testOffsetSet() { 301 | $this->list->push(20); 302 | $this->list[1] = 30; 303 | $this->list[] = 40; 304 | $this->list[] = 'string'; 305 | $this->assertEquals(4, $this->list->size()); 306 | } 307 | } -------------------------------------------------------------------------------- /tests/Lists/StackTest.php: -------------------------------------------------------------------------------- 1 | expectException(\InvalidArgumentException::class); 14 | $this->stack = new Stack(-1); 15 | } 16 | 17 | public function testPushWithMaxSize() { 18 | $this->stack = new Stack(5); 19 | $this->stack->push(1); 20 | $this->assertEquals($this->stack->peek(), 1); 21 | $this->stack->push(2); 22 | $this->assertEquals($this->stack->peek(), 2); 23 | $this->stack->push(3); 24 | $this->assertEquals($this->stack->peek(), 3); 25 | $this->stack->push(4); 26 | $this->stack->push(5); 27 | $this->expectException(FullException::class); 28 | $this->stack->push(6); 29 | } 30 | 31 | public function testPush() { 32 | $this->stack = new Stack(); 33 | $this->stack->push(1); 34 | $this->stack->push(2); 35 | $this->stack->push(3); 36 | $this->stack->push(4); 37 | $this->stack->push(5); 38 | $this->assertEquals($this->stack->size(), 5); 39 | $this->assertEquals($this->stack->peek(), 5); 40 | } 41 | 42 | public function testPop() { 43 | $this->stack = new Stack(); 44 | $this->assertNull($this->stack->pop()); 45 | $this->stack->push(1); 46 | $this->stack->push(2); 47 | $this->stack->push(3); 48 | $this->stack->push(4); 49 | $this->stack->push(5); 50 | $this->assertEquals($this->stack->pop(), 5); 51 | $this->assertEquals($this->stack->pop(), 4); 52 | $this->assertEquals($this->stack->pop(), 3); 53 | $this->assertEquals($this->stack->pop(), 2); 54 | $this->assertEquals($this->stack->pop(), 1); 55 | } 56 | 57 | public function testDequeueWithMaxSize() { 58 | $this->stack = new Stack(4); 59 | $this->stack->push(1); 60 | $this->stack->push(2); 61 | $this->stack->push(3); 62 | $this->stack->push(5); 63 | $this->expectException(FullException::class); 64 | $this->stack->push(6); 65 | } 66 | 67 | public function testSize() { 68 | $this->stack = new Stack(); 69 | $this->assertEquals($this->stack->size(), 0); 70 | $this->stack->push(1); 71 | $this->assertEquals($this->stack->size(), 1); 72 | $this->stack->push(2); 73 | $this->assertEquals($this->stack->size(), 2); 74 | $this->stack->push(3); 75 | $this->assertEquals($this->stack->size(), 3); 76 | $this->stack->push(4); 77 | $this->assertEquals($this->stack->size(), 4); 78 | $this->stack->push(5); 79 | $this->assertEquals($this->stack->size(), 5); 80 | } 81 | 82 | public function testEmpty() { 83 | $this->stack = new Stack(); 84 | $this->assertTrue($this->stack->empty()); 85 | $this->stack->push(true); 86 | $this->assertFalse($this->stack->empty()); 87 | $this->stack->push("string"); 88 | $this->stack->push(3.14); 89 | $this->stack->pop(); 90 | $this->stack->pop(); 91 | $this->stack->pop(); 92 | $this->assertTrue($this->stack->empty()); 93 | } 94 | 95 | public function testPeek() { 96 | $this->stack = new Stack(); 97 | $this->assertNull($this->stack->peek()); 98 | $this->stack->push(1000); 99 | $this->assertEquals($this->stack->peek(), 1000); 100 | $this->stack->push(false); 101 | $this->assertEquals($this->stack->peek(), false); 102 | $this->stack->push(3.14); 103 | $this->assertEquals($this->stack->peek(), 3.14); 104 | $this->stack->push(4); 105 | $this->assertEquals($this->stack->pop(), 4); 106 | $this->assertEquals($this->stack->peek(), 3.14); 107 | $this->assertEquals($this->stack->pop(), 3.14); 108 | $this->assertEquals($this->stack->peek(), false); 109 | $this->assertEquals($this->stack->pop(), false); 110 | $this->assertEquals($this->stack->peek(), 1000); 111 | $this->assertEquals($this->stack->pop(), 1000); 112 | $this->assertNull($this->stack->peek()); 113 | } 114 | 115 | public function testIsFull() { 116 | $this->stack = new Stack(); 117 | $this->assertFalse($this->stack->isFull()); 118 | $this->stack->push(['hello']); 119 | $this->stack->push(false); 120 | $this->stack->push(['hello']); 121 | $this->stack->push(false); 122 | $this->stack->push(['hello']); 123 | $this->stack->push(false); 124 | $this->stack->push(['hello']); 125 | $this->stack->push(false); 126 | $this->assertFalse($this->stack->isFull()); 127 | } 128 | 129 | public function testIsFullWithMaxSize() { 130 | $this->stack = new Stack(2); 131 | $this->stack->push(['hello']); 132 | $this->stack->push(false); 133 | $this->assertTrue($this->stack->isFull()); 134 | $this->stack->pop(); 135 | $this->assertFalse($this->stack->isFull()); 136 | } 137 | } -------------------------------------------------------------------------------- /tests/Sets/DisjointSetTest.php: -------------------------------------------------------------------------------- 1 | set = new DisjointSet(); 13 | } 14 | 15 | public function testMakeSet() { 16 | $sets = []; 17 | $sets[] = $this->set->makeSet('hello'); 18 | $sets[] = $this->set->makeSet('goodbye'); 19 | $this->assertEquals($sets[0], $this->set->makeSet('hello')); 20 | $this->assertEquals($sets[1], $this->set->makeSet('goodbye')); 21 | } 22 | 23 | public function testUnion() { 24 | 25 | $this->set->makeSet('hello'); 26 | $this->set->makeSet('goodbye'); 27 | $this->set->makeSet([true, 3.14]); 28 | 29 | $this->set->union(0, 1); 30 | $this->assertEquals(0, $this->set->find(0)); 31 | $this->set->union(0, 1); 32 | $this->assertEquals(2, $this->set->find(2)); 33 | $this->set->union(1, 2); 34 | $this->set->find(2); 35 | 36 | $this->set->makeSet(3.14); 37 | $this->assertEquals(3, $this->set->find(3)); 38 | $this->set->union(2, 3); 39 | $this->assertEquals(0, $this->set->find(3)); 40 | } 41 | } -------------------------------------------------------------------------------- /tests/Trees/AVLTreeTest.php: -------------------------------------------------------------------------------- 1 | tree = new AVLTree(); 13 | } 14 | 15 | public function testPut() { 16 | $this->tree->put(24, "Siro"); 17 | $this->tree->put(19, "Clara"); 18 | $this->tree->put(51, "Elisa"); 19 | $this->assertEquals(3, $this->tree->size()); 20 | $this->assertEquals("Siro", $this->tree->get(24)); 21 | $this->assertEquals("Clara", $this->tree->get(19)); 22 | $this->assertEquals("Elisa", $this->tree->get(51)); 23 | $this->assertNull($this->tree->get(100)); 24 | 25 | $this->tree->put(17, "Carmen"); 26 | $this->tree->put(18, "Celia"); 27 | $this->assertEquals("Carmen", $this->tree->get(17)); 28 | $this->assertEquals("Celia", $this->tree->get(18)); 29 | } 30 | 31 | public function testPutWithUpdate() { 32 | $this->tree->put(24, "Siro"); 33 | $this->tree->put(19, "Clara"); 34 | $this->tree->put(51, "Elisa"); 35 | $this->assertEquals(3, $this->tree->size()); 36 | $this->tree->put(51, "Pepe", true); 37 | $this->tree->put(24, "John", true); 38 | $this->assertEquals("John", $this->tree->get(24)); 39 | $this->assertEquals("Clara", $this->tree->get(19)); 40 | $this->assertEquals("Pepe", $this->tree->get(51)); 41 | } 42 | 43 | public function testPutOrUpdate() { 44 | $this->tree->put(24, "Siro"); 45 | $this->tree->put(19, "Clara"); 46 | $this->tree->put(51, "Elisa"); 47 | $this->assertEquals(3, $this->tree->size()); 48 | $this->tree->putOrUpdate(24, "John", true); 49 | $this->assertEquals(3, $this->tree->size()); 50 | $this->tree->putOrUpdate(34, "Ana"); 51 | $this->assertEquals(4, $this->tree->size()); 52 | $this->assertEquals("John", $this->tree->get(24)); 53 | $this->assertEquals("Clara", $this->tree->get(19)); 54 | $this->assertEquals("Elisa", $this->tree->get(51)); 55 | $this->assertEquals("Ana", $this->tree->get(34)); 56 | } 57 | 58 | public function testEmpty() { 59 | $this->assertTrue($this->tree->empty()); 60 | $this->tree->put(24, "Siro"); 61 | $this->tree->put(19, "Clara"); 62 | $this->tree->put(51, "Elisa"); 63 | $this->assertFalse($this->tree->empty()); 64 | } 65 | 66 | public function testSize() { 67 | $this->assertEquals(0, $this->tree->size()); 68 | $this->tree->put(24, "Siro"); 69 | $this->assertEquals(1, $this->tree->size()); 70 | $this->tree->put(19, "Clara"); 71 | $this->assertEquals(2, $this->tree->size()); 72 | $this->tree->put(51, "Elisa"); 73 | $this->assertEquals(3, $this->tree->size()); 74 | } 75 | 76 | public function testExists() { 77 | $this->assertFalse($this->tree->exists("greet")); 78 | $this->tree->put("greet", "saludo"); 79 | $this->tree->put("tree", "árbol"); 80 | $this->tree->put("water", "agua"); 81 | $this->assertTrue($this->tree->exists("greet")); 82 | $this->assertTrue($this->tree->exists("tree")); 83 | $this->assertTrue($this->tree->exists("water")); 84 | $this->assertFalse($this->tree->exists("sun")); 85 | } 86 | 87 | public function testSearch() { 88 | $this->tree->put("greet", "saludo"); 89 | $this->tree->put("tree", "árbol"); 90 | $this->tree->put("water", "agua"); 91 | $this->assertEquals("greet", $this->tree->search("greet")->key); 92 | $this->assertEquals("árbol", $this->tree->search("tree")->data); 93 | $this->assertEquals("water", $this->tree->search("water")->key); 94 | $this->assertNull($this->tree->search(100)); 95 | } 96 | 97 | public function testMin() { 98 | $this->tree->put(2, "two"); 99 | $this->tree->put(3, "three"); 100 | $this->assertEquals(2, $this->tree->min()->key); 101 | $this->tree->put(1, "one"); 102 | $this->assertEquals(1, $this->tree->min()->key); 103 | } 104 | 105 | public function testMax() { 106 | $this->tree->put(1, "one"); 107 | $this->assertEquals(1, $this->tree->max()->key); 108 | $this->tree->put(2, "two"); 109 | $this->assertEquals(2, $this->tree->max()->key); 110 | $this->tree->put(3, "three"); 111 | $this->assertEquals(3, $this->tree->max()->key); 112 | } 113 | 114 | public function testDelete() { 115 | $this->tree->put(1, "one"); 116 | $this->tree->put(2, "two"); 117 | $this->tree->put(3, "three"); 118 | $this->assertEquals(1, $this->tree->delete(1)->key); 119 | $this->assertEquals(2, $this->tree->size()); 120 | $this->assertEquals("three", $this->tree->delete(3)->data); 121 | $this->assertEquals(1, $this->tree->size()); 122 | $this->assertEquals(2, $this->tree->delete(2)->key); 123 | $this->assertEquals(0, $this->tree->size()); 124 | $this->assertNull($this->tree->delete(2)); 125 | $this->assertTrue($this->tree->empty()); 126 | } 127 | 128 | public function testDeleteWithBalance() { 129 | $this->tree->put(1, "Clara"); 130 | $this->tree->put(0, "Siro"); 131 | $this->tree->put(2, "Elisa"); 132 | $this->tree->put(3, "Pepe"); 133 | 134 | $this->tree->delete(0); 135 | $this->tree->preorder(function($node) { 136 | // echo $node->data; 137 | }); 138 | $this->assertNull($this->tree->search(0)); 139 | 140 | $this->tree->put(0, "Jose"); 141 | $this->tree->delete(3); 142 | /* 143 | $this->tree->preorder(function($node) { 144 | echo $node->data; 145 | }); 146 | */ 147 | $this->assertNull($this->tree->search(3)); 148 | } 149 | 150 | public function testDeleteWithBalanceWorstCase() { 151 | $this->tree->put(3, 3); 152 | $this->tree->put(2, 2); 153 | $this->tree->put(7, 7); 154 | $this->tree->put(4, 4); 155 | $this->tree->put(8, 8); 156 | 157 | $this->tree->delete(3); 158 | $this->tree->preorder(function($node) { 159 | // echo $node->data; 160 | }); 161 | $this->assertNull($this->tree->search(3)); 162 | } 163 | 164 | public function testDeleteWithRLRotation() { 165 | $this->tree->put(1, "Clara"); 166 | $this->tree->put(0, "Siro"); 167 | $this->tree->put(3, "Elisa"); 168 | $this->tree->put(2, "Pepe"); 169 | 170 | $this->tree->delete(0); 171 | /* 172 | $this->tree->preorder(function($node) { 173 | echo $node->data; 174 | }); 175 | */ 176 | $this->assertNull($this->tree->search(0)); 177 | } 178 | 179 | public function testDeleteMin() { 180 | $this->tree->put(1, "one"); 181 | $this->tree->put(2, "two"); 182 | $this->tree->put(3, "three"); 183 | $this->assertEquals(1, $this->tree->deleteMin()->key); 184 | $this->assertEquals(2, $this->tree->size()); 185 | $this->assertEquals(2, $this->tree->deleteMin()->key); 186 | $this->assertEquals(1, $this->tree->size()); 187 | $this->tree->put(100, "hundred"); 188 | $this->assertEquals(2, $this->tree->size()); 189 | $this->assertEquals(3, $this->tree->deleteMin()->key); 190 | $this->assertEquals(1, $this->tree->size()); 191 | $this->assertEquals(100, $this->tree->deleteMin()->key); 192 | $this->assertEquals(0, $this->tree->size()); 193 | } 194 | 195 | public function testDeleteMax() { 196 | $this->tree->put(1, "one"); 197 | $this->tree->put(2, "two"); 198 | $this->tree->put(3, "three"); 199 | $this->assertEquals(3, $this->tree->deleteMax()->key); 200 | $this->assertEquals(2, $this->tree->size()); 201 | $this->assertEquals(2, $this->tree->deleteMax()->key); 202 | $this->assertEquals(1, $this->tree->size()); 203 | $this->tree->put(100, "hundred"); 204 | $this->assertEquals(2, $this->tree->size()); 205 | $this->assertEquals(100, $this->tree->deleteMax()->key); 206 | $this->assertEquals(1, $this->tree->size()); 207 | $this->assertEquals(1, $this->tree->deleteMax()->key); 208 | $this->assertEquals(0, $this->tree->size()); 209 | } 210 | 211 | public function testPreorder() { 212 | $this->tree->put(2, "two"); 213 | $this->tree->put(1, "one"); 214 | $this->tree->put(3, "three"); 215 | $this->tree->preorder(function ($node) { 216 | // do something with the node 217 | // echo $node->key . PHP_EOL; 218 | }); 219 | $this->assertTrue(true); 220 | } 221 | 222 | public function testInorder() { 223 | $this->tree->put(2, "two"); 224 | $this->tree->put(1, "one"); 225 | $this->tree->put(3, "three"); 226 | $this->tree->inorder(function ($node) { 227 | // do something with the node 228 | // echo $node->key . PHP_EOL; 229 | }); 230 | $this->assertTrue(true); 231 | } 232 | 233 | public function testPostorder() { 234 | $this->tree->put(2, "two"); 235 | $this->tree->put(1, "one"); 236 | $this->tree->put(3, "three"); 237 | $this->tree->postorder(function ($node) { 238 | // do something with the node 239 | // echo $node->key . PHP_EOL; 240 | }); 241 | $this->assertTrue(true); 242 | } 243 | 244 | public function testIsLeaf() { 245 | $this->tree->put(2, "two"); 246 | $this->tree->put(1, "one"); 247 | $this->tree->put(3, "three"); 248 | 249 | $this->assertTrue($this->tree->isLeaf($this->tree->search(3))); 250 | $this->assertFalse($this->tree->isLeaf($this->tree->search(2))); 251 | $this->assertTrue($this->tree->isLeaf($this->tree->search(1))); 252 | } 253 | 254 | public function testIsRoot() { 255 | $this->tree->put(2, "two"); 256 | $this->tree->put(1, "one"); 257 | $this->tree->put(3, "three"); 258 | 259 | $this->assertTrue($this->tree->isRoot($this->tree->search(2))); 260 | $this->assertFalse($this->tree->isRoot($this->tree->search(3))); 261 | $this->assertFalse($this->tree->isRoot($this->tree->search(1))); 262 | } 263 | } -------------------------------------------------------------------------------- /tests/Trees/BinarySearchTreeTest.php: -------------------------------------------------------------------------------- 1 | tree = new BinarySearchTree(); 13 | } 14 | 15 | public function testPut() { 16 | $this->tree->put(24, "Siro"); 17 | $this->tree->put(19, "Clara"); 18 | $this->tree->put(51, "Elisa"); 19 | $this->assertEquals(3, $this->tree->size()); 20 | $this->assertEquals("Siro", $this->tree->get(24)); 21 | $this->assertEquals("Clara", $this->tree->get(19)); 22 | $this->assertEquals("Elisa", $this->tree->get(51)); 23 | $this->assertNull($this->tree->get(100)); 24 | } 25 | 26 | public function testPutWithUpdate() { 27 | $this->tree->put(24, "Siro"); 28 | $this->tree->put(19, "Clara"); 29 | $this->tree->put(51, "Elisa"); 30 | $this->assertEquals(3, $this->tree->size()); 31 | $this->tree->put(51, "Pepe", true); 32 | $this->tree->put(24, "John", true); 33 | $this->assertEquals("John", $this->tree->get(24)); 34 | $this->assertEquals("Clara", $this->tree->get(19)); 35 | $this->assertEquals("Pepe", $this->tree->get(51)); 36 | } 37 | 38 | public function testPutOrUpdate() { 39 | $this->tree->put(24, "Siro"); 40 | $this->tree->put(19, "Clara"); 41 | $this->tree->put(51, "Elisa"); 42 | $this->assertEquals(3, $this->tree->size()); 43 | $this->tree->putOrUpdate(24, "John", true); 44 | $this->assertEquals(3, $this->tree->size()); 45 | $this->tree->putOrUpdate(34, "Ana"); 46 | $this->assertEquals(4, $this->tree->size()); 47 | $this->assertEquals("John", $this->tree->get(24)); 48 | $this->assertEquals("Clara", $this->tree->get(19)); 49 | $this->assertEquals("Elisa", $this->tree->get(51)); 50 | $this->assertEquals("Ana", $this->tree->get(34)); 51 | } 52 | 53 | public function testEmpty() { 54 | $this->assertTrue($this->tree->empty()); 55 | $this->tree->put(24, "Siro"); 56 | $this->tree->put(19, "Clara"); 57 | $this->tree->put(51, "Elisa"); 58 | $this->assertFalse($this->tree->empty()); 59 | } 60 | 61 | public function testSize() { 62 | $this->assertEquals(0, $this->tree->size()); 63 | $this->tree->put(24, "Siro"); 64 | $this->assertEquals(1, $this->tree->size()); 65 | $this->tree->put(19, "Clara"); 66 | $this->assertEquals(2, $this->tree->size()); 67 | $this->tree->put(51, "Elisa"); 68 | $this->assertEquals(3, $this->tree->size()); 69 | } 70 | 71 | public function testExists() { 72 | $this->assertFalse($this->tree->exists("greet")); 73 | $this->tree->put("greet", "saludo"); 74 | $this->tree->put("tree", "árbol"); 75 | $this->tree->put("water", "agua"); 76 | $this->assertTrue($this->tree->exists("greet")); 77 | $this->assertTrue($this->tree->exists("tree")); 78 | $this->assertTrue($this->tree->exists("water")); 79 | $this->assertFalse($this->tree->exists("sun")); 80 | } 81 | 82 | public function testSearch() { 83 | $this->tree->put("greet", "saludo"); 84 | $this->tree->put("tree", "árbol"); 85 | $this->tree->put("water", "agua"); 86 | $this->assertEquals("greet", $this->tree->search("greet")->key); 87 | $this->assertEquals("árbol", $this->tree->search("tree")->data); 88 | $this->assertEquals("water", $this->tree->search("water")->key); 89 | $this->assertNull($this->tree->search(100)); 90 | } 91 | 92 | public function testMin() { 93 | $this->tree->put(2, "two"); 94 | $this->tree->put(3, "three"); 95 | $this->assertEquals(2, $this->tree->min()->key); 96 | $this->tree->put(1, "one"); 97 | $this->assertEquals(1, $this->tree->min()->key); 98 | } 99 | 100 | public function testMax() { 101 | $this->tree->put(1, "one"); 102 | $this->assertEquals(1, $this->tree->max()->key); 103 | $this->tree->put(2, "two"); 104 | $this->assertEquals(2, $this->tree->max()->key); 105 | $this->tree->put(3, "three"); 106 | $this->assertEquals(3, $this->tree->max()->key); 107 | } 108 | 109 | public function testDelete() { 110 | $this->tree->put(1, "one"); 111 | $this->tree->put(2, "two"); 112 | $this->tree->put(3, "three"); 113 | $this->assertEquals(1, $this->tree->delete(1)->key); 114 | $this->assertEquals(2, $this->tree->size()); 115 | $this->assertEquals("three", $this->tree->delete(3)->data); 116 | $this->assertEquals(1, $this->tree->size()); 117 | $this->assertEquals(2, $this->tree->delete(2)->key); 118 | $this->assertEquals(0, $this->tree->size()); 119 | $this->assertNull($this->tree->delete(2)); 120 | $this->assertTrue($this->tree->empty()); 121 | } 122 | 123 | public function testDeleteMin() { 124 | $this->tree->put(1, "one"); 125 | $this->tree->put(2, "two"); 126 | $this->tree->put(3, "three"); 127 | $this->assertEquals(1, $this->tree->deleteMin()->key); 128 | $this->assertEquals(2, $this->tree->size()); 129 | $this->assertEquals(2, $this->tree->deleteMin()->key); 130 | $this->assertEquals(1, $this->tree->size()); 131 | $this->tree->put(100, "hundred"); 132 | $this->assertEquals(2, $this->tree->size()); 133 | $this->assertEquals(3, $this->tree->deleteMin()->key); 134 | $this->assertEquals(1, $this->tree->size()); 135 | $this->assertEquals(100, $this->tree->deleteMin()->key); 136 | $this->assertEquals(0, $this->tree->size()); 137 | } 138 | 139 | public function testDeleteMax() { 140 | $this->tree->put(1, "one"); 141 | $this->tree->put(2, "two"); 142 | $this->tree->put(3, "three"); 143 | $this->assertEquals(3, $this->tree->deleteMax()->key); 144 | $this->assertEquals(2, $this->tree->size()); 145 | $this->assertEquals(2, $this->tree->deleteMax()->key); 146 | $this->assertEquals(1, $this->tree->size()); 147 | $this->tree->put(100, "hundred"); 148 | $this->assertEquals(2, $this->tree->size()); 149 | $this->assertEquals(100, $this->tree->deleteMax()->key); 150 | $this->assertEquals(1, $this->tree->size()); 151 | $this->assertEquals(1, $this->tree->deleteMax()->key); 152 | $this->assertEquals(0, $this->tree->size()); 153 | } 154 | 155 | public function testPreorder() { 156 | $this->tree->put(2, "two"); 157 | $this->tree->put(1, "one"); 158 | $this->tree->put(3, "three"); 159 | $this->tree->preorder(function ($node) { 160 | // do something with the node 161 | // echo $node->key . PHP_EOL; 162 | }); 163 | $this->assertTrue(true); 164 | } 165 | 166 | public function testInorder() { 167 | $this->tree->put(2, "two"); 168 | $this->tree->put(1, "one"); 169 | $this->tree->put(3, "three"); 170 | $this->tree->inorder(function ($node) { 171 | // do something with the node 172 | // echo $node->key . PHP_EOL; 173 | }); 174 | $this->assertTrue(true); 175 | } 176 | 177 | public function testPostorder() { 178 | $this->tree->put(2, "two"); 179 | $this->tree->put(1, "one"); 180 | $this->tree->put(3, "three"); 181 | $this->tree->postorder(function ($node) { 182 | // do something with the node 183 | // echo $node->key . PHP_EOL; 184 | }); 185 | $this->assertTrue(true); 186 | } 187 | 188 | public function testIsLeaf() { 189 | $this->tree->put(2, "two"); 190 | $this->tree->put(1, "one"); 191 | $this->tree->put(3, "three"); 192 | 193 | $this->assertTrue($this->tree->isLeaf($this->tree->search(3))); 194 | $this->assertFalse($this->tree->isLeaf($this->tree->search(2))); 195 | $this->assertTrue($this->tree->isLeaf($this->tree->search(1))); 196 | } 197 | 198 | public function testIsRoot() { 199 | $this->tree->put(2, "two"); 200 | $this->tree->put(1, "one"); 201 | $this->tree->put(3, "three"); 202 | 203 | $this->assertTrue($this->tree->isRoot($this->tree->search(2))); 204 | $this->assertFalse($this->tree->isRoot($this->tree->search(3))); 205 | $this->assertFalse($this->tree->isRoot($this->tree->search(1))); 206 | } 207 | } -------------------------------------------------------------------------------- /tests/Trees/TrieTreeTest.php: -------------------------------------------------------------------------------- 1 | tree = new TrieTree(); 13 | } 14 | 15 | public function testAdd() { 16 | $this->tree->add('hello'); 17 | $this->tree->add('bye'); 18 | $this->assertEquals(8, $this->tree->size()); 19 | $this->tree->add('hello'); 20 | $this->assertEquals(8, $this->tree->size()); 21 | $this->tree->add('hell'); 22 | $this->assertEquals(8, $this->tree->size()); 23 | } 24 | 25 | public function testWordCount() { 26 | $this->tree->add('hello'); 27 | $this->assertEquals(1, $this->tree->wordCount()); 28 | $this->tree->add('bye'); 29 | $this->assertEquals(2, $this->tree->wordCount()); 30 | $this->tree->add('hello'); 31 | $this->assertEquals(2, $this->tree->wordCount()); 32 | $this->tree->add('hell'); 33 | $this->assertEquals(3, $this->tree->wordCount()); 34 | } 35 | 36 | public function testContains() { 37 | $this->tree->add('hello'); 38 | $this->assertTrue($this->tree->contains('hello')); 39 | $this->tree->add('bye'); 40 | $this->assertTrue($this->tree->contains('bye')); 41 | $this->assertFalse($this->tree->contains('what')); 42 | } 43 | 44 | public function testStartsWith() { 45 | $this->assertFalse($this->tree->startsWith('hello')); 46 | $this->tree->add('hello'); 47 | $this->tree->add('bye'); 48 | $this->assertTrue($this->tree->startsWith('b')); 49 | $this->assertTrue($this->tree->startsWith('hel')); 50 | $this->assertFalse($this->tree->startsWith('hellooo')); 51 | } 52 | 53 | public function testWithPrefix() { 54 | $this->tree->add('hello'); 55 | $this->tree->add('hell'); 56 | $this->tree->add('bye'); 57 | $this->tree->add('beyond'); 58 | $withH = $this->tree->withPrefix('he'); 59 | $withB = $this->tree->withPrefix('b'); 60 | $withBy = $this->tree->withPrefix('by'); 61 | $all = $this->tree->withPrefix(''); 62 | 63 | $this->assertSame(['hell', 'hello'], $withH); 64 | $this->assertSame(['bye', 'beyond'], $withB); 65 | $this->assertSame(['bye'], $withBy); 66 | $this->assertSame(['hell', 'hello', 'bye', 'beyond'], $all); 67 | $this->assertEquals(4, $this->tree->wordCount()); 68 | } 69 | 70 | public function testDelete() { 71 | $this->tree->add('hellou'); 72 | $this->tree->add('hell'); 73 | $this->tree->add('yellow'); 74 | $this->tree->delete('hellou'); 75 | $this->assertEquals(2, $this->tree->wordCount()); 76 | $this->assertFalse($this->tree->contains('hellou')); 77 | $this->assertTrue($this->tree->contains('hell')); 78 | $this->assertTrue($this->tree->contains('yellow')); 79 | $this->tree->delete('yellow'); 80 | $this->assertEquals(4, $this->tree->size()); 81 | } 82 | 83 | public function testClear() { 84 | $this->tree->add('hellou'); 85 | $this->tree->add('hell'); 86 | $this->tree->add('yellow'); 87 | $this->assertEquals(3, $this->tree->wordCount()); 88 | $this->tree->clear(); 89 | $this->assertFalse($this->tree->contains('hellou')); 90 | $this->assertFalse($this->tree->contains('hell')); 91 | $this->assertFalse($this->tree->contains('yellow')); 92 | $this->assertEquals(0, $this->tree->size()); 93 | } 94 | 95 | public function testGetWords() { 96 | $this->assertSame([], $this->tree->getWords()); 97 | $this->tree->add('hello'); 98 | $this->tree->add('hell'); 99 | $this->tree->add('yellow'); 100 | $this->tree->add(''); 101 | $this->assertEquals(3, $this->tree->wordCount()); 102 | $this->assertSame(['hell', 'hello', 'yellow'], $this->tree->getWords()); 103 | $this->tree->delete('hello'); 104 | $this->assertSame(['hell', 'yellow'], $this->tree->getWords()); 105 | $this->tree->delete('hell'); 106 | $this->assertSame(['yellow'], $this->tree->getWords()); 107 | $this->tree->delete('yellow'); 108 | $this->assertSame([], $this->tree->getWords()); 109 | } 110 | } -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 |