-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from KaririCode-Framework/develop
Add ArrayDeque and ArrayQueue Implementations with Comprehensive Unit…
- Loading branch information
Showing
4 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\DataStructure\Queue; | ||
|
||
use KaririCode\Contract\DataStructure\Queue; | ||
|
||
/** | ||
* ArrayDeque implementation. | ||
* | ||
* This class implements a double-ended queue using a circular array. | ||
* It provides amortized O(1) time complexity for add and remove operations at both ends. | ||
* | ||
* @category Queues | ||
* | ||
* @implements Queue<mixed> | ||
*/ | ||
class ArrayDeque implements Queue | ||
{ | ||
private array $elements; | ||
private int $front = 0; | ||
private int $size = 0; | ||
private int $capacity; | ||
|
||
public function __construct(int $initialCapacity = 16) | ||
{ | ||
$this->capacity = $initialCapacity; | ||
$this->elements = array_fill(0, $this->capacity, null); | ||
} | ||
|
||
public function enqueue(mixed $element): void | ||
{ | ||
$this->ensureCapacity(); | ||
$index = ($this->front + $this->size) % $this->capacity; | ||
$this->elements[$index] = $element; | ||
++$this->size; | ||
} | ||
|
||
public function dequeue(): mixed | ||
{ | ||
if ($this->isEmpty()) { | ||
return null; | ||
} | ||
$element = $this->elements[$this->front]; | ||
$this->elements[$this->front] = null; | ||
$this->front = ($this->front + 1) % $this->capacity; | ||
--$this->size; | ||
|
||
return $element; | ||
} | ||
|
||
public function peek(): mixed | ||
{ | ||
return $this->isEmpty() ? null : $this->elements[$this->front]; | ||
} | ||
|
||
public function addFirst(mixed $element): void | ||
{ | ||
$this->ensureCapacity(); | ||
$this->front = ($this->front - 1 + $this->capacity) % $this->capacity; | ||
$this->elements[$this->front] = $element; | ||
++$this->size; | ||
} | ||
|
||
public function removeLast(): mixed | ||
{ | ||
if ($this->isEmpty()) { | ||
return null; | ||
} | ||
$index = ($this->front + $this->size - 1) % $this->capacity; | ||
$element = $this->elements[$index]; | ||
$this->elements[$index] = null; | ||
--$this->size; | ||
|
||
return $element; | ||
} | ||
|
||
public function peekLast(): mixed | ||
{ | ||
if ($this->isEmpty()) { | ||
return null; | ||
} | ||
$index = ($this->front + $this->size - 1) % $this->capacity; | ||
|
||
return $this->elements[$index]; | ||
} | ||
|
||
public function isEmpty(): bool | ||
{ | ||
return 0 === $this->size; | ||
} | ||
|
||
public function size(): int | ||
{ | ||
return $this->size; | ||
} | ||
|
||
/** | ||
* Ensures that the deque has enough capacity to add a new element. | ||
*/ | ||
private function ensureCapacity(): void | ||
{ | ||
if ($this->size === $this->capacity) { | ||
$newCapacity = $this->capacity * 2; | ||
$newElements = array_fill(0, $newCapacity, null); | ||
for ($i = 0; $i < $this->size; ++$i) { | ||
$newElements[$i] = $this->elements[($this->front + $i) % $this->capacity]; | ||
} | ||
$this->elements = $newElements; | ||
$this->front = 0; | ||
$this->capacity = $newCapacity; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\DataStructure\Queue; | ||
|
||
use KaririCode\Contract\DataStructure\Queue; | ||
|
||
/** | ||
* ArrayQueue implementation. | ||
* | ||
* This class implements a simple queue using a circular array. | ||
* It provides amortized O(1) time complexity for enqueue and dequeue operations. | ||
* | ||
* @category Queues | ||
* | ||
* @author Walmir Silva <walmir.silva@kariricode.org> | ||
* @license MIT | ||
* | ||
* @see https://kariricode.org/ | ||
*/ | ||
class ArrayQueue implements Queue | ||
{ | ||
private array $elements; | ||
private int $front = 0; | ||
private int $size = 0; | ||
private int $capacity; | ||
|
||
public function __construct(int $initialCapacity = 16) | ||
{ | ||
$this->capacity = $initialCapacity; | ||
$this->elements = array_fill(0, $this->capacity, null); | ||
} | ||
|
||
public function enqueue(mixed $element): void | ||
{ | ||
$this->ensureCapacity(); | ||
$index = ($this->front + $this->size) % $this->capacity; | ||
$this->elements[$index] = $element; | ||
++$this->size; | ||
} | ||
|
||
public function dequeue(): mixed | ||
{ | ||
if ($this->isEmpty()) { | ||
return null; | ||
} | ||
$element = $this->elements[$this->front]; | ||
$this->elements[$this->front] = null; | ||
$this->front = ($this->front + 1) % $this->capacity; | ||
--$this->size; | ||
|
||
return $element; | ||
} | ||
|
||
public function peek(): mixed | ||
{ | ||
return $this->isEmpty() ? null : $this->elements[$this->front]; | ||
} | ||
|
||
public function isEmpty(): bool | ||
{ | ||
return 0 === $this->size; | ||
} | ||
|
||
public function size(): int | ||
{ | ||
return $this->size; | ||
} | ||
|
||
/** | ||
* Ensures that the queue has enough capacity to add a new element. | ||
*/ | ||
private function ensureCapacity(): void | ||
{ | ||
if ($this->size === $this->capacity) { | ||
$newCapacity = $this->capacity * 2; | ||
$newElements = array_fill(0, $newCapacity, null); | ||
for ($i = 0; $i < $this->size; ++$i) { | ||
$newElements[$i] = $this->elements[($this->front + $i) % $this->capacity]; | ||
} | ||
$this->elements = $newElements; | ||
$this->front = 0; | ||
$this->capacity = $newCapacity; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace KaririCode\DataStructure\Tests\Queue; | ||
|
||
use KaririCode\DataStructure\Queue\ArrayDeque; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class ArrayDequeTest extends TestCase | ||
{ | ||
// Test enqueuing elements | ||
public function testEnqueueAddsElementToEndOfDeque(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$this->assertSame(1, $deque->peek()); | ||
} | ||
|
||
// Test dequeuing elements | ||
public function testDequeueRemovesElementFromFrontOfDeque(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$this->assertSame(1, $deque->dequeue()); | ||
$this->assertSame(2, $deque->peek()); | ||
} | ||
|
||
// Test dequeuing from an empty deque | ||
public function testDequeueFromEmptyDequeReturnsNull(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$this->assertNull($deque->dequeue()); | ||
} | ||
|
||
// Test peeking elements | ||
public function testPeekReturnsElementFromFrontWithoutRemovingIt(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$this->assertSame(1, $deque->peek()); | ||
$this->assertSame(1, $deque->peek()); | ||
} | ||
|
||
// Test peeking from an empty deque | ||
public function testPeekFromEmptyDequeReturnsNull(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$this->assertNull($deque->peek()); | ||
} | ||
|
||
// Test adding elements to the front | ||
public function testAddFirstAddsElementToFrontOfDeque(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$deque->addFirst(2); | ||
$this->assertSame(2, $deque->peek()); | ||
} | ||
|
||
// Test removing last elements | ||
public function testRemoveLastRemovesElementFromEndOfDeque(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$this->assertSame(2, $deque->removeLast()); | ||
$this->assertSame(1, $deque->peekLast()); | ||
} | ||
|
||
// Test removing last element from an empty deque | ||
public function testRemoveLastFromEmptyDequeReturnsNull(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$this->assertNull($deque->removeLast()); | ||
} | ||
|
||
// Test peeking last elements | ||
public function testPeekLastReturnsElementFromEndWithoutRemovingIt(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$this->assertSame(2, $deque->peekLast()); | ||
$this->assertSame(2, $deque->peekLast()); | ||
} | ||
|
||
// Test peeking last from an empty deque | ||
public function testPeekLastFromEmptyDequeReturnsNull(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$this->assertNull($deque->peekLast()); | ||
} | ||
|
||
// Test checking if deque is empty | ||
public function testIsEmptyReturnsTrueIfDequeIsEmpty(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$this->assertTrue($deque->isEmpty()); | ||
$deque->enqueue(1); | ||
$this->assertFalse($deque->isEmpty()); | ||
} | ||
|
||
// Test getting the size of the deque | ||
public function testSizeReturnsNumberOfElementsInDeque(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$this->assertSame(0, $deque->size()); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$this->assertSame(2, $deque->size()); | ||
} | ||
|
||
// Test ensuring capacity of deque | ||
public function testEnsureCapacityDoublesCapacityWhenFull(): void | ||
{ | ||
$deque = new ArrayDeque(2); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$deque->enqueue(3); // Should trigger capacity increase | ||
$this->assertSame(3, $deque->size()); | ||
} | ||
|
||
// Test handling null values in the deque | ||
public function testHandlingNullValuesCorrectly(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(null); | ||
$this->assertSame(null, $deque->dequeue()); | ||
} | ||
|
||
// Test circular nature of the deque | ||
public function testCircularBehavior(): void | ||
{ | ||
$deque = new ArrayDeque(3); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$deque->enqueue(3); | ||
$deque->dequeue(); | ||
$deque->enqueue(4); | ||
$this->assertSame(2, $deque->dequeue()); | ||
$this->assertSame(3, $deque->dequeue()); | ||
$this->assertSame(4, $deque->dequeue()); | ||
} | ||
|
||
// Test deque with various data types | ||
public function testDequeWithVariousDataTypes(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(123); | ||
$deque->enqueue('string'); | ||
$deque->enqueue([1, 2, 3]); | ||
$deque->enqueue(new \stdClass()); | ||
|
||
$this->assertSame(123, $deque->dequeue()); | ||
$this->assertSame('string', $deque->dequeue()); | ||
$this->assertSame([1, 2, 3], $deque->dequeue()); | ||
$this->assertInstanceOf(\stdClass::class, $deque->dequeue()); | ||
} | ||
|
||
// Test deque behavior after mixed operations | ||
public function testDequeBehaviorAfterMixedOperations(): void | ||
{ | ||
$deque = new ArrayDeque(); | ||
$deque->enqueue(1); | ||
$deque->enqueue(2); | ||
$deque->addFirst(0); | ||
$deque->removeLast(); | ||
$deque->enqueue(3); | ||
$this->assertSame(0, $deque->dequeue()); | ||
$this->assertSame(1, $deque->dequeue()); | ||
$this->assertSame(3, $deque->peekLast()); | ||
} | ||
} |
Oops, something went wrong.