1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace DataStructures\Trees; |
4
|
|
|
|
5
|
|
|
use DataStructures\Trees\Interfaces\TreeInterface; |
6
|
|
|
use DataStructures\Trees\Interfaces\BinaryNodeInterface; |
7
|
|
|
|
8
|
|
|
abstract class BinaryTreeAbstract implements TreeInterface { |
9
|
|
|
protected $root; |
10
|
|
|
protected $size; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Checks if the tree is empty. |
14
|
|
|
* |
15
|
|
|
* @return boolean true if is empty, else false. |
16
|
|
|
*/ |
17
|
|
|
public function empty() { |
18
|
|
|
return $this->root === null; |
19
|
|
|
} |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Returns the tree size. |
23
|
|
|
* |
24
|
|
|
* @return int the length |
25
|
|
|
*/ |
26
|
|
|
public function size() { |
27
|
|
|
return $this->size; |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
public function put($key, $data){} |
31
|
|
|
public function putOrUpdate($key, $data){} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Retrieve the data stored in the tree. |
35
|
|
|
* |
36
|
|
|
* @param int|string $key the key to identify the data. |
37
|
|
|
* @return mixed |
38
|
|
|
*/ |
39
|
|
|
public function get($key){ |
40
|
|
|
if($this->root === null) { |
41
|
|
|
return null; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
$node = $this->root; |
45
|
|
|
while($node !== null) { |
46
|
|
|
if($key < $node->key) { |
47
|
|
|
$node = $node->left; |
48
|
|
|
} else if($key > $node->key) { |
49
|
|
|
$node = $node->right; |
50
|
|
|
} else { |
51
|
|
|
return $node->data; |
52
|
|
|
} |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
return null; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Returns the root node. |
60
|
|
|
* |
61
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the root node. |
62
|
|
|
*/ |
63
|
|
|
public function getRoot(){ |
64
|
|
|
return $this->root; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Looks for the node with the given key. |
69
|
|
|
* |
70
|
|
|
* @param int|string $key the key used to look for. |
71
|
|
|
* @return bool true if was found. |
72
|
|
|
*/ |
73
|
|
|
public function exists($key){ |
74
|
|
|
// $this->_exists($this->root, $key); for recursive search |
|
|
|
|
75
|
|
|
if($this->root === null) { |
76
|
|
|
return false; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
if($this->root->key === $key) { |
80
|
|
|
return true; |
81
|
|
|
} else { |
82
|
|
|
$node = $this->root; |
83
|
|
|
while($node !== null) { |
84
|
|
|
if($key < $node->key) { |
85
|
|
|
$node = $node->left; |
86
|
|
|
} else if($key > $node->key) { |
87
|
|
|
$node = $node->right; |
88
|
|
|
} else { |
89
|
|
|
return true; |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
return false; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Method that retrieves true if found a node with the specified key. |
99
|
|
|
* It's the recursive version of exists method and it's used internally |
100
|
|
|
* for traverse through a root node. |
101
|
|
|
* |
102
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null $node the root node. |
103
|
|
|
* @param int|string $key the key used to look for. |
104
|
|
|
* @return bool true if exists a node with that key. |
105
|
|
|
*/ |
106
|
|
|
private function _exists($node, $key) : bool { |
|
|
|
|
107
|
|
|
if($node === null) { |
108
|
|
|
return false; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
if($node->key === $key) { |
112
|
|
|
return true; |
113
|
|
|
} else if($key < $node->key) { |
114
|
|
|
return $this->_exists($node->left, $key); |
115
|
|
|
} else if($key > $node->key) { |
116
|
|
|
return $this->_exists($node->right, $key); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public function floor($key){} |
121
|
|
|
public function ceil($key){} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Gets the node with the minimum key. The most left and more bottom. |
125
|
|
|
* |
126
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the minum node or |
127
|
|
|
* null if the tree is empty. |
128
|
|
|
*/ |
129
|
|
|
public function min() { |
130
|
|
|
if($this->root === null) { |
131
|
|
|
return null; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
if($this->root->left === null) { |
135
|
|
|
return $this->root; |
|
|
|
|
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$current = $this->root; |
139
|
|
|
while($current->left !== null) { |
140
|
|
|
$current = $current->left; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
return $current; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Gets the node with the maximum key. The most right and more bottom. |
148
|
|
|
* |
149
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the maximum node or |
150
|
|
|
* null if the tree is empty. |
151
|
|
|
*/ |
152
|
|
|
public function max() { |
153
|
|
|
if($this->root === null) { |
154
|
|
|
return null; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
if($this->root->right === null) { |
158
|
|
|
return $this->root; |
|
|
|
|
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
$node = $this->root; |
162
|
|
|
while($node->right !== null) { |
163
|
|
|
$node = $node->right; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
return $node; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Returns the minimum node from a given node in position X. |
171
|
|
|
* |
172
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode $node the start point. |
173
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the minimum node. |
174
|
|
|
*/ |
175
|
|
View Code Duplication |
private function getMinNode(BinaryNodeInterface $node = null) { |
|
|
|
|
176
|
|
|
if($node === null) { |
177
|
|
|
return null; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
while($node->left !== null) { |
181
|
|
|
$node = $node->left; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
return $node; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Returns the maximum node from a given node in position X. |
189
|
|
|
* |
190
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode $node the start point. |
191
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the maximum node. |
192
|
|
|
*/ |
193
|
|
View Code Duplication |
private function getMaxNode(BinaryNodeInterface $node = null) { |
|
|
|
|
194
|
|
|
if($node === null) { |
195
|
|
|
return null; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
while($node->right !== null) { |
199
|
|
|
$node = $node->right; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
return $node; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Deletes the node with the minimum key and returns it. The most left and more bottom. |
207
|
|
|
* |
208
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null if null takes the root. |
209
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the minimum node or |
210
|
|
|
* null if the tree is empty. |
211
|
|
|
*/ |
212
|
|
View Code Duplication |
public function deleteMin(BinaryNodeInterface $node = null) { |
|
|
|
|
213
|
|
|
$node = $this->getMinNode($node ?? $this->root); |
214
|
|
|
if($node !== null) { |
215
|
|
|
$this->_delete($node); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
return $node; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Deletes the node with the maximum key and returns it. The most right and more bottom. |
223
|
|
|
* |
224
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null if null takes the root. |
225
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the maximum node or |
226
|
|
|
* null if the tree is empty. |
227
|
|
|
*/ |
228
|
|
View Code Duplication |
public function deleteMax(BinaryNodeInterface $node = null) { |
|
|
|
|
229
|
|
|
$node = $this->getMaxNode($node ?? $this->root); |
230
|
|
|
if($node !== null) { |
231
|
|
|
$this->_delete($node); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
return $node; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Deletes the selected node if is not null and returns the node |
239
|
|
|
* that replaces the deleted node. Also decrease the size of tree. |
240
|
|
|
* |
241
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null The node to be deleted. |
242
|
|
|
* @return the node that replaces the deleted. |
243
|
|
|
*/ |
244
|
|
|
protected abstract function _delete(BinaryNodeInterface &$node); |
|
|
|
|
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Replaces the node n to remove a new one k and links k with the parent |
248
|
|
|
* of n. |
249
|
|
|
* |
250
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode $nodeToReplace the node to remove. |
251
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null $newNode the newNode |
252
|
|
|
* to link with the $nodeToReplace parent. |
253
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode the new linked node. |
254
|
|
|
*/ |
255
|
|
|
protected function replace(&$nodeToReplace, &$newNode) { |
256
|
|
|
if($nodeToReplace->parent === null) { |
257
|
|
|
$this->root = &$newNode; |
258
|
|
|
} else if($nodeToReplace === $nodeToReplace->parent->left) { |
259
|
|
|
$nodeToReplace->parent->left = &$newNode; |
260
|
|
|
} else if($nodeToReplace === $nodeToReplace->parent->right) { |
261
|
|
|
$nodeToReplace->parent->right = &$newNode; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
if($newNode !== null) { |
265
|
|
|
$newNode->parent = &$nodeToReplace->parent; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
return $newNode; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
public function delete($key){} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Retrieves the node with the specified key. |
275
|
|
|
* |
276
|
|
|
* @param int|string $key the key used to store. |
277
|
|
|
* @return DataStructures\Trees\Nodes\BSTNode|null the node or null. |
278
|
|
|
*/ |
279
|
|
|
public function search($key) { |
280
|
|
|
if($this->root === null) { |
281
|
|
|
return null; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
if($this->root->key === $key) { |
285
|
|
|
return $this->root; |
|
|
|
|
286
|
|
|
} else { |
287
|
|
|
$node = $this->root; |
288
|
|
|
while($node !== null) { |
289
|
|
|
if($key < $node->key) { |
290
|
|
|
$node = $node->left; |
291
|
|
|
} else if($key > $node->key) { |
292
|
|
|
$node = $node->right; |
293
|
|
|
} else { |
294
|
|
|
return $node; |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
return null; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Returns true if is leaf the node. |
304
|
|
|
* |
305
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null $node default to null. |
306
|
|
|
* @return true if is leaf the node, is not null and their subtrees has no |
307
|
|
|
* pointers to successors. |
308
|
|
|
*/ |
309
|
|
|
public function isLeaf($node) { // BinaryTreeNode |
310
|
|
|
return ($node !== null && $node->left === null && $node->right === null); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Checks if a node is root. New nodes that does not point to any other node |
315
|
|
|
* also are called a root node. |
316
|
|
|
* |
317
|
|
|
* @param DataStructures\Trees\Nodes\BSTNode|null $node default to null. |
318
|
|
|
* @return true if is root the node, is not null and their subtrees has no |
319
|
|
|
* pointers to successors. |
320
|
|
|
*/ |
321
|
|
|
public function isRoot($node) { |
322
|
|
|
return $node !== null && $node->parent === null; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Binds to count() method. This is equal to make $this->tree->size(). |
327
|
|
|
* |
328
|
|
|
* @return integer the tree size. 0 if it is empty. |
329
|
|
|
*/ |
330
|
|
|
public function count() { |
331
|
|
|
return $this->size; |
332
|
|
|
} |
333
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.