Completed
Push — master ( ef3ef7...555aef )
by Siro Díaz
01:27
created

DoublyLinkedList::delete()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 8.2222
c 0
b 0
f 0
cc 7
eloc 11
nc 5
nop 1
1
<?php
2
/**
3
 * DataStructures for PHP
4
 *
5
 * @link      https://github.com/SiroDiaz/DataStructures
6
 * @copyright Copyright (c) 2017 Siro Díaz Palazón
7
 * @license   https://github.com/SiroDiaz/DataStructures/blob/master/README.md (MIT License)
8
 */
9
namespace DataStructures\Lists;
10
11
use DataStructures\Lists\Traits\{CountTrait, ArrayAccessTrait};
12
use DataStructures\Lists\Nodes\DoublyLinkedListNode;
13
use DataStructures\Lists\ListAbstract;
14
use OutOfBoundsException;
15
16
/**
17
 * DoublyLinkedList
18
 *
19
 * DoublyLinkedList is a double linked list that has
20
 * a pointer to the next and the previous node.
21
 *
22
 * @author Siro Diaz Palazon <[email protected]>
23
 */
24
class DoublyLinkedList extends ListAbstract {
25
    use ArrayAccessTrait;
26
27
    protected $head;
28
    private $tail;
29
    private $position;
30
    private $current;
31
32 View Code Duplication
    public function __construct() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
     * Gets the data stored in the position especified.
42
     *
43
     * @param integer $index Index that must be greater than 0
44
     *  and lower than the list size.
45
     * @return mixed The data stored in the given index
46
     * @throws OutOfBoundsException if index is out bounds.
47
     */
48 View Code Duplication
    public function get($index) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
49
        $node = $this->search($index);
50
        if($node === null) {
51
            return null;
52
        }
53
54
        return $node->data;
55
    }
56
57
    /**
58
     * Gets the node stored in the position especified.
59
     * If index is 0 or (size - 1) the method is O(1) else O(n).
60
     *
61
     * @param integer $index the position.
62
     * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1)
63
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null the node stored in $index.
64
     */
65
    protected function search($index) {
66
        if($index < 0 || $index > $this->size - 1) {
67
            throw new OutOfBoundsException();
68
        }
69
70
        if($index === 0) {
71
            return $this->head;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->head; of type null|DataStructures\List...es\DoublyLinkedListNode adds the type DataStructures\Lists\Nodes\DoublyLinkedListNode to the return on line 71 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
72
        }
73
74
        if($index === $this->size - 1) {
75
            return $this->tail;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->tail; of type null|DataStructures\List...es\DoublyLinkedListNode adds the type DataStructures\Lists\Nodes\DoublyLinkedListNode to the return on line 75 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
76
        }
77
78
        $current = &$this->head;
79
        if($index < (int) ceil($this->size / 2)) {
80
            $i = 0;
81
            while($i < $index) {
82
                $current = &$current->next;
83
                $i++;
84
            }    
85
        } else {
86
            $i = $this->size;
87
            while($i > $index) {
88
                $current = &$current->prev;
89
                $i--;
90
            }
91
        }
92
93
        return $current;
94
    }
95
96
    /**
97
     * Returns the data in the last node with O(1).
98
     *
99
     * @return mixed null if the list is empty.
100
     */
101
    public function getLast() {
102
        if($this->head === null) {
103
            return null;
104
        }
105
        return $this->tail->data;
106
    }
107
108
    /**
109
     * Returns the last node with O(1).
110
     *
111
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null if the list is empty.
112
     */
113
    public function searchLast() {
114
        if($this->head === null) {
115
            return null;
116
        }
117
        return $this->tail;
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123
    public function contains($data) : bool {
124
        if($this->empty()) {
125
            return false;
126
        }
127
128
        $current = $this->head->next;
129
        $prev = $this->head;
130
        while($current !== $this->head) {
131
            if($prev->data === $data) {
132
                return true;
133
            }
134
            $prev = $current;
135
            $current = $current->next;
136
        }
137
138
        return false;
139
    }
140
141
    /**
142
     * {@inheritDoc}
143
     */
144 View Code Duplication
    public function indexOf($data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
        if($this->head === null) {
146
            return false;
147
        }
148
        
149
        $current = $this->head;
150
        $i = 0;
151
        
152
        while($i < $this->size) {
153
            if($current->data === $data) {
154
                return $i;
155
            }
156
157
            $current = $current->next;
158
            $i++;
159
        }
160
161
        return false;
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     */
167 View Code Duplication
    public function lastIndexOf($data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
        if($this->head === null) {
169
            return false;
170
        }
171
        
172
        $current = $this->head;
173
        $i = 0;
174
        $pos = false;
175
        while($i < $this->size) {
176
            if($current->data == $data) {
177
                $pos = $i;
178
            }
179
180
            $current = $current->next;
181
            $i++;
182
        }
183
184
        return $pos;
185
    }
186
187
    /**
188
     * {@inheritDoc}
189
     */
190
    public function remove($data) {
191
        $current = &$this->head;
192
        $i = 0;
193
        
194
        if($this->head === null) {
195
            return null;
196
        }
197
198
        if($this->head->data === $data) {
199
            $this->head = &$this->head->next;
200
            $this->head->prev = &$this->tail;
201
            $this->size--;
202
            
203
            return $data;
204
        }
205
206 View Code Duplication
        while($i < $this->size) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
207
            if($current->data === $data) {
208
                $current->prev = &$current->next;
209
                $current = null;
210
                $this->size--;
211
                return $data;
212
            }
213
214
            $current = $current->next;
215
        }
216
217
        return null;
218
    }
219
    
220
    /**
221
     * Generator for retrieve all nodes stored.
222
     * 
223
     * @return null if the head is null (or list is empty)
224
     */
225 View Code Duplication
    public function getAll() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
226
        if($this->head === null) {
227
            return;
228
        }
229
230
        if($this->head === $this->tail) {
231
            yield $this->head->data;
232
        } else {
233
            $current = $this->head;
234
            $i = 0;
235
            while($i < $this->size) {
236
                yield $current->data;
237
                $current = $current->next;
238
                $i++;
239
            }
240
        }
241
    }
242
243
    /**
244
     * {@inheritDoc}
245
     */
246
    protected function insertBeginning($data) {
247
        $newNode = new DoublyLinkedListNode($data);
248
        if($this->head === null) {
249
            $newNode->next = &$this->head;
250
            $newNode->prev = &$this->head;
251
            $this->head = &$newNode;
252
            $this->tail = &$newNode;
253
        } else {
254
            $this->tail->next = &$newNode;
255
            $newNode->next = &$this->head;
256
            $newNode->prev = &$this->tail;
257
            $this->head = &$newNode;
258
        }
259
    }
260
261
    protected function insertEnd($data) {
262
        $newNode = new DoublyLinkedListNode($data);
263
        $this->tail->next = &$newNode;
264
        $newNode->next = &$this->head;
265
        $newNode->prev = &$this->tail;
266
        $this->tail = &$newNode;
267
        $this->head->prev = &$newNode;
268
    }
269
270
    /**
271
     * Add a new node in the specified index.
272
     *
273
     * @param integer $index the position.
274
     * @param mixed $data the data to be stored.
275
     */
276
    protected function insertAt($index, $data) {
277
        $newNode = new DoublyLinkedListNode($data);
278
        $current = $this->head;
279
        $prev = null;
280
        $i = 0;
281
        while($i < $index) {
282
            $prev = $current;
283
            $current = $current->next;
284
            $i++;
285
        }
286
        
287
        $prev->next = &$newNode;
288
        $newNode->prev = &$prev;
289
        $newNode->next = &$current;
290
        $current->prev = &$newNode;
291
    }
292
    
293
    /**
294
     * Delete a node in the given position and returns it back.
295
     *
296
     * @param integer $index the position.
297
     * @throws OutOfBoundsException if index is negative
298
     *  or is greater than the size of the list.
299
     */
300
    public function delete($index) {
301
        if($index < 0 || ($index > 0 && $index > $this->size - 1)) {
302
            throw new OutOfBoundsException();
303
        }
304
305
        // if the list is empty
306
        if($this->head === null) {
307
            return null;
308
        }
309
310
        if($index === 0) {
311
            return $this->deleteBeginning();
312
        } else if($index === $this->size - 1) {
313
            return $this->deleteEnd();
314
        } else {
315
            return $this->deleteAt($index);
316
        }
317
    }
318
319
    /**
320
     * Deletes at the beginnig of the list and returns the data stored.
321
     *
322
     * @return mixed the data stored in the node.
323
     */
324
    private function deleteBeginning() {
325
        // if only there is an element
326
        if($this->head->next === $this->head) {
327
            $temp = $this->head;
328
            $this->head = null;
329
        } else {
330
            $temp = $this->head;
331
            $this->head = &$this->head->next;
332
            $this->tail->next = &$this->head;
333
            
334
        }
335
        $this->size--;
336
        return $temp->data;
337
    }
338
339
    /**
340
     * Deletes at the specified position and returns the data stored.
341
     *
342
     * @param integer $index the position.
343
     * @return mixed the data stored in the node.
344
     */
345
    private function deleteAt($index) {
346
        $i = 0;
347
        $prev = $this->head;
348
        $current = $this->head;
349
        
350
        while($i < $index) {
351
            $prev = $current;
352
            $current = $current->next;
353
            $i++;
354
        }
355
356
        $temp = $current;
357
        $prev->next = &$current->next;
358
        $current->next->prev = &$pre;
0 ignored issues
show
Bug introduced by
The variable $pre does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
359
        $current = null;
0 ignored issues
show
Unused Code introduced by
$current is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
360
        $this->size--;
361
362
        return $temp->data;
363
    }
364
365
    /**
366
     * Deletes at the end of the list and returns the data stored.
367
     *
368
     * @return mixed the data stored in the node.
369
     */
370 View Code Duplication
    private function deleteEnd() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
        $prev = $this->head;
372
        $current = $this->head;
373
        
374
        while($current !== $this->tail) {
375
            $prev = $current;
376
            $current = $current->next;
377
        }
378
        
379
        $temp = $current;
380
        $prev->next = &$this->head;
381
        $this->head->prev = &$prev;
382
        $this->tail = &$prev;
383
        $current = null;
0 ignored issues
show
Unused Code introduced by
$current is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
384
385
        $this->size--;
386
387
        return $temp->data;
388
    }
389
    
390
    /**
391
     * Converts/exports the list content into array type.
392
     *
393
     * @return array data stored in all nodes.
394
     */
395
    public function toArray() : array {
396
        $arr = [];
397
        foreach($this->getAll() as $node) {
398
            $arr[] = $node;
399
        }
400
401
        return $arr;
402
    }
403
404
    /**
405
     * Reset the cursor position.
406
     */
407
    public function rewind() {
408
        $this->position = 0;
409
        $this->current = &$this->head;
410
    }
411
412
    /**
413
     * Returns the current node data.
414
     *
415
     * @return mixed
416
     */
417
    public function current() {
418
        return $this->current->data;
419
    }
420
421
    /**
422
     * Key or index that indicates the cursor position.
423
     *
424
     * @return integer The current position.
425
     */
426
    public function key() {
427
        return $this->position;
428
    }
429
430
    /**
431
     * Move the cursor to the next node and increments the
432
     * position counter.
433
     */
434
    public function next() {
435
        ++$this->position;
436
        $this->current = $this->current->next;
437
    }
438
439
    /**
440
     * Returns if the current pointer position is valid.
441
     *
442
     * @return boolean true if pointer is not last, else false.
443
     */
444
    public function valid() {
445
        return $this->position < $this->size;
446
    }
447
}