├── renovate.json
├── .phpunit-watcher.yml
├── exercises
├── Numbers
│ ├── Numbers.php
│ └── Complete
│ │ └── NumbersComplete.php
├── LinkedList
│ ├── Node.php
│ ├── Complete
│ │ ├── NodeComplete.php
│ │ └── LinkedListComplete.php
│ └── LinkedList.php
├── Vowels
│ ├── Vowels.php
│ └── Complete
│ │ └── VowelsComplete.php
├── MaxChar
│ ├── MaxChar.php
│ └── Complete
│ │ └── MaxCharComplete.php
├── Ladder
│ ├── Ladder.php
│ └── Complete
│ │ └── LadderComplete.php
├── Spiral
│ ├── Spiral.php
│ └── Complete
│ │ └── SpiralComplete.php
├── Tree
│ ├── Node.php
│ ├── Tree.php
│ └── Complete
│ │ ├── NodeComplete.php
│ │ └── TreeComplete.php
├── Pyramid
│ ├── Pyramid.php
│ └── Complete
│ │ └── PyramidComplete.php
├── MaxHeap
│ ├── MaxHeap.php
│ └── Complete
│ │ └── MaxHeapComplete.php
├── Events
│ ├── Events.php
│ └── Complete
│ │ └── EventsComplete.php
├── Capitalize
│ ├── Capitalize.php
│ └── Complete
│ │ └── CapitalizeComplete.php
├── Fibonacci
│ ├── Fibonacci.php
│ └── Complete
│ │ └── FibonacciComplete.php
├── MaxSubArraySum
│ ├── MaxSubArraySum.php
│ └── Complete
│ │ └── MaxSubArraySumComplete.php
├── Palindrome
│ ├── Palindrome.php
│ └── Complete
│ │ └── PalindromeComplete.php
├── ChainResponsabilityPattern
│ ├── Complete
│ │ ├── UpperCaseFilter.php
│ │ ├── MultipleFilter.php
│ │ └── MainFilterAbstract.php
│ └── exercice.php
├── FizzBuzz
│ ├── FizzBuzz.php
│ └── Complete
│ │ └── FizzBuzzComplete.php
├── Search
│ ├── Search.php
│ └── Complete
│ │ └── SearchComplete.php
├── Stack
│ ├── Stack.php
│ └── Complete
│ │ └── StackComplete.php
├── QueueFromStacks
│ ├── QueueFromStacks.php
│ └── Complete
│ │ └── QueueFromStacksComplete.php
├── Sort
│ ├── Sort.php
│ └── Complete
│ │ └── SortComplete.php
├── ArrayGroup
│ ├── ArrayGroup.php
│ └── Complete
│ │ └── ArrayGroupComplete.php
├── Anagram
│ ├── Anagram.php
│ └── Complete
│ │ └── AnagramComplete.php
├── Reverse
│ ├── Reverse.php
│ └── Complete
│ │ └── ReverseComplete.php
├── Queue
│ ├── Queue.php
│ └── Complete
│ │ └── QueueComplete.php
└── BinarySearchTree
│ ├── BinarySearchTree.php
│ └── Complete
│ └── BinarySearchTreeComplete.php
├── .travis.yml
├── tests
├── FizzBuzz
│ ├── Complete
│ │ └── FizzBuzzCompleteTest.php
│ └── FizzBuzzTest.php
├── Numbers
│ ├── Complete
│ │ └── NumbersCompleteTest.php
│ └── NumbersTest.php
├── GetReflectionMethod.php
├── LinkedList
│ ├── NodeTest.php
│ ├── Complete
│ │ └── NodeCompleteTest.php
│ └── LinkedListTest.php
├── Anagram
│ ├── Complete
│ │ └── AnagramCompleteTest.php
│ └── AnagramTest.php
├── MaxSubArraySum
│ ├── MaxSubArraySumTest.php
│ └── Complete
│ │ └── MaxSubArrayCompleteTest.php
├── Capitalize
│ ├── CapitalizeTest.php
│ └── Complete
│ │ └── CapitalizeCompleteTest.php
├── MaxChar
│ ├── MaxCharTest.php
│ └── Complete
│ │ └── MaxCharCompleteTest.php
├── Spiral
│ ├── Complete
│ │ └── SpiralCompleteTest.php
│ └── SpiralTest.php
├── Ladder
│ ├── LadderTest.php
│ └── Complete
│ │ └── LadderCompleteTest.php
├── Pyramid
│ ├── PyramidTest.php
│ └── Complete
│ │ └── PyramidCompleteTest.php
├── ArrayGroup
│ ├── ArrayGroupTest.php
│ └── Complete
│ │ └── ArrayGroupCompleteTest.php
├── Vowels
│ ├── VowelsTest.php
│ └── Complete
│ │ └── VowelsCompleteTest.php
├── Fibonacci
│ ├── FibonacciTest.php
│ └── Complete
│ │ └── FibonacciCompleteTest.php
├── Search
│ ├── SearchTest.php
│ └── Complete
│ │ └── SearchCompleteTest.php
├── ChainResponsabilityPattern
│ └── Complete
│ │ ├── UpperCaseFilterTest.php
│ │ ├── MultipleFilterTest.php
│ │ └── ChainResponsabilityPatternTest.php
├── Palindrome
│ ├── PalindromeTest.php
│ └── Complete
│ │ └── PalindromeCompleteTest.php
├── Reverse
│ ├── ReverseTest.php
│ └── Complete
│ │ └── ReverseCompleteTest.php
├── Stack
│ ├── Complete
│ │ └── StackCompleteTest.php
│ └── StackTest.php
├── MaxHeap
│ ├── Complete
│ │ └── MaxHeapCompleteTest.php
│ └── MaxHeapTest.php
├── QueueFromStacks
│ ├── QueueFromStacksTest.php
│ └── Complete
│ │ └── QueueFromStacksCompleteTest.php
├── Tree
│ ├── Complete
│ │ ├── NodeCompleteTest.php
│ │ └── TreeCompleteTest.php
│ ├── TreeTest.php
│ └── NodeTest.php
├── Queue
│ ├── Complete
│ │ └── QueueCompleteTest.php
│ └── QueueTest.php
├── Sort
│ ├── SortTest.php
│ └── Complete
│ │ └── SortCompleteTest.php
├── BinarySearchTree
│ ├── BinarySearchTreeTest.php
│ └── Complete
│ │ └── BinarySearchTreeCompleteTest.php
└── Events
│ ├── Complete
│ └── EventsCompleteTest.php
│ └── EventsTest.php
├── phpunit.xml
├── composer.json
├── LICENSE
├── .all-contributorsrc
├── .gitignore
└── README.md
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["config:base"],
3 | "automerge": true
4 | }
5 |
--------------------------------------------------------------------------------
/.phpunit-watcher.yml:
--------------------------------------------------------------------------------
1 | watch:
2 | directories:
3 | - exercises
4 | - tests
5 | fileMask: '*.php'
6 |
--------------------------------------------------------------------------------
/exercises/Numbers/Numbers.php:
--------------------------------------------------------------------------------
1 | '# '
12 | * '## '
13 | * '###'
14 | */
15 | final class Ladder
16 | {
17 | }
18 |
--------------------------------------------------------------------------------
/exercises/Spiral/Spiral.php:
--------------------------------------------------------------------------------
1 | ' # '
12 | * ' ### '
13 | * '#####'
14 | */
15 | final class Pyramid
16 | {
17 | }
18 |
--------------------------------------------------------------------------------
/exercises/MaxHeap/MaxHeap.php:
--------------------------------------------------------------------------------
1 | 1, 2, 'fizz', 4, 'buzz'
15 | */
16 | final class FizzBuzz
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/exercises/LinkedList/Complete/NodeComplete.php:
--------------------------------------------------------------------------------
1 | data = $data;
18 | $this->next = $next;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/FizzBuzz/Complete/FizzBuzzCompleteTest.php:
--------------------------------------------------------------------------------
1 | expectOutputString('12fizz4buzzfizz78fizzbuzz11fizz1314fizzbuzz');
15 | FizzBuzzComplete::print(15);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/exercises/Search/Search.php:
--------------------------------------------------------------------------------
1 | push(1);
17 | * $queue->push(2);
18 | * $queue->peek() === 1;
19 | * $queue->pop() === 1;
20 | */
21 | final class Stack
22 | {
23 | }
24 |
--------------------------------------------------------------------------------
/exercises/Numbers/Complete/NumbersComplete.php:
--------------------------------------------------------------------------------
1 | getMethod($name);
18 | $method->setAccessible(true);
19 |
20 | return $method;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/exercises/Sort/Sort.php:
--------------------------------------------------------------------------------
1 | [[ 1, 2], [3, 4], [5]]
13 | * @example ArrayGroup::group([1, 2, 3, 4, 5], 3) -> [[ 1, 2, 3], [4, 5]]
14 | * @example ArrayGroup::group([1, 2, 3, 4, 5], 6) -> [[ 1, 2, 3, 4, 5]]
15 | */
16 | final class ArrayGroup
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/exercises/FizzBuzz/Complete/FizzBuzzComplete.php:
--------------------------------------------------------------------------------
1 | data);
18 | self::assertInstanceOf(Node::class, $tail->next);
19 | self::assertSame(2, $tail->next->data);
20 | self::assertNull($tail->next->next);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 | ./tests
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/LinkedList/Complete/NodeCompleteTest.php:
--------------------------------------------------------------------------------
1 | data);
17 | self::assertInstanceOf(NodeComplete::class, $tail->next);
18 | self::assertSame(2, $tail->next->data);
19 | self::assertNull($tail->next->next);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/exercises/Reverse/Reverse.php:
--------------------------------------------------------------------------------
1 | expectOutputString('12fizz4buzzfizz78fizzbuzz11fizz1314fizzbuzz');
26 | FizzBuzz::print(15);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/exercises/Stack/Complete/StackComplete.php:
--------------------------------------------------------------------------------
1 | queue[] = $value;
20 | }
21 |
22 | /** @return mixed|null */
23 | public function pop()
24 | {
25 | return array_pop($this->queue);
26 | }
27 |
28 | /** @return mixed|null */
29 | public function peek()
30 | {
31 | return count($this->queue)
32 | ? $this->queue[array_key_last($this->queue)]
33 | : null;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/exercises/Anagram/Complete/AnagramComplete.php:
--------------------------------------------------------------------------------
1 | add(1);
19 | * $queue1->add(2);
20 | * $queue1->add(3);
21 | * $queue1->peek() === 3;
22 | * $queue1->remove() === 3;
23 | *
24 | * $queue2 = new Queue();
25 | *
26 | * $queue1->add('a');
27 | * $queue1->add('b');
28 | * $queue1->add('c');
29 | *
30 | * Queue::zip(queue1, queue2) -> [1, 'a', 2, 'b', 'c']
31 | */
32 | final class Queue
33 | {
34 | }
35 |
--------------------------------------------------------------------------------
/exercises/Tree/Tree.php:
--------------------------------------------------------------------------------
1 | data = $data;
21 | }
22 |
23 | /** @param mixed $data */
24 | public function add($data): void
25 | {
26 | $this->children[] = new self($data);
27 | }
28 |
29 | /** @param mixed $data */
30 | public function remove($data): void
31 | {
32 | $this->children = array_values(array_filter(
33 | $this->children,
34 | static function ($child) use ($data) {
35 | return $child->data !== $data;
36 | }
37 | ));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Capitalize/CapitalizeTest.php:
--------------------------------------------------------------------------------
1 | */
12 | private $events = [];
13 |
14 | public function on(string $event, callable $callback): void
15 | {
16 | if (array_key_exists($event, $this->events)) {
17 | $this->events[$event][] = $callback;
18 | } else {
19 | $this->events[$event] = [$callback];
20 | }
21 | }
22 |
23 | public function fire(string $event): void
24 | {
25 | if (array_key_exists($event, $this->events)) {
26 | /** @var callable $callback */
27 | foreach ($this->events[$event] as $callback) {
28 | $callback();
29 | }
30 | }
31 | }
32 |
33 | public function off(string $event): void
34 | {
35 | unset($this->events[$event]);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/MaxChar/MaxCharTest.php:
--------------------------------------------------------------------------------
1 | multiple;
16 | }
17 |
18 | /**
19 | * Set the value of multiple
20 | *
21 | * @return self
22 | */
23 | public function setMultiple(int $multiple)
24 | {
25 | $this->multiple = $multiple;
26 |
27 | return $this;
28 | }
29 |
30 | /**
31 | * filter integer
32 | *
33 | * @param [type] $content
34 | * @return void
35 | */
36 | public function doFilter($content = null)
37 | {
38 | $contentFilter = filter_var($content, FILTER_VALIDATE_INT);
39 |
40 | if($contentFilter !== false)
41 | {
42 | return $content * $this->multiple;
43 | }
44 | return $content;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/Anagram/AnagramTest.php:
--------------------------------------------------------------------------------
1 | expectOutputString('#');
26 | Ladder::print(1);
27 | }
28 |
29 | public function testPrintLadder1(): void
30 | {
31 | self::markTestSkipped();
32 | $this->expectOutputString('# ##');
33 | Ladder::print(2);
34 | }
35 |
36 | public function testPrintLadder2(): void
37 | {
38 | self::markTestSkipped();
39 | $this->expectOutputString('# ## ### #### #####');
40 | Ladder::print(5);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/exercises/ChainResponsabilityPattern/exercice.php:
--------------------------------------------------------------------------------
1 | setMultiple(3);
15 | * $uppercaseFilter->setSuccessor($uppercaseFilter);
16 | *
17 | * echo $mainFilter.filter('kevin schmitt'); KEVIN SCHMITT
18 | * echo $mainFilter.filter('360'); 24
19 | * echo $mainFilter.filter(12); 24
20 | */
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Anton Ždanov
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.
--------------------------------------------------------------------------------
/tests/Pyramid/PyramidTest.php:
--------------------------------------------------------------------------------
1 | expectOutputString('#');
26 | Pyramid::print(1);
27 | }
28 |
29 | public function testPrintLadder1(): void
30 | {
31 | self::markTestSkipped();
32 | $this->expectOutputString(' # ###');
33 | Pyramid::print(2);
34 | }
35 |
36 | public function testPrintLadder2(): void
37 | {
38 | self::markTestSkipped();
39 | $this->expectOutputString(' # ### ##### ####### #########');
40 | Pyramid::print(5);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/ArrayGroup/ArrayGroupTest.php:
--------------------------------------------------------------------------------
1 | _successor;
20 | }
21 |
22 | /**
23 | * setter $_successor
24 | *
25 | * @param MainFilterAbstract $succesor
26 | * @return void
27 | */
28 | public function setSuccessor(MainFilterAbstract $succesor = null)
29 | {
30 | $this->_successor = $succesor;
31 | return $this;
32 | }
33 |
34 | /**
35 | * filter $content
36 | *
37 | * @param [string|int] $content
38 | * @return string|int
39 | */
40 | public function filter($content)
41 | {
42 | $result = $this->doFilter($content);
43 |
44 | if ($this->_successor) {
45 | $result = $this->_successor->filter($result);
46 | }
47 |
48 | return $result;
49 | }
50 |
51 | abstract public function doFilter();
52 | }
--------------------------------------------------------------------------------
/tests/Vowels/VowelsTest.php:
--------------------------------------------------------------------------------
1 | $freq) {
27 | if ($freq <= $freqMax) {
28 | continue;
29 | }
30 |
31 | $max = $char;
32 | $freqMax = $freq;
33 | }
34 |
35 | return (string) $max;
36 | }
37 |
38 | public static function get1(string $string): string
39 | {
40 | $array = [];
41 | for ($i = 0; $i < strlen($string); ++$i) {
42 | if (isset($array[$string[$i]])) {
43 | ++$array[$string[$i]];
44 | } else {
45 | $array[$string[$i]] = 1;
46 | }
47 | }
48 |
49 | arsort($array);
50 | $max = array_keys($array)[0];
51 |
52 | return (string) $max;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tests/Pyramid/Complete/PyramidCompleteTest.php:
--------------------------------------------------------------------------------
1 | expectOutputString('#');
15 | PyramidComplete::print1(1);
16 | }
17 |
18 | public function testPrintLadder1(): void
19 | {
20 | $this->expectOutputString(' # ###');
21 | PyramidComplete::print1(2);
22 | }
23 |
24 | public function testPrintLadder2(): void
25 | {
26 | $this->expectOutputString(' # ### ##### ####### #########');
27 | PyramidComplete::print1(5);
28 | }
29 |
30 | public function testPrintLadderRecursive(): void
31 | {
32 | $this->expectOutputString('#');
33 | PyramidComplete::print2(1);
34 | }
35 |
36 | public function testPrintLadderRecursive1(): void
37 | {
38 | $this->expectOutputString(' # ###');
39 | PyramidComplete::print2(2);
40 | }
41 |
42 | public function testPrintLadderRecursive2(): void
43 | {
44 | $this->expectOutputString(' # ### ##### ####### #########');
45 | PyramidComplete::print2(5);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/exercises/Ladder/Complete/LadderComplete.php:
--------------------------------------------------------------------------------
1 | queue, $value);
21 | }
22 |
23 | /** @return mixed|null */
24 | public function remove()
25 | {
26 | return array_pop($this->queue);
27 | }
28 |
29 | /** @return mixed|null */
30 | public function peek()
31 | {
32 | return count($this->queue)
33 | ? $this->queue[array_key_last($this->queue)]
34 | : null;
35 | }
36 |
37 | public static function zip(self $queue1, self $queue2): self
38 | {
39 | $new = new self();
40 |
41 | while ($queue1->peek() || $queue2->peek()) {
42 | $value1 = $queue1->remove();
43 | if ($value1) {
44 | $new->add($value1);
45 | }
46 |
47 | $value2 = $queue2->remove();
48 | if ($value2) {
49 | $new->add($value2);
50 | }
51 | }
52 |
53 | return $new;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/Spiral/SpiralTest.php:
--------------------------------------------------------------------------------
1 | */
12 | private static $cache = [];
13 |
14 | public static function get1(int $index): int
15 | {
16 | $result = [];
17 |
18 | for ($i = 0; $i <= $index; ++$i) {
19 | if ($i < 2) {
20 | $result[] = $i;
21 | } else {
22 | $result[] = $result[$i - 1] + $result[$i - 2];
23 | }
24 | }
25 |
26 | return $result[$index];
27 | }
28 |
29 | public static function get2(int $index): int
30 | {
31 | if ($index < 2) {
32 | return $index;
33 | }
34 |
35 | return self::get2($index - 1) + self::get2($index - 2);
36 | }
37 |
38 | public static function get3(int $index): int
39 | {
40 | if (array_key_exists($index, self::$cache)) {
41 | return self::$cache[$index];
42 | }
43 |
44 | if ($index < 2) {
45 | self::$cache[$index] = $index;
46 |
47 | return $index;
48 | }
49 |
50 | $result = self::get3($index - 1) + self::get3($index - 2);
51 | self::$cache[$index] = $result;
52 |
53 | return $result;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/MaxChar/Complete/MaxCharCompleteTest.php:
--------------------------------------------------------------------------------
1 | > */
10 | public static function make(int $length): array
11 | {
12 | $results = [];
13 |
14 | $columnStart = 0;
15 | $columnEnd = $length - 1;
16 | $rowStart = 0;
17 | $rowEnd = $length - 1;
18 | $counter = 1;
19 |
20 | while ($columnStart <= $columnEnd && $rowStart <= $rowEnd) {
21 | for ($column = $columnStart; $column <= $columnEnd; ++$column) {
22 | $results[$rowStart][$column] = $counter;
23 | ++$counter;
24 | }
25 | ++$rowStart;
26 |
27 | for ($row = $rowStart; $row <= $rowEnd; ++$row) {
28 | $results[$row][$columnEnd] = $counter;
29 | ++$counter;
30 | }
31 | --$columnEnd;
32 |
33 | for ($column = $columnEnd; $column >= $columnStart; --$column) {
34 | $results[$rowEnd][$column] = $counter;
35 | ++$counter;
36 | }
37 | --$rowEnd;
38 |
39 | for ($row = $rowEnd; $row >= $rowStart; --$row) {
40 | $results[$row][$columnStart] = $counter;
41 | ++$counter;
42 | }
43 | ++$columnStart;
44 | }
45 |
46 | return $results;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/MaxSubArraySum/Complete/MaxSubArrayCompleteTest.php:
--------------------------------------------------------------------------------
1 | $length) {
18 | return 0;
19 | }
20 |
21 | $max = -INF;
22 |
23 | for ($i = 0; $i < $length - $n + 1; ++$i) {
24 | $temp = 0;
25 | for ($j = 0; $j < $n; ++$j) {
26 | $temp += $input[$i + $j];
27 | }
28 | if ($temp > $max) {
29 | $max = $temp;
30 | }
31 | }
32 |
33 | return $max;
34 | }
35 |
36 | /** @param int[] $input */
37 | public static function max2(array $input, int $n): int
38 | {
39 | $length = count($input);
40 | if ($length < $n) {
41 | return 0;
42 | }
43 | $maxSum = 0;
44 | $tempSum = 0;
45 |
46 | for ($i = 0; $i < $n; ++$i) {
47 | $maxSum += $input[$i];
48 | }
49 |
50 | $tempSum = $maxSum;
51 |
52 | for ($i = $n; $i < $length; ++$i) {
53 | $tempSum = ($tempSum - $input[$i - $n] + $input[$i]);
54 | $maxSum = max($maxSum, $tempSum);
55 | }
56 |
57 | return $maxSum;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/exercises/QueueFromStacks/Complete/QueueFromStacksComplete.php:
--------------------------------------------------------------------------------
1 | storage = new StackComplete();
19 | $this->temporary = new StackComplete();
20 | }
21 |
22 | /** @param mixed $value */
23 | public function add($value): void
24 | {
25 | $this->storage->push($value);
26 | }
27 |
28 | /** @return mixed|null */
29 | public function remove()
30 | {
31 | while ($this->storage->peek()) {
32 | $this->temporary->push($this->storage->pop());
33 | }
34 |
35 | $item = $this->temporary->pop();
36 |
37 | while ($this->temporary->peek()) {
38 | $this->storage->push($this->temporary->pop());
39 | }
40 |
41 | return $item;
42 | }
43 |
44 | /** @return mixed|null */
45 | public function peek()
46 | {
47 | while ($this->storage->peek()) {
48 | $this->temporary->push($this->storage->pop());
49 | }
50 |
51 | $item = $this->temporary->peek();
52 |
53 | while ($this->temporary->peek()) {
54 | $this->storage->push($this->temporary->pop());
55 | }
56 |
57 | return $item;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/exercises/ArrayGroup/Complete/ArrayGroupComplete.php:
--------------------------------------------------------------------------------
1 | $iValue) {
40 | $temporary[] = $iValue;
41 |
42 | if ($i === $last) {
43 | $grouped[] = $temporary;
44 | } elseif (count($temporary) === $size) {
45 | $grouped[] = $temporary;
46 | $temporary = [];
47 | }
48 | }
49 |
50 | return $grouped;
51 | }
52 |
53 | /**
54 | * @param mixed[] $array
55 | *
56 | * @return mixed[]
57 | */
58 | public static function group2(array $array, int $size): array
59 | {
60 | return array_chunk($array, $size);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Search/SearchTest.php:
--------------------------------------------------------------------------------
1 | doFilter($content));
22 | }
23 |
24 | /**
25 | *
26 | * @return array
27 | */
28 | public function getContentString(): array
29 | {
30 | return [
31 | ['lowercase'],
32 | ['MixUpperAndLower'],
33 | ['string#/'],
34 | ];
35 | }
36 |
37 | /**
38 | * Get same value if value is not string
39 | * @dataProvider getCongetInvalidContent
40 | *
41 | * @param mix $content
42 | * @return void
43 | */
44 | public function testDoFilterWithNoStringData($content): void
45 | {
46 | $upperCaseFilter = new UpperCaseFilter();
47 | self::assertSame($content, $upperCaseFilter->doFilter($content));
48 | }
49 |
50 | /**
51 | *
52 | * @return array
53 | */
54 | public function getCongetInvalidContent(): array
55 | {
56 | return [
57 | [34],
58 | ['45']
59 | ];
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/exercises/Pyramid/Complete/PyramidComplete.php:
--------------------------------------------------------------------------------
1 | = $middleLower && $middleUpper >= $column) {
24 | $line .= '#';
25 | } else {
26 | $line .= ' ';
27 | }
28 | }
29 | echo $line;
30 | }
31 | }
32 |
33 | public static function print2(int $rows, int $row = 0, string $line = ''): void
34 | {
35 | if ($rows === $row) {
36 | return;
37 | }
38 |
39 | $columns = $rows * 2 - 1;
40 | $length = strlen($line);
41 |
42 | if ($length === $columns) {
43 | echo $line;
44 | self::print2($rows, $row + 1);
45 |
46 | return;
47 | }
48 |
49 | $middle = floor($columns / 2);
50 | $middleLower = $middle - $row;
51 | $middleUpper = $middle + $row;
52 |
53 | if ($length >= $middleLower && $middleUpper >= $length) {
54 | $line .= '#';
55 | } else {
56 | $line .= ' ';
57 | }
58 |
59 | self::print2($rows, $row, $line);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/Search/Complete/SearchCompleteTest.php:
--------------------------------------------------------------------------------
1 | setMultiple($multiple);
25 |
26 | self::assertSame($content * $multiple, $multipleFilter->Filter($content));
27 | }
28 |
29 | /**
30 | *
31 | * @return array
32 | */
33 | public function getContentNumbers(): array
34 | {
35 | return [
36 | [99999999999999, 2],
37 | [12, 2],
38 | [0, 0],
39 | ['0', 0],
40 | ];
41 | }
42 |
43 | /**
44 | * @dataProvider getContentNoNumbers
45 | *
46 | * @param int|double $content
47 | * @return void
48 | */
49 | public function testDoFilterWithNoNumber($content, int $multiple): void
50 | {
51 | $multipleFilter = new MultipleFilter();
52 | $multipleFilter->setMultiple($multiple);
53 |
54 | self::assertSame($content, $multipleFilter->Filter($content));
55 | }
56 |
57 | /**
58 | *
59 | * @return array
60 | */
61 | public function getContentNoNumbers(): array
62 | {
63 | return [
64 | ['Test', 4],
65 | [4.5, 0],
66 | ];
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/exercises/Search/Complete/SearchComplete.php:
--------------------------------------------------------------------------------
1 | $v) {
21 | if ($v === $n) {
22 | return $i;
23 | }
24 | }
25 |
26 | return null;
27 | }
28 |
29 | /** @param mixed[] $input */
30 | public static function binary(array $input, int $n): ?int
31 | {
32 | if (count($input) === 0) {
33 | return null;
34 | }
35 |
36 | $start = 0;
37 | $end = count($input) - 1;
38 | $middle = intdiv($start + $end, 2);
39 |
40 | while ($input[$middle] !== $n && $start <= $end) {
41 | if ($n < $input[$middle]) {
42 | $end = $middle - 1;
43 | } else {
44 | $start = $middle + 1;
45 | }
46 | $middle = intdiv($start + $end, 2);
47 | }
48 |
49 | return $input[$middle] === $n ? $middle : null;
50 | }
51 |
52 | public static function naive(string $input, string $n): int
53 | {
54 | $counter = 0;
55 |
56 | for ($i = 0, $iMax = strlen($input), $nMax = strlen($n); $i < $iMax; ++$i) {
57 | for ($j = 0; $j < $nMax; ++$j) {
58 | if ($n[$j] !== $input[$i + $j]) {
59 | break;
60 | }
61 | if ($j === $nMax - 1) {
62 | ++$counter;
63 | }
64 | }
65 | }
66 |
67 | return $counter;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/exercises/Palindrome/Complete/PalindromeComplete.php:
--------------------------------------------------------------------------------
1 | root;
22 | $queue = [];
23 |
24 | while ($current) {
25 | array_push($queue, ...$current->children);
26 | $callback($current->data);
27 | $current = array_shift($queue);
28 | }
29 | }
30 |
31 | /** @param callable $callback node data will be passed as the only argument */
32 | public function traverseDepthFirst(callable $callback): void
33 | {
34 | $current = $this->root;
35 | $queue = [];
36 |
37 | while ($current) {
38 | array_unshift($queue, ...$current->children);
39 | $callback($current->data);
40 | $current = array_shift($queue);
41 | }
42 | }
43 |
44 | /** @return int[] each respective tree level width */
45 | public static function levelWidth(NodeComplete $root): array
46 | {
47 | $level = 0;
48 | $widths = [0];
49 |
50 | $queue = [$root, self::SENTINEL];
51 |
52 | while (count($queue) > 1) {
53 | $current = array_shift($queue);
54 |
55 | if ($current === self::SENTINEL) {
56 | $widths[++$level] = 0;
57 | $queue[] = self::SENTINEL;
58 | } else {
59 | array_push($queue, ...$current->children);
60 | ++$widths[$level];
61 | }
62 | }
63 |
64 | return $widths;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/ArrayGroup/Complete/ArrayGroupCompleteTest.php:
--------------------------------------------------------------------------------
1 | stack = new StackComplete();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::assertTrue(
24 | method_exists(StackComplete::class, 'push'),
25 | 'Class does not have method push'
26 | );
27 | self::assertTrue(
28 | method_exists(StackComplete::class, 'pop'),
29 | 'Class does not have method pop'
30 | );
31 | self::assertTrue(
32 | method_exists(StackComplete::class, 'peek'),
33 | 'Class does not have method peek'
34 | );
35 | }
36 |
37 | public function testCanCreateObject(): void
38 | {
39 | self::assertIsObject($this->stack);
40 | }
41 |
42 | public function testCanPush(): void
43 | {
44 | $this->stack->push(1);
45 | $this->stack->push(2);
46 | $this->stack->push(3);
47 | self::assertTrue(true);
48 | }
49 |
50 | public function testCanPop(): void
51 | {
52 | $this->stack->push(1);
53 | $this->stack->push(2);
54 | $this->stack->push(3);
55 | self::assertSame(3, $this->stack->pop());
56 | self::assertSame(2, $this->stack->pop());
57 | self::assertSame(1, $this->stack->pop());
58 | self::assertNull($this->stack->pop());
59 | }
60 |
61 | public function testCanPeek(): void
62 | {
63 | self::assertNull($this->stack->peek());
64 | $this->stack->push(1);
65 | $this->stack->push(2);
66 | $this->stack->push(3);
67 |
68 | $this->stack->pop();
69 | $this->stack->push(1);
70 |
71 | self::assertSame(1, $this->stack->peek());
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "azdanov",
10 | "name": "Anton Ždanov",
11 | "avatar_url": "https://avatars2.githubusercontent.com/u/6123841?v=4",
12 | "profile": "https://azdanov.js.org/",
13 | "contributions": [
14 | "code",
15 | "doc",
16 | "test"
17 | ]
18 | },
19 | {
20 | "login": "Ahmed-Aboud",
21 | "name": "ahmed",
22 | "avatar_url": "https://avatars0.githubusercontent.com/u/25877831?v=4",
23 | "profile": "https://github.com/Ahmed-Aboud",
24 | "contributions": [
25 | "code",
26 | "test"
27 | ]
28 | },
29 | {
30 | "login": "tizis",
31 | "name": "Roman Yepanchenko",
32 | "avatar_url": "https://avatars1.githubusercontent.com/u/16865573?v=4",
33 | "profile": "https://github.com/tizis",
34 | "contributions": [
35 | "bug"
36 | ]
37 | },
38 | {
39 | "login": "fathom",
40 | "name": "Fathom",
41 | "avatar_url": "https://avatars3.githubusercontent.com/u/13853845?v=4",
42 | "profile": "https://github.com/fathom",
43 | "contributions": [
44 | "bug",
45 | "code"
46 | ]
47 | },
48 | {
49 | "login": "Olden",
50 | "name": "Alexander Katynia",
51 | "avatar_url": "https://avatars3.githubusercontent.com/u/546682?v=4",
52 | "profile": "https://github.com/Olden",
53 | "contributions": [
54 | "bug"
55 | ]
56 | },
57 | {
58 | "login": "kevin-schmitt",
59 | "name": "kevin-schmitt",
60 | "avatar_url": "https://avatars2.githubusercontent.com/u/10809414?v=4",
61 | "profile": "https://github.com/kevin-schmitt",
62 | "contributions": [
63 | "code",
64 | "test"
65 | ]
66 | }
67 | ],
68 | "contributorsPerLine": 7,
69 | "projectName": "php-interview-exercises",
70 | "projectOwner": "azdanov",
71 | "repoType": "github",
72 | "repoHost": "https://github.com",
73 | "commitConvention": "none"
74 | }
75 |
--------------------------------------------------------------------------------
/tests/Stack/StackTest.php:
--------------------------------------------------------------------------------
1 | stack = new Stack();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::markTestSkipped();
24 | self::assertTrue(
25 | method_exists(Stack::class, 'push'),
26 | 'Class does not have method push'
27 | );
28 | self::assertTrue(
29 | method_exists(Stack::class, 'pop'),
30 | 'Class does not have method pop'
31 | );
32 | self::assertTrue(
33 | method_exists(Stack::class, 'peek'),
34 | 'Class does not have method peek'
35 | );
36 | }
37 |
38 | public function testCanCreateObject(): void
39 | {
40 | self::markTestSkipped();
41 | self::assertIsObject($this->stack);
42 | }
43 |
44 | public function testCanPush(): void
45 | {
46 | self::markTestSkipped();
47 | $this->stack->push(1);
48 | $this->stack->push(2);
49 | $this->stack->push(3);
50 | self::assertTrue(true);
51 | }
52 |
53 | public function testCanPop(): void
54 | {
55 | self::markTestSkipped();
56 | $this->stack->push(1);
57 | $this->stack->push(2);
58 | $this->stack->push(3);
59 | self::assertSame(3, $this->stack->pop());
60 | self::assertSame(2, $this->stack->pop());
61 | self::assertSame(1, $this->stack->pop());
62 | self::assertNull($this->stack->pop());
63 | }
64 |
65 | public function testCanPeek(): void
66 | {
67 | self::markTestSkipped();
68 | $this->stack->push(1);
69 | $this->stack->push(2);
70 | $this->stack->push(3);
71 |
72 | $this->stack->pop();
73 | $this->stack->push(1);
74 |
75 | self::assertSame(1, $this->stack->peek());
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/tests/MaxHeap/Complete/MaxHeapCompleteTest.php:
--------------------------------------------------------------------------------
1 | heap = new MaxHeapComplete();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::assertTrue(
24 | method_exists(MaxHeapComplete::class, 'insert'),
25 | 'Class does not have method insert'
26 | );
27 | self::assertTrue(
28 | method_exists(MaxHeapComplete::class, 'extractMax'),
29 | 'Class does not have method extractMax'
30 | );
31 | }
32 |
33 | public function testCanInsertValuesInCorrectOrder(): void
34 | {
35 | $this->heap->insert(41);
36 | $this->heap->insert(39);
37 | $this->heap->insert(33);
38 | $this->heap->insert(18);
39 | $this->heap->insert(27);
40 | $this->heap->insert(12);
41 | $this->heap->insert(55);
42 |
43 | /**
44 | * 55
45 | * / \
46 | * 39 41
47 | * / \ / \
48 | * 18 27 12 33.
49 | */
50 | self::assertSame([55, 39, 41, 18, 27, 12, 33], $this->heap->values);
51 | }
52 |
53 | public function testCanExtractMaxAndOrder(): void
54 | {
55 | $this->heap->insert(41);
56 | $this->heap->insert(39);
57 | $this->heap->insert(33);
58 | $this->heap->insert(18);
59 | $this->heap->insert(27);
60 | $this->heap->insert(12);
61 | $this->heap->insert(55);
62 |
63 | self::assertSame(55, $this->heap->extractMax());
64 |
65 | /**
66 | * 41
67 | * / \
68 | * 39 33
69 | * / \ /
70 | * 18 27 12.
71 | */
72 | self::assertSame([41, 39, 33, 18, 27, 12], $this->heap->values);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tests/MaxHeap/MaxHeapTest.php:
--------------------------------------------------------------------------------
1 | heap = new MaxHeap();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::markTestSkipped();
24 | self::assertTrue(
25 | method_exists(MaxHeap::class, 'insert'),
26 | 'Class does not have method insert'
27 | );
28 | self::assertTrue(
29 | method_exists(MaxHeap::class, 'extractMax'),
30 | 'Class does not have method extractMax'
31 | );
32 | }
33 |
34 | public function testCanInsertValuesInCorrectOrder(): void
35 | {
36 | self::markTestSkipped();
37 | $this->heap->insert(41);
38 | $this->heap->insert(39);
39 | $this->heap->insert(33);
40 | $this->heap->insert(18);
41 | $this->heap->insert(27);
42 | $this->heap->insert(12);
43 | $this->heap->insert(55);
44 |
45 | /**
46 | * 55
47 | * / \
48 | * 39 41
49 | * / \ / \
50 | * 18 27 12 33.
51 | */
52 | self::assertSame([55, 39, 41, 18, 27, 12, 33], $this->heap->values);
53 | }
54 |
55 | public function testCanExtractMaxAndOrder(): void
56 | {
57 | self::markTestSkipped();
58 | $this->heap->insert(41);
59 | $this->heap->insert(39);
60 | $this->heap->insert(33);
61 | $this->heap->insert(18);
62 | $this->heap->insert(27);
63 | $this->heap->insert(12);
64 | $this->heap->insert(55);
65 |
66 | self::assertSame(55, $this->heap->extractMax());
67 |
68 | /**
69 | * 41
70 | * / \
71 | * 39 33
72 | * / \ /
73 | * 18 27 12.
74 | */
75 | self::assertSame([41, 39, 33, 18, 27, 12], $this->heap->values);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/tests/QueueFromStacks/QueueFromStacksTest.php:
--------------------------------------------------------------------------------
1 | queue = new QueueFromStacks();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::markTestSkipped();
24 | self::assertTrue(
25 | method_exists(QueueFromStacks::class, 'add'),
26 | 'Class does not have static method add'
27 | );
28 | self::assertTrue(
29 | method_exists(QueueFromStacks::class, 'remove'),
30 | 'Class does not have static method remove'
31 | );
32 | self::assertTrue(
33 | method_exists(QueueFromStacks::class, 'peek'),
34 | 'Class does not have static method peek'
35 | );
36 | }
37 |
38 | public function testCanCreateObject(): void
39 | {
40 | self::markTestSkipped();
41 | self::assertIsObject($this->queue);
42 | }
43 |
44 | public function testCanAdd(): void
45 | {
46 | self::markTestSkipped();
47 | $this->queue->add(1);
48 | $this->queue->add(2);
49 | $this->queue->add(3);
50 |
51 | self::assertTrue(true);
52 | }
53 |
54 | public function testCanRemove(): void
55 | {
56 | self::markTestSkipped();
57 | $this->queue->add(1);
58 | $this->queue->add(2);
59 | $this->queue->add(3);
60 |
61 | self::assertSame(1, $this->queue->remove());
62 | self::assertSame(2, $this->queue->remove());
63 | self::assertSame(3, $this->queue->remove());
64 | }
65 |
66 | public function testCanPeek(): void
67 | {
68 | self::markTestSkipped();
69 | $this->queue->add(1);
70 | $this->queue->add(2);
71 | $this->queue->add(3);
72 | $this->queue->remove();
73 |
74 | self::assertSame(2, $this->queue->peek());
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/QueueFromStacks/Complete/QueueFromStacksCompleteTest.php:
--------------------------------------------------------------------------------
1 | queue = new QueueFromStacksComplete();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::assertTrue(
24 | method_exists(QueueFromStacksComplete::class, 'add'),
25 | 'Class does not have method add'
26 | );
27 | self::assertTrue(
28 | method_exists(QueueFromStacksComplete::class, 'remove'),
29 | 'Class does not have method remove'
30 | );
31 | self::assertTrue(
32 | method_exists(QueueFromStacksComplete::class, 'peek'),
33 | 'Class does not have method peek'
34 | );
35 | }
36 |
37 | public function testCanCreateObject(): void
38 | {
39 | self::assertIsObject($this->queue);
40 | }
41 |
42 | public function testCanAdd(): void
43 | {
44 | $this->queue->add(1);
45 | $this->queue->add(2);
46 | $this->queue->add(3);
47 |
48 | self::assertTrue(true);
49 | }
50 |
51 | public function testCanRemove(): void
52 | {
53 | $this->queue->add(1);
54 | $this->queue->add(2);
55 | $this->queue->add(3);
56 |
57 | self::assertSame(1, $this->queue->remove());
58 | self::assertSame(2, $this->queue->remove());
59 | self::assertSame(3, $this->queue->remove());
60 | self::assertNull($this->queue->remove());
61 | }
62 |
63 | public function testCanPeek(): void
64 | {
65 | self::assertNull($this->queue->peek());
66 |
67 | $this->queue->add(1);
68 | $this->queue->add(2);
69 | $this->queue->add(3);
70 |
71 | $this->queue->remove();
72 | $this->queue->add(1);
73 |
74 | self::assertSame(2, $this->queue->peek());
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/Tree/Complete/NodeCompleteTest.php:
--------------------------------------------------------------------------------
1 | data);
37 | }
38 |
39 | public function testCanAdd(): void
40 | {
41 | $node = new NodeComplete(10);
42 |
43 | $node->add(20);
44 |
45 | self::assertCount(1, $node->children);
46 | self::assertSame(20, $node->children[0]->data);
47 | }
48 |
49 | public function testCanAddMultiple(): void
50 | {
51 | $node = new NodeComplete(10);
52 |
53 | $node->add(20);
54 | $node->add(30);
55 | $node->add(40);
56 |
57 | self::assertCount(3, $node->children);
58 | self::assertSame(40, $node->children[array_key_last($node->children)]->data);
59 | }
60 |
61 | public function testCanRemove(): void
62 | {
63 | $node = new NodeComplete(10);
64 | $node->add(20);
65 | self::assertSame(20, $node->children[0]->data);
66 |
67 | $node->remove(20);
68 |
69 | self::assertCount(0, $node->children);
70 | }
71 |
72 | public function testCanRemoveMultiple(): void
73 | {
74 | $node = new NodeComplete(10);
75 | $node->add(20);
76 | $node->add(30);
77 | $node->add(40);
78 | self::assertSame(20, $node->children[0]->data);
79 | self::assertSame(30, $node->children[1]->data);
80 | self::assertSame(40, $node->children[2]->data);
81 |
82 | $node->remove(20);
83 | $node->remove(40);
84 |
85 | self::assertCount(1, $node->children);
86 | self::assertSame(30, $node->children[0]->data);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/tests/Tree/Complete/TreeCompleteTest.php:
--------------------------------------------------------------------------------
1 | tree = new TreeComplete();
19 | }
20 |
21 | public function testHasRootProperty(): void
22 | {
23 | self::assertObjectHasAttribute('root', $this->tree);
24 | }
25 |
26 | public function testTraverseBreadthFirst(): void
27 | {
28 | $letters = [];
29 | $this->tree->root = new NodeComplete('1a');
30 | $this->tree->root->add('2b');
31 | $this->tree->root->add('2c');
32 | $this->tree->root->children[0]->add('3d');
33 | $this->tree->traverseBreadthFirst(
34 | static function ($data) use (&$letters): void {
35 | $letters[] = $data;
36 | }
37 | );
38 |
39 | self::assertEquals(['1a', '2b', '2c', '3d'], $letters);
40 | }
41 |
42 | public function testTraverseDepthFirst(): void
43 | {
44 | $letters = [];
45 | $this->tree->root = new NodeComplete('1a');
46 | $this->tree->root->add('2b');
47 | $this->tree->root->add('2c');
48 | $this->tree->root->children[0]->add('3d');
49 | $this->tree->traverseDepthFirst(
50 | static function ($data) use (&$letters): void {
51 | $letters[] = $data;
52 | }
53 | );
54 |
55 | self::assertEquals(['1a', '2b', '3d', '2c'], $letters);
56 | }
57 |
58 | public function testLevelWidth1(): void
59 | {
60 | $this->tree->root = new NodeComplete(0);
61 | $this->tree->root->add(1);
62 | $this->tree->root->add(2);
63 | $this->tree->root->add(3);
64 | $this->tree->root->children[0]->add(4);
65 | $this->tree->root->children[1]->add(5);
66 |
67 | self::assertEquals([1, 3, 2], TreeComplete::levelWidth($this->tree->root));
68 | }
69 |
70 | public function testLevelWidth2(): void
71 | {
72 | $this->tree->root = new NodeComplete(0);
73 | $this->tree->root->add(1);
74 | $this->tree->root->children[0]->add(2);
75 | $this->tree->root->children[0]->add(3);
76 | $this->tree->root->children[0]->children[0]->add(4);
77 |
78 | self::assertEquals([1, 1, 2, 1], TreeComplete::levelWidth($this->tree->root));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/ChainResponsabilityPattern/Complete/ChainResponsabilityPatternTest.php:
--------------------------------------------------------------------------------
1 | setMultiple($multiple);
32 |
33 | $upperCaseFilter->setSuccessor($multipleFilter);
34 |
35 | $resultContent = $this->getContentExcepted($content, $multiple);
36 | self::assertSame($resultContent, $upperCaseFilter->Filter($content));
37 | }
38 |
39 | /**
40 | * @dataProvider getContent
41 | * function test pattern with UpperCaseFilter from MultipleFilter
42 | *
43 | * @param [type] $content
44 | * @return void
45 | */
46 | public function testPatternSuccessorMultipleFilter($content) : void
47 | {
48 | // init
49 | $upperCaseFilter = new UpperCaseFilter();
50 | $multipleFilter = new MultipleFilter();
51 |
52 | $multipleFilter->setSuccessor($upperCaseFilter);
53 | $resultContent = $this->getContentExcepted($content);
54 |
55 | self::assertSame($resultContent, $upperCaseFilter->Filter($content));
56 | }
57 |
58 | /**
59 | * Undocumented function
60 | *
61 | * @return void
62 | */
63 | public function getContent()
64 | {
65 | return [
66 | ['test'],
67 | ['Test'],
68 | [24],
69 | ['45hello#'],
70 | [0],
71 | ];
72 | }
73 |
74 | /**
75 | * get excepted result return by pattern filters
76 | *
77 | * @param [type] $content
78 | * @return void
79 | */
80 | private function getContentExcepted($content, int $multiple = 1)
81 | {
82 |
83 | return (filter_var($content, FILTER_VALIDATE_INT) || $content === 0) ? ($multiple * $content) : strtoupper($content);
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
3 |
4 | # User-specific stuff
5 | .idea/
6 | .idea/**/workspace.xml
7 | .idea/**/tasks.xml
8 | .idea/**/usage.statistics.xml
9 | .idea/**/dictionaries
10 | .idea/**/shelf
11 |
12 | # Generated files
13 | .idea/**/contentModel.xml
14 |
15 | # Sensitive or high-churn files
16 | .idea/**/dataSources/
17 | .idea/**/dataSources.ids
18 | .idea/**/dataSources.local.xml
19 | .idea/**/sqlDataSources.xml
20 | .idea/**/dynamic.xml
21 | .idea/**/uiDesigner.xml
22 | .idea/**/dbnavigator.xml
23 |
24 | # Gradle
25 | .idea/**/gradle.xml
26 | .idea/**/libraries
27 |
28 | # Gradle and Maven with auto-import
29 | # When using Gradle or Maven with auto-import, you should exclude module files,
30 | # since they will be recreated, and may cause churn. Uncomment if using
31 | # auto-import.
32 | # .idea/modules.xml
33 | # .idea/*.iml
34 | # .idea/modules
35 |
36 | # CMake
37 | cmake-build-*/
38 |
39 | # Mongo Explorer plugin
40 | .idea/**/mongoSettings.xml
41 |
42 | # File-based project format
43 | *.iws
44 |
45 | # IntelliJ
46 | out/
47 |
48 | # mpeltonen/sbt-idea plugin
49 | .idea_modules/
50 |
51 | # JIRA plugin
52 | atlassian-ide-plugin.xml
53 |
54 | # Cursive Clojure plugin
55 | .idea/replstate.xml
56 |
57 | # Crashlytics plugin (for Android Studio and IntelliJ)
58 | com_crashlytics_export_strings.xml
59 | crashlytics.properties
60 | crashlytics-build.properties
61 | fabric.properties
62 |
63 | # Editor-based Rest Client
64 | .idea/httpRequests
65 |
66 | # Android studio 3.1+ serialized cache file
67 | .idea/caches/build_file_checksums.ser
68 |
69 | composer.phar
70 | /vendor/
71 |
72 | # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
73 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
74 | # composer.lock
75 |
76 | .vscode/*
77 | !.vscode/settings.json
78 | !.vscode/tasks.json
79 | !.vscode/launch.json
80 | !.vscode/extensions.json
81 |
82 | # Windows thumbnail cache files
83 | Thumbs.db
84 | ehthumbs.db
85 | ehthumbs_vista.db
86 |
87 | # Dump file
88 | *.stackdump
89 |
90 | # Folder config file
91 | [Dd]esktop.ini
92 |
93 | # Recycle Bin used on file shares
94 | $RECYCLE.BIN/
95 |
96 | # Windows Installer files
97 | *.cab
98 | *.msi
99 | *.msix
100 | *.msm
101 | *.msp
102 |
103 | # Windows shortcuts
104 | *.lnk
105 | .phpunit.result.cache
106 | .DS_Store
--------------------------------------------------------------------------------
/exercises/MaxHeap/Complete/MaxHeapComplete.php:
--------------------------------------------------------------------------------
1 | values[] = $value;
20 |
21 | $currentIndex = count($this->values) - 1;
22 | $currentValue = $this->values[$currentIndex];
23 |
24 | while (true) {
25 | $parentIndex = (int) (floor($currentIndex - 1) / 2);
26 | $parentValue = $this->values[$parentIndex];
27 |
28 | if ($currentValue <= $parentValue) {
29 | break;
30 | }
31 |
32 | $this->values[$parentIndex] = $currentValue;
33 | $this->values[$currentIndex] = $parentValue;
34 | $currentIndex = $parentIndex;
35 | }
36 | }
37 |
38 | /** @return mixed */
39 | public function extractMax()
40 | {
41 | $max = $this->values[0];
42 |
43 | $this->values[0] = array_pop($this->values);
44 |
45 | $currentIndex = 0;
46 | $length = count($this->values);
47 | $currentElement = $this->values[0];
48 |
49 | while (true) {
50 | /** @var int $leftChildIndex */
51 | $leftChildIndex = 2 * $currentIndex + 1;
52 | /** @var int $rightChildIndex */
53 | $rightChildIndex = 2 * $currentIndex + 2;
54 |
55 | $leftChild = null;
56 | $rightChild = null;
57 | $swapIndex = null;
58 |
59 | if ($leftChildIndex < $length) {
60 | $leftChild = $this->values[$leftChildIndex];
61 |
62 | if ($leftChild > $currentElement) {
63 | $swapIndex = $leftChildIndex;
64 | }
65 | }
66 |
67 | if ($rightChildIndex < $length) {
68 | $rightChild = $this->values[$rightChildIndex];
69 |
70 | if (($swapIndex === null && $rightChild > $currentElement) ||
71 | ($swapIndex !== null && $rightChild > $leftChild)) {
72 | $swapIndex = $rightChildIndex;
73 | }
74 | }
75 |
76 | if ($swapIndex === null) {
77 | break;
78 | }
79 |
80 | $this->values[$currentIndex] = $this->values[$swapIndex];
81 | $this->values[$swapIndex] = $currentElement;
82 | $currentIndex = $swapIndex;
83 | }
84 |
85 | return $max;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/Tree/TreeTest.php:
--------------------------------------------------------------------------------
1 | tree = new Tree();
19 | }
20 |
21 | public function testHasRootProperty(): void
22 | {
23 | self::markTestSkipped();
24 | self::assertObjectHasAttribute('root', $this->tree);
25 | }
26 |
27 | public function testTraverseBreadthFirst(): void
28 | {
29 | self::markTestSkipped();
30 | $letters = [];
31 | $this->tree->root = new Node('1a');
32 | $this->tree->root->add('2b');
33 | $this->tree->root->add('2c');
34 | $this->tree->root->children[0]->add('3d');
35 | $this->tree->traverseBreadthFirst(
36 | static function ($data) use (&$letters): void {
37 | $letters[] = $data;
38 | }
39 | );
40 |
41 | self::assertEquals(['1a', '2b', '2c', '3d'], $letters);
42 | }
43 |
44 | public function testTraverseDepthFirst(): void
45 | {
46 | self::markTestSkipped();
47 | $letters = [];
48 | $this->tree->root = new Node('1a');
49 | $this->tree->root->add('2b');
50 | $this->tree->root->add('2c');
51 | $this->tree->root->children[0]->add('3d');
52 | $this->tree->traverseDepthFirst(
53 | static function ($data) use (&$letters): void {
54 | $letters[] = $data;
55 | }
56 | );
57 |
58 | self::assertEquals(['1a', '2b', '3d', '2c'], $letters);
59 | }
60 |
61 | public function testLevelWidth1(): void
62 | {
63 | self::markTestSkipped();
64 | $this->tree->root = new Node(0);
65 | $this->tree->root->add(1);
66 | $this->tree->root->add(2);
67 | $this->tree->root->add(3);
68 | $this->tree->root->children[0]->add(4);
69 | $this->tree->root->children[1]->add(5);
70 |
71 | self::assertEquals([1, 3, 2], TreeComplete::levelWidth($this->tree->root));
72 | }
73 |
74 | public function testLevelWidth2(): void
75 | {
76 | self::markTestSkipped();
77 | $this->tree->root = new Node(0);
78 | $this->tree->root->add(1);
79 | $this->tree->root->children[0]->add(2);
80 | $this->tree->root->children[0]->add(3);
81 | $this->tree->root->children[0]->children[0]->add(4);
82 |
83 | self::assertEquals([1, 1, 2, 1], TreeComplete::levelWidth($this->tree->root));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/tests/Ladder/Complete/LadderCompleteTest.php:
--------------------------------------------------------------------------------
1 | expectedString1 = sprintf('#%s', PHP_EOL);
29 | $this->expectedString2 = sprintf('# %s##%s', PHP_EOL, PHP_EOL);
30 | $this->expectedString5 = sprintf(
31 | '# %s## %s### %s#### %s#####%s',
32 | PHP_EOL,
33 | PHP_EOL,
34 | PHP_EOL,
35 | PHP_EOL,
36 | PHP_EOL
37 | );
38 | }
39 |
40 | public function testPrint11(): void
41 | {
42 | $this->expectOutputString($this->expectedString1);
43 | LadderComplete::print1(1);
44 | }
45 |
46 | public function testPrint12(): void
47 | {
48 | $this->expectOutputString($this->expectedString2);
49 | LadderComplete::print1(2);
50 | }
51 |
52 | public function testPrint15(): void
53 | {
54 | $this->expectOutputString($this->expectedString5);
55 | LadderComplete::print1(5);
56 | }
57 |
58 | public function testPrint21(): void
59 | {
60 | $this->expectOutputString($this->expectedString1);
61 | LadderComplete::print2(1);
62 | }
63 |
64 | public function testPrint22(): void
65 | {
66 | $this->expectOutputString($this->expectedString2);
67 | LadderComplete::print2(2);
68 | }
69 |
70 | public function testPrint25(): void
71 | {
72 | $this->expectOutputString($this->expectedString5);
73 | LadderComplete::print2(5);
74 | }
75 |
76 | public function testPrint31(): void
77 | {
78 | $this->expectOutputString($this->expectedString1);
79 | LadderComplete::print3(1);
80 | }
81 |
82 | public function testPrint32(): void
83 | {
84 | $this->expectOutputString($this->expectedString2);
85 | LadderComplete::print3(2);
86 | }
87 |
88 | public function testPrint35(): void
89 | {
90 | $this->expectOutputString($this->expectedString5);
91 | LadderComplete::print3(5);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/tests/Tree/NodeTest.php:
--------------------------------------------------------------------------------
1 | data);
40 | }
41 |
42 | public function testCanAdd(): void
43 | {
44 | self::markTestSkipped();
45 | $node = new Node(10);
46 |
47 | $node->add(20);
48 |
49 | self::assertCount(1, $node->children);
50 | self::assertSame(20, $node->children[0]->data);
51 | }
52 |
53 | public function testCanAddMultiple(): void
54 | {
55 | self::markTestSkipped();
56 | $node = new Node(10);
57 |
58 | $node->add(20);
59 | $node->add(30);
60 | $node->add(40);
61 |
62 | self::assertCount(3, $node->children);
63 | self::assertSame(40, $node->children[array_key_last($node->children)]->data);
64 | }
65 |
66 | public function testCanRemove(): void
67 | {
68 | self::markTestSkipped();
69 | $node = new Node(10);
70 | $node->add(20);
71 | self::assertSame(20, $node->children[0]->data);
72 |
73 | $node->remove(20);
74 |
75 | self::assertCount(0, $node->children);
76 | }
77 |
78 | public function testCanRemoveMultiple(): void
79 | {
80 | self::markTestSkipped();
81 | $node = new Node(10);
82 | $node->add(20);
83 | $node->add(30);
84 | $node->add(40);
85 | self::assertSame(20, $node->children[0]->data);
86 | self::assertSame(30, $node->children[1]->data);
87 | self::assertSame(40, $node->children[2]->data);
88 |
89 | $node->remove(20);
90 | $node->remove(40);
91 |
92 | self::assertCount(1, $node->children);
93 | self::assertSame(30, $node->children[0]->data);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/exercises/Reverse/Complete/ReverseComplete.php:
--------------------------------------------------------------------------------
1 | = 0; $i--) {
55 | $revers .= mb_substr($string, $i, 1);
56 | }
57 |
58 | return $revers;
59 | }
60 |
61 | public static function int(int $number): int
62 | {
63 | /** @see https://wiki.php.net/rfc/combined-comparison-operator */
64 | $sign = $number <=> 0;
65 |
66 | return $sign * (int) strrev((string) $number);
67 | }
68 |
69 | public static function int2(int $number): int
70 | {
71 | $revers = 0;
72 |
73 | $sign = $number <=> 0;
74 | $number = abs($number);
75 |
76 | $n = (int) log10($number) + 1;
77 |
78 | for ($i = 1; $i <= $n; $i++) {
79 | $pow = pow(10, $n - $i);
80 | $numeral = (int) ($number / $pow);
81 | $number -= $numeral * $pow;
82 | $revers += $numeral * pow(10, $i - 1);
83 | }
84 |
85 | return $revers * $sign;
86 | }
87 |
88 | public static function int3(int $number): int
89 | {
90 | $reverse = 0;
91 |
92 | $sign = $number <=> 0;
93 | $number = abs($number);
94 |
95 | while ($number > 0) {
96 | $lastDigit = $number % 10;
97 | $reverse = ($reverse * 10) + $lastDigit;
98 | $number = (int) ($number / 10);
99 | }
100 |
101 | return $reverse * $sign;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/tests/Fibonacci/Complete/FibonacciCompleteTest.php:
--------------------------------------------------------------------------------
1 | queue = new QueueComplete();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::assertTrue(
24 | method_exists(QueueComplete::class, 'add'),
25 | 'Class does not have method add'
26 | );
27 | self::assertTrue(
28 | method_exists(QueueComplete::class, 'remove'),
29 | 'Class does not have method remove'
30 | );
31 | self::assertTrue(
32 | method_exists(QueueComplete::class, 'peek'),
33 | 'Class does not have method peek'
34 | );
35 | self::assertTrue(
36 | method_exists(QueueComplete::class, 'zip'),
37 | 'Class does not have static method zip'
38 | );
39 | }
40 |
41 | public function testCanCreateObject(): void
42 | {
43 | self::assertIsObject($this->queue);
44 | }
45 |
46 | public function testCanAdd(): void
47 | {
48 | $this->queue->add(1);
49 | $this->queue->add(2);
50 | $this->queue->add(3);
51 |
52 | self::assertTrue(true);
53 | }
54 |
55 | public function testCanRemove(): void
56 | {
57 | $this->queue->add(1);
58 | $this->queue->add(2);
59 | $this->queue->add(3);
60 |
61 | self::assertSame(1, $this->queue->remove());
62 | self::assertSame(2, $this->queue->remove());
63 | self::assertSame(3, $this->queue->remove());
64 | self::assertNull($this->queue->remove());
65 | }
66 |
67 | public function testCanPeek(): void
68 | {
69 | self::assertNull($this->queue->peek());
70 | $this->queue->add(1);
71 | $this->queue->add(2);
72 | $this->queue->add(3);
73 |
74 | $this->queue->remove();
75 | $this->queue->add(1);
76 |
77 | self::assertSame(2, $this->queue->peek());
78 | }
79 |
80 | public function testCanZip(): void
81 | {
82 | $queue1 = new QueueComplete();
83 | $queue2 = new QueueComplete();
84 |
85 | $queue1->add(1);
86 | $queue1->add(2);
87 | $queue1->add(3);
88 | $queue1->add(4);
89 | $queue2->add('a');
90 | $queue2->add('b');
91 |
92 | $queue3 = QueueComplete::zip($queue1, $queue2);
93 |
94 | self::assertSame(1, $queue3->remove());
95 | self::assertSame('a', $queue3->remove());
96 | self::assertSame(2, $queue3->remove());
97 | self::assertSame('b', $queue3->remove());
98 | self::assertSame(3, $queue3->remove());
99 | self::assertSame(4, $queue3->remove());
100 | self::assertNull($queue3->remove());
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/tests/Queue/QueueTest.php:
--------------------------------------------------------------------------------
1 | queue = new Queue();
19 | }
20 |
21 | public function testHasMethods(): void
22 | {
23 | self::markTestSkipped();
24 | self::assertTrue(
25 | method_exists(Queue::class, 'add'),
26 | 'Class does not have method add'
27 | );
28 | self::assertTrue(
29 | method_exists(Queue::class, 'remove'),
30 | 'Class does not have method remove'
31 | );
32 | self::assertTrue(
33 | method_exists(Queue::class, 'peek'),
34 | 'Class does not have method peek'
35 | );
36 | self::assertTrue(
37 | method_exists(Queue::class, 'zip'),
38 | 'Class does not have static method zip'
39 | );
40 | }
41 |
42 | public function testCanCreateObject(): void
43 | {
44 | self::markTestSkipped();
45 | self::assertIsObject($this->queue);
46 | }
47 |
48 | public function testCanAdd(): void
49 | {
50 | self::markTestSkipped();
51 | $this->queue->add(1);
52 | $this->queue->add(2);
53 | $this->queue->add(3);
54 |
55 | self::assertTrue(true);
56 | }
57 |
58 | public function testCanRemove(): void
59 | {
60 | self::markTestSkipped();
61 | $this->queue->add(1);
62 | $this->queue->add(2);
63 | $this->queue->add(3);
64 |
65 | self::assertSame(1, $this->queue->remove());
66 | self::assertSame(2, $this->queue->remove());
67 | self::assertSame(3, $this->queue->remove());
68 | }
69 |
70 | public function testCanPeek(): void
71 | {
72 | self::markTestSkipped();
73 | $this->queue->add(1);
74 | $this->queue->add(2);
75 | $this->queue->add(3);
76 |
77 | $this->queue->remove();
78 | $this->queue->add(1);
79 |
80 | self::assertSame(2, $this->queue->peek());
81 | }
82 |
83 | public function testCanZip(): void
84 | {
85 | self::markTestSkipped();
86 | $queue1 = new Queue();
87 | $queue2 = new Queue();
88 |
89 | $queue1->add(1);
90 | $queue1->add(2);
91 | $queue1->add(3);
92 | $queue1->add(4);
93 | $queue2->add('a');
94 | $queue2->add('b');
95 |
96 | $queue3 = Queue::zip($queue1, $queue2);
97 |
98 | self::assertSame(1, $queue3->remove());
99 | self::assertSame('a', $queue3->remove());
100 | self::assertSame(2, $queue3->remove());
101 | self::assertSame('b', $queue3->remove());
102 | self::assertSame(3, $queue3->remove());
103 | self::assertSame(4, $queue3->remove());
104 | self::assertNull($queue3->remove());
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/tests/Sort/SortTest.php:
--------------------------------------------------------------------------------
1 | sorted, Sort::bubble($this->input));
58 | }
59 |
60 | public function testSelectionSort(): void
61 | {
62 | self::markTestSkipped();
63 | self::assertSame($this->sorted, Sort::selection($this->input));
64 | }
65 |
66 | public function testInsertionSort(): void
67 | {
68 | self::markTestSkipped();
69 | self::assertSame($this->sorted, Sort::insertion($this->input));
70 | }
71 |
72 | /** @throws ReflectionException */
73 | public function testMergerHelper(): void
74 | {
75 | self::markTestSkipped();
76 | $left = [1, 9];
77 | $right = [3, 7, 10];
78 |
79 | $merger = self::getMethod(Sort::class, 'merger');
80 |
81 | self::assertSame([1, 3, 7, 9, 10], $merger->invoke(null, $left, $right));
82 | }
83 |
84 | public function testMergeSort(): void
85 | {
86 | self::markTestSkipped();
87 | self::assertSame($this->sorted, Sort::merge($this->input));
88 | }
89 |
90 | public function testQuickSort(): void
91 | {
92 | self::markTestSkipped();
93 | self::assertSame($this->sorted, Sort::quick($this->input));
94 | }
95 |
96 | public function testRadixSort(): void
97 | {
98 | self::markTestSkipped();
99 | self::assertSame([ 12, 23, 345, 2345, 5467, 9852 ], Sort::radix([23, 345, 5467, 12, 2345, 9852]));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/tests/Sort/Complete/SortCompleteTest.php:
--------------------------------------------------------------------------------
1 | sorted, SortComplete::bubble($this->input));
61 | }
62 |
63 | public function testSelectionSort(): void
64 | {
65 | self::assertSame($this->sorted, SortComplete::selection($this->input));
66 | }
67 |
68 | public function testInsertionSort(): void
69 | {
70 | self::assertSame($this->sorted, SortComplete::insertion($this->input));
71 | }
72 |
73 | /** @throws ReflectionException */
74 | public function testMergerHelper(): void
75 | {
76 | $left = [1, 9];
77 | $right = [3, 7, 10];
78 |
79 | $merger = self::getMethod(SortComplete::class, 'merger');
80 |
81 | self::assertSame([1, 3, 7, 9, 10], $merger->invoke(null, $left, $right));
82 | }
83 |
84 | public function testMergeSort(): void
85 | {
86 | self::assertSame($this->sorted, SortComplete::merge($this->input));
87 | }
88 |
89 | public function testQuick1Sort(): void
90 | {
91 | self::assertSame($this->sorted, SortComplete::quick1($this->input));
92 | }
93 |
94 | public function testQuick2Sort(): void
95 | {
96 | SortComplete::quick2($this->input);
97 | self::assertSame($this->sorted, $this->input);
98 | }
99 |
100 | public function testRadixSort(): void
101 | {
102 | self::assertSame([ 12, 23, 345, 2345, 5467, 9852 ], SortComplete::radix([23, 345, 5467, 12, 2345, 9852]));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/tests/BinarySearchTree/BinarySearchTreeTest.php:
--------------------------------------------------------------------------------
1 | insert(5);
40 | $node->insert(15);
41 | $node->insert(20);
42 |
43 | self::assertSame(5, $node->left->data);
44 | self::assertSame(15, $node->right->data);
45 | self::assertSame(20, $node->right->right->data);
46 | }
47 |
48 | public function testContains(): void
49 | {
50 | self::markTestSkipped();
51 | $node = new BinarySearchTree(10);
52 |
53 | $node->insert(5);
54 | $node->insert(15);
55 | $node->insert(20);
56 | $node->insert(0);
57 | $node->insert(-5);
58 | $node->insert(3);
59 |
60 | $three = $node->left->left->right;
61 |
62 | self::assertSame($three, $node->contains(3));
63 | }
64 |
65 | /**
66 | * 10
67 | * / \
68 | * 5 15
69 | * / \
70 | * 0 20
71 | * / \
72 | * -5 3.
73 | */
74 | public function testDepthFirstInOrder(): void
75 | {
76 | self::markTestSkipped();
77 | $node = new BinarySearchTree(10);
78 |
79 | $node->insert(5);
80 | $node->insert(15);
81 | $node->insert(20);
82 | $node->insert(0);
83 | $node->insert(-5);
84 | $node->insert(3);
85 |
86 | self::assertSame([-5, 0, 3, 5, 10, 15, 20], $node->depthFirstInOrder());
87 | }
88 |
89 | /**
90 | * 10
91 | * / \
92 | * 5 15
93 | * / \
94 | * 0 20
95 | * / \
96 | * -5 3.
97 | */
98 | public function testDepthFirstPostOrder(): void
99 | {
100 | self::markTestSkipped();
101 | $node = new BinarySearchTree(10);
102 |
103 | $node->insert(5);
104 | $node->insert(15);
105 | $node->insert(20);
106 | $node->insert(0);
107 | $node->insert(-5);
108 | $node->insert(3);
109 |
110 | self::assertSame([-5, 3, 0, 5, 20, 15, 10], $node->depthFirstPostOrder());
111 | }
112 |
113 | /**
114 | * 10
115 | * / \
116 | * 5 15
117 | * / \
118 | * 0 20
119 | * / \
120 | * -5 3.
121 | */
122 | public function testDepthFirstPreOrder(): void
123 | {
124 | self::markTestSkipped();
125 | $node = new BinarySearchTree(10);
126 |
127 | $node->insert(5);
128 | $node->insert(15);
129 | $node->insert(20);
130 | $node->insert(0);
131 | $node->insert(-5);
132 | $node->insert(3);
133 |
134 | self::assertSame([10, 5, 0, -5, 3, 15, 20], $node->depthFirstPreOrder());
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/tests/Events/Complete/EventsCompleteTest.php:
--------------------------------------------------------------------------------
1 | on('load', $callback);
44 | $events->fire('load');
45 |
46 | self::assertEquals(1, $counter);
47 | }
48 |
49 | public function testOnAndFireMultipleCallbacks(): void
50 | {
51 | $counter = 0;
52 | $events = new EventsComplete();
53 | $callback1 = static function () use (&$counter): void {
54 | ++$counter;
55 | };
56 | $callback2 = static function () use (&$counter): void {
57 | ++$counter;
58 | };
59 |
60 | $events->on('load', $callback1);
61 | $events->on('load', $callback2);
62 | $events->fire('load');
63 |
64 | self::assertEquals(2, $counter);
65 | }
66 |
67 | public function testOnAndFireMultipleTimes(): void
68 | {
69 | $counter = 0;
70 | $events = new EventsComplete();
71 | $callback1 = static function () use (&$counter): void {
72 | ++$counter;
73 | };
74 | $callback2 = static function () use (&$counter): void {
75 | ++$counter;
76 | };
77 |
78 | $events->on('load', $callback1);
79 | $events->on('load', $callback2);
80 | $events->fire('load');
81 | $events->fire('load');
82 | $events->fire('load');
83 |
84 | self::assertEquals(6, $counter);
85 | }
86 |
87 | public function testOnAndFireWithDifferentNames(): void
88 | {
89 | $counter = 0;
90 | $events = new EventsComplete();
91 | $callback1 = static function () use (&$counter): void {
92 | ++$counter;
93 | };
94 | $callback2 = static function () use (&$counter): void {
95 | ++$counter;
96 | };
97 |
98 | $events->on('load', $callback1);
99 | $events->on('visit', $callback2);
100 | $events->on('load', $callback2);
101 | $events->fire('load');
102 | $events->fire('visit');
103 | $events->fire('load');
104 |
105 | self::assertEquals(5, $counter);
106 | }
107 |
108 | public function testOff(): void
109 | {
110 | $counter = 0;
111 | $events = new EventsComplete();
112 | $callback1 = static function () use (&$counter): void {
113 | ++$counter;
114 | };
115 | $callback2 = static function () use (&$counter): void {
116 | ++$counter;
117 | };
118 |
119 | $events->on('load', $callback1);
120 | $events->on('visit', $callback2);
121 | $events->on('load', $callback2);
122 |
123 | $events->off('load');
124 |
125 | $events->fire('load');
126 | $events->fire('visit');
127 | $events->fire('load');
128 |
129 | self::assertEquals(1, $counter);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/tests/Events/EventsTest.php:
--------------------------------------------------------------------------------
1 | on('load', $callback);
47 | $events->fire('load');
48 |
49 | self::assertSame(1, $counter);
50 | }
51 |
52 | public function testOnAndFireMultipleCallbacks(): void
53 | {
54 | self::markTestSkipped();
55 | $counter = 0;
56 | $events = new Events();
57 | $callback1 = static function () use (&$counter): void {
58 | ++$counter;
59 | };
60 | $callback2 = static function () use (&$counter): void {
61 | ++$counter;
62 | };
63 |
64 | $events->on('load', $callback1);
65 | $events->on('load', $callback2);
66 | $events->fire('load');
67 |
68 | self::assertSame(2, $counter);
69 | }
70 |
71 | public function testOnAndFireMultipleTimes(): void
72 | {
73 | self::markTestSkipped();
74 | $counter = 0;
75 | $events = new Events();
76 | $callback1 = static function () use (&$counter): void {
77 | ++$counter;
78 | };
79 | $callback2 = static function () use (&$counter): void {
80 | ++$counter;
81 | };
82 |
83 | $events->on('load', $callback1);
84 | $events->on('load', $callback2);
85 | $events->fire('load');
86 | $events->fire('load');
87 | $events->fire('load');
88 |
89 | self::assertSame(6, $counter);
90 | }
91 |
92 | public function testOnAndFireWithDifferentNames(): void
93 | {
94 | self::markTestSkipped();
95 | $counter = 0;
96 | $events = new Events();
97 | $callback1 = static function () use (&$counter): void {
98 | ++$counter;
99 | };
100 | $callback2 = static function () use (&$counter): void {
101 | ++$counter;
102 | };
103 |
104 | $events->on('load', $callback1);
105 | $events->on('visit', $callback2);
106 | $events->on('load', $callback2);
107 | $events->fire('load');
108 | $events->fire('visit');
109 | $events->fire('load');
110 |
111 | self::assertSame(5, $counter);
112 | }
113 |
114 | public function testOff(): void
115 | {
116 | self::markTestSkipped();
117 | $counter = 0;
118 | $events = new Events();
119 | $callback1 = static function () use (&$counter): void {
120 | ++$counter;
121 | };
122 | $callback2 = static function () use (&$counter): void {
123 | ++$counter;
124 | };
125 |
126 | $events->on('load', $callback1);
127 | $events->on('visit', $callback2);
128 | $events->on('load', $callback2);
129 |
130 | $events->off('load');
131 |
132 | $events->fire('load');
133 | $events->fire('visit');
134 | $events->fire('load');
135 |
136 | self::assertSame(1, $counter);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/exercises/BinarySearchTree/Complete/BinarySearchTreeComplete.php:
--------------------------------------------------------------------------------
1 | data = $data;
20 | }
21 |
22 | public static function validate(
23 | self $node,
24 | ?int $min = null,
25 | ?int $max = null
26 | ): bool {
27 | if (($max !== null && $node->data > $max) ||
28 | ($min !== null && $node->data < $min)) {
29 | return false;
30 | }
31 |
32 | if (($node->left && !self::validate($node->left, $min, $node->data)) ||
33 | ($node->right && !self::validate($node->right, $node->data, $max))) {
34 | return false;
35 | }
36 |
37 | return true;
38 | }
39 |
40 | /** @param mixed $data */
41 | public function insert($data): void
42 | {
43 | if ($this->left && $data < $this->data) {
44 | $this->left->insert($data);
45 |
46 | return;
47 | }
48 |
49 | if ($this->right && $data > $this->data) {
50 | $this->right->insert($data);
51 |
52 | return;
53 | }
54 |
55 | if ($data < $this->data) {
56 | $this->left = new self($data);
57 |
58 | return;
59 | }
60 |
61 | if ($data > $this->data) {
62 | $this->right = new self($data);
63 | }
64 | }
65 |
66 | /** @param mixed $data */
67 | public function contains($data): ?self
68 | {
69 | if ($data === $this->data) {
70 | return $this;
71 | }
72 |
73 | if ($this->left && $data < $this->data) {
74 | return $this->left->contains($data);
75 | }
76 |
77 | if ($this->right && $data > $this->data) {
78 | return $this->right->contains($data);
79 | }
80 |
81 | return null;
82 | }
83 |
84 | /** @return mixed[] */
85 | public function depthFirstInOrder(): array
86 | {
87 | $data = [];
88 |
89 | self::traverseInOrder($this, static function (self $node) use (&$data): void {
90 | $data[] = $node->data;
91 | });
92 |
93 | return $data;
94 | }
95 |
96 | /** @return mixed[] */
97 | public function depthFirstPostOrder(): array
98 | {
99 | $data = [];
100 |
101 | self::traversePostOrder($this, static function (self $node) use (&$data): void {
102 | $data[] = $node->data;
103 | });
104 |
105 | return $data;
106 | }
107 |
108 | /** @return mixed[] */
109 | public function depthFirstPreOrder(): array
110 | {
111 | $data = [];
112 |
113 | self::traversePreOrder($this, static function (self $node) use (&$data): void {
114 | $data[] = $node->data;
115 | });
116 |
117 | return $data;
118 | }
119 |
120 | /** @param mixed[] $node */
121 | private static function traverseInOrder(self $node, callable $cb): void
122 | {
123 | if ($node->left) {
124 | self::traverseInOrder($node->left, $cb);
125 | }
126 |
127 | $cb($node);
128 |
129 | if ($node->right) {
130 | self::traverseInOrder($node->right, $cb);
131 | }
132 | }
133 |
134 | /** @param mixed[] $node */
135 | private static function traversePostOrder(self $node, callable $cb): void
136 | {
137 | if ($node->left) {
138 | self::traversePostOrder($node->left, $cb);
139 | }
140 |
141 | if ($node->right) {
142 | self::traversePostOrder($node->right, $cb);
143 | }
144 |
145 | $cb($node);
146 | }
147 |
148 | /** @param mixed[] $node */
149 | private static function traversePreOrder(self $node, callable $cb): void
150 | {
151 | $cb($node);
152 |
153 | if ($node->left) {
154 | self::traversePreOrder($node->left, $cb);
155 | }
156 |
157 | if ($node->right) {
158 | self::traversePreOrder($node->right, $cb);
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/tests/BinarySearchTree/Complete/BinarySearchTreeCompleteTest.php:
--------------------------------------------------------------------------------
1 | insert(5);
37 | $node->insert(15);
38 | $node->insert(20);
39 |
40 | self::assertSame(5, $node->left->data);
41 | self::assertSame(15, $node->right->data);
42 | self::assertSame(20, $node->right->right->data);
43 | }
44 |
45 | public function testContains(): void
46 | {
47 | $node = new BinarySearchTreeComplete(10);
48 |
49 | $node->insert(5);
50 | $node->insert(15);
51 | $node->insert(20);
52 | $node->insert(0);
53 | $node->insert(-5);
54 | $node->insert(3);
55 |
56 | $three = $node->left->left->right;
57 |
58 | self::assertSame($three, $node->contains(3));
59 | }
60 |
61 | public function testValidateAValidTree(): void
62 | {
63 | $node = new BinarySearchTreeComplete(10);
64 |
65 | $node->insert(5);
66 | $node->insert(15);
67 | $node->insert(0);
68 | $node->insert(20);
69 |
70 | self::assertTrue(BinarySearchTreeComplete::validate($node));
71 | }
72 |
73 | public function testValidateAnInvalidTree(): void
74 | {
75 | $node = new BinarySearchTreeComplete(10);
76 |
77 | $node->insert(5);
78 | $node->insert(15);
79 | $node->insert(0);
80 | $node->insert(20);
81 | $node->left->left->right = new BinarySearchTreeComplete(10);
82 |
83 | self::assertFalse(BinarySearchTreeComplete::validate($node));
84 | }
85 |
86 | /**
87 | * 10
88 | * / \
89 | * 5 15
90 | * / \
91 | * 0 20
92 | * / \
93 | * -5 3.
94 | */
95 | public function testDepthFirstInOrder(): void
96 | {
97 | $node = new BinarySearchTreeComplete(10);
98 |
99 | $node->insert(5);
100 | $node->insert(15);
101 | $node->insert(20);
102 | $node->insert(0);
103 | $node->insert(-5);
104 | $node->insert(3);
105 |
106 | self::assertSame([-5, 0, 3, 5, 10, 15, 20], $node->depthFirstInOrder());
107 | }
108 |
109 | /**
110 | * 10
111 | * / \
112 | * 5 15
113 | * / \
114 | * 0 20
115 | * / \
116 | * -5 3.
117 | */
118 | public function testDepthFirstPostOrder(): void
119 | {
120 | $node = new BinarySearchTreeComplete(10);
121 |
122 | $node->insert(5);
123 | $node->insert(15);
124 | $node->insert(20);
125 | $node->insert(0);
126 | $node->insert(-5);
127 | $node->insert(3);
128 |
129 | self::assertSame([-5, 3, 0, 5, 20, 15, 10], $node->depthFirstPostOrder());
130 | }
131 |
132 | /**
133 | * 10
134 | * / \
135 | * 5 15
136 | * / \
137 | * 0 20
138 | * / \
139 | * -5 3.
140 | */
141 | public function testDepthFirstPreOrder(): void
142 | {
143 | $node = new BinarySearchTreeComplete(10);
144 |
145 | $node->insert(5);
146 | $node->insert(15);
147 | $node->insert(20);
148 | $node->insert(0);
149 | $node->insert(-5);
150 | $node->insert(3);
151 |
152 | self::assertSame([10, 5, 0, -5, 3, 15, 20], $node->depthFirstPreOrder());
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/tests/Palindrome/Complete/PalindromeCompleteTest.php:
--------------------------------------------------------------------------------
1 | head = $a;
41 | * $a->next = $b;
42 | * $b->next = $c;
43 | * $c->next = $a;
44 | * LinkedList::circular($list) === true
45 | */
46 | public static function circular(self $list): ?Node
47 | {
48 | }
49 |
50 | /**
51 | * Return a node that is located n places from the end.
52 | *
53 | * Do not use size, and assume that n is always less than
54 | * the list length.
55 | */
56 | public static function fromLast(self $list, int $n): ?Node
57 | {
58 | }
59 |
60 | /**
61 | * Create a new Node from data and assign the node to the head property.
62 | *
63 | * @param mixed $data
64 | */
65 | public function insertFirst($data): void
66 | {
67 | }
68 |
69 | /**
70 | * Create a new Node from data and assign the node at the end.
71 | *
72 | * @param mixed $data
73 | */
74 | public function insertLast($data): void
75 | {
76 | }
77 |
78 | /**
79 | * @return int number of nodes in the linked list
80 | */
81 | public function size(): int
82 | {
83 | }
84 |
85 | /**
86 | * @return Node first node in the linked list
87 | */
88 | public function getFirst(): Node
89 | {
90 | }
91 |
92 | /**
93 | * @return Node first node in the linked list
94 | */
95 | public function getLast(): Node
96 | {
97 | }
98 |
99 | /**
100 | * Empty the linked list.
101 | */
102 | public function clear(): void
103 | {
104 | }
105 |
106 | /**
107 | * Remove first node from the linked list.
108 | */
109 | public function removeFirst(): void
110 | {
111 | }
112 |
113 | /**
114 | * Remove last node from the linked list.
115 | */
116 | public function removeLast(): void
117 | {
118 | }
119 |
120 | /**
121 | * @return Node at specified index
122 | */
123 | public function getAt(int $index): Node
124 | {
125 | }
126 |
127 | /**
128 | * @param int $index to remove node at
129 | */
130 | public function removeAt(int $index): void
131 | {
132 | }
133 |
134 | /**
135 | * Insert data at position.
136 | *
137 | * @param mixed $data
138 | */
139 | public function insertAt($data, int $index): void
140 | {
141 | }
142 |
143 | /**
144 | * @param callable $callback to call for each node in linked list
145 | */
146 | public function forEach(callable $callback): void
147 | {
148 | }
149 |
150 | /**
151 | * Return the current element.
152 | *
153 | * @see https://php.net/manual/en/iterator.current.php
154 | *
155 | * @return mixed can return any type
156 | */
157 | public function current()
158 | {
159 | }
160 |
161 | /**
162 | * Move forward to next element.
163 | *
164 | * @see https://php.net/manual/en/iterator.next.php
165 | */
166 | public function next(): void
167 | {
168 | }
169 |
170 | /**
171 | * Return the key of the current element.
172 | *
173 | * @see https://php.net/manual/en/iterator.key.php
174 | *
175 | * @return mixed scalar on success, or null on failure
176 | */
177 | public function key()
178 | {
179 | }
180 |
181 | /**
182 | * Checks if current position is valid.
183 | *
184 | * @see https://php.net/manual/en/iterator.valid.php
185 | *
186 | * @return bool The return value will be casted to boolean and then evaluated.
187 | * Returns true on success or false on failure.
188 | */
189 | public function valid(): bool
190 | {
191 | }
192 |
193 | /**
194 | * Rewind the Iterator to the first element.
195 | *
196 | * @see https://php.net/manual/en/iterator.rewind.php
197 | */
198 | public function rewind(): void
199 | {
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/exercises/LinkedList/Complete/LinkedListComplete.php:
--------------------------------------------------------------------------------
1 | head = null;
19 | }
20 |
21 | public function size(): int
22 | {
23 | $count = 0;
24 |
25 | $current = $this->head;
26 |
27 | while ($current) {
28 | ++$count;
29 | $current = $current->next;
30 | }
31 |
32 | return $count;
33 | }
34 |
35 | public function getFirst(): ?NodeComplete
36 | {
37 | return $this->head;
38 | }
39 |
40 | public function getLast(): ?NodeComplete
41 | {
42 | $current = $this->head;
43 | if ($current === null) {
44 | return $current;
45 | }
46 |
47 | while ($current->next !== null) {
48 | $current = $current->next;
49 | }
50 |
51 | return $current;
52 | }
53 |
54 | public function clear(): void
55 | {
56 | $this->head = null;
57 | }
58 |
59 | public function removeLast(): void
60 | {
61 | $current = $this->head;
62 | if ($current === null) {
63 | return;
64 | }
65 |
66 | $previous = null;
67 | while ($current->next) {
68 | $previous = $current;
69 | $current = $current->next;
70 | }
71 |
72 | $previous->next = null;
73 | }
74 |
75 | public function removeAt(int $index): void
76 | {
77 | $current = $this->head;
78 |
79 | if ($current === null) {
80 | return;
81 | }
82 |
83 | if ($index === 0 || $current->next === null) {
84 | $this->removeFirst();
85 |
86 | return;
87 | }
88 |
89 | $previous = null;
90 | for ($i = 0; $current->next && $i <= $index; ++$i) {
91 | $previous = $current;
92 | $current = $current->next;
93 | }
94 |
95 | $previous->next = $current->next;
96 | }
97 |
98 | public function removeFirst(): void
99 | {
100 | if ($this->head === null) {
101 | return;
102 | }
103 |
104 | $this->head = $this->head->next;
105 | }
106 |
107 | /** @param mixed $data */
108 | public function insertAt($data, int $index): void
109 | {
110 | $current = $this->head;
111 | if ($index <= 0 || !$current) {
112 | $this->insertFirst($data);
113 |
114 | return;
115 | }
116 |
117 | $previous = null;
118 | for ($i = 0; $current->next && $i < $index; ++$i) {
119 | $previous = $current;
120 | $current = $current->next;
121 | }
122 |
123 | if ($current->next === null) {
124 | $this->insertLast($data);
125 |
126 | return;
127 | }
128 |
129 | $previous->next = new NodeComplete($data, $current);
130 | }
131 |
132 | /** @param mixed $data */
133 | public function insertFirst($data): void
134 | {
135 | if ($this->head) {
136 | $this->head = new NodeComplete($data, $this->head);
137 | } else {
138 | $this->head = new NodeComplete($data);
139 | }
140 | }
141 |
142 | /** @param mixed $data */
143 | public function insertLast($data): void
144 | {
145 | $last = $this->getLast();
146 | $new = new NodeComplete($data);
147 |
148 | if ($last) {
149 | $last->next = $new;
150 | } else {
151 | $this->head = $new;
152 | }
153 | }
154 |
155 | public function forEach(callable $callback): void
156 | {
157 | $current = $this->head;
158 |
159 | $counter = 0;
160 | while ($current) {
161 | $callback($current->data, $counter++);
162 | $current = $current->next;
163 | }
164 | }
165 |
166 | public function current(): NodeComplete
167 | {
168 | return $this->getAt($this->position);
169 | }
170 |
171 | public function getAt(int $index): ?NodeComplete
172 | {
173 | $current = $this->head;
174 | for ($i = 0; $current && $i < $index; ++$i) {
175 | $current = $current->next;
176 | }
177 |
178 | return $current;
179 | }
180 |
181 | public function next(): void
182 | {
183 | ++$this->position;
184 | }
185 |
186 | public function key(): int
187 | {
188 | return $this->position;
189 | }
190 |
191 | public function valid(): bool
192 | {
193 | return isset($this->getAt($this->position)->data);
194 | }
195 |
196 | public function rewind(): void
197 | {
198 | $this->position = 0;
199 | }
200 |
201 | public static function midpoint(self $list): ?NodeComplete
202 | {
203 | $current = $list->head;
204 | if (!$current) {
205 | return $current;
206 | }
207 |
208 | $sentinel = $list->head;
209 | while ($sentinel->next && $sentinel->next->next) {
210 | $current = $current->next;
211 | $sentinel = $sentinel->next->next;
212 | }
213 |
214 | return $current;
215 | }
216 |
217 | public static function circular(self $list): bool
218 | {
219 | $current = $list->head;
220 | if (!$current) {
221 | return false;
222 | }
223 |
224 | $sentinel = $list->head;
225 | while ($sentinel->next && $sentinel->next->next) {
226 | $current = $current->next;
227 | $sentinel = $sentinel->next->next;
228 |
229 | if ($current === $sentinel) {
230 | return true;
231 | }
232 | }
233 |
234 | return false;
235 | }
236 |
237 | public static function fromLast(self $list, int $n): ?NodeComplete
238 | {
239 | $current = $list->head;
240 |
241 | if (!$current) {
242 | return null;
243 | }
244 |
245 | $sentinel = $list->head;
246 |
247 | while ($n-- > 0) {
248 | $sentinel = $sentinel->next;
249 | }
250 |
251 | while ($sentinel->next) {
252 | $current = $current->next;
253 | $sentinel = $sentinel->next;
254 | }
255 |
256 | return $current;
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP Interview Exercises · [](https://travis-ci.com/azdanov/php-interview-exercises) [](http://makeapullrequest.com) [](#contributors)
2 |
3 | __[NO LONGER MAINTAINED]__: The exercises should still be useful when preparing for a PHP interview, but I'm not able to maintain the repository.
4 |
5 | A number of exercises to practice whiteboard interview questions in PHP.
6 |
7 | Inside `exercises` directory, you can find folders containing the exercises and completed versions.
8 | There are multiple ways to solve each problem, feel free to experiment.
9 |
10 | If you are stuck don't be afraid to search online for a pseudo-code algorithm or a solution. Even by copying and later analyzing the solution you will gain lots of experience in problem solving.
11 |
12 | Also check out the tests and see what results are expected. Playing around with [Xdebug](https://xdebug.org/) is a plus.
13 |
14 | It is useful to actually see how algorithms manipulate data. [Visualgo](https://visualgo.net/en) is a good resource for that.
15 |
16 | ## Install
17 |
18 | 1. Clone this repository.
19 | 2. Run `composer install`.
20 |
21 | ## Usage
22 |
23 | 1. Pick an exercise for example `exercises/ReverseString` and inside `tests/ReverseString/ReverseStringTest.php` delete `static::markTestSkipped();`.
24 | 2. Run `./vendor/bin/phpunit` or `./vendor/bin/phpunit-watcher watch` to start tests.
25 | 3. Write implementation.
26 |
27 | This will allow you to write code and be sure that the solution is correct.
28 |
29 | ## Problem Solving Patterns
30 |
31 | To solve certain problems in the most efficient way many [algorithmic patterns](https://cs.lmu.edu/~ray/notes/algpatterns/) are available.
32 | Such as:
33 |
34 | - Frequency Counter
35 | - Multiple Pointers
36 | - Sliding Window
37 | - Divide and Conquer
38 | - Backtracking
39 |
40 | And more...
41 |
42 | ## Suggested Path
43 |
44 | - [FizzBuzz](./exercises/FizzBuzz/FizzBuzz.php)
45 | - [Numbers](./exercises/Numbers/Numbers.php)
46 | - [Reverse](./exercises/Reverse/Reverse.php)
47 | - [Palindrome](./exercises/Palindrome/Palindrome.php)
48 | - [MaxChar](./exercises/MaxChar/MaxChar.php)
49 | - [ArrayGroup](./exercises/ArrayGroup/ArrayGroup.php)
50 | - [Capitalize](./exercises/Capitalize/Capitalize.php)
51 | - [Vowels](./exercises/Vowels/Vowels.php)
52 | - [Anagram](./exercises/Anagram/Anagram.php)
53 | - [MaxSubArraySum](./exercises/MaxSubArraySum/MaxSubArraySum.php)
54 | - [Search](./exercises/Search/Search.php)
55 | - [Ladder](./exercises/Ladder/Ladder.php)
56 | - [Pyramid](./exercises/Pyramid/Pyramid.php)
57 | - [Spiral](./exercises/Spiral/Spiral.php)
58 | - [Fibonacci](./exercises/Fibonacci/Fibonacci.php)
59 | - [Events](./exercises/Events/Events.php)
60 | - [Queue](./exercises/Queue/Queue.php)
61 | - [Stack](./exercises/Stack/Stack.php)
62 | - [QueueFromStacks](./exercises/QueueFromStacks/QueueFromStacks.php)
63 | - [Sort](./exercises/Sort/Sort.php)
64 | - [LinkedList](./exercises/LinkedList/LinkedList.php)
65 | - [Tree](./exercises/Tree/Tree.php)
66 | - [BinarySearchTree](./exercises/BinarySearchTree/BinarySearchTree.php)
67 | - [MaxHeap](./exercises/MaxHeap/MaxHeap.php)
68 |
69 | ## Aside
70 |
71 | [](./exercises)
72 |
73 | This Exercise Counter is made with [Shields IO](https://shields.io/endpoint), [Axios](https://github.com/axios/axios), [GitHub GraphQL](https://developer.github.com/v4/) and [Netlify Functions](https://www.netlify.com/docs/functions/).
74 |
75 | ## Contributing
76 |
77 | Do you have an idea for an exercise or a better solution? Submit a PR!
78 |
79 | ## Contributors
80 |
81 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
82 |
83 |
84 |
85 |
95 |
96 |
97 |
98 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
99 |
100 | ## License
101 |
102 | [MIT](./LICENSE)
103 |
--------------------------------------------------------------------------------
/exercises/Sort/Complete/SortComplete.php:
--------------------------------------------------------------------------------
1 | $input[$i + 1]) {
36 | self::swap($input[$i], $input[$i + 1]);
37 | $noSwap = true;
38 | }
39 | }
40 | }
41 |
42 | return $input;
43 | }
44 |
45 | /**
46 | * @param mixed[] $input
47 | *
48 | * @return mixed[]
49 | */
50 | public static function selection(array $input): array
51 | {
52 | for ($i = 0, $min = $i, $length = count($input); $i < $length; $min = ++$i) {
53 | for ($j = $i + 1; $j < $length; ++$j) {
54 | if ($input[$j] < $input[$min]) {
55 | $min = $j;
56 | }
57 | }
58 |
59 | if ($i !== $min) {
60 | self::swap($input[$i], $input[$min]);
61 | }
62 | }
63 |
64 | return $input;
65 | }
66 |
67 | /**
68 | * @param mixed[] $input
69 | *
70 | * @return mixed[]
71 | */
72 | public static function insertion(array $input): array
73 | {
74 | foreach ($input as $i => $value) {
75 | for ($j = $i; $j > 0 && $input[$j - 1] > $value; --$j) {
76 | $input[$j] = $input[$j - 1];
77 | }
78 | $input[$j] = $value;
79 | }
80 |
81 | return $input;
82 | }
83 |
84 | /**
85 | * @param mixed[] $input
86 | *
87 | * @return mixed[]
88 | */
89 | public static function merge(array $input): array
90 | {
91 | $length = count($input);
92 | if ($length <= 1) {
93 | return $input;
94 | }
95 |
96 | $middle = intdiv($length, 2);
97 | $left = array_slice($input, 0, $middle);
98 | $right = array_slice($input, $middle);
99 |
100 | $left = self::merge($left);
101 | $right = self::merge($right);
102 |
103 | return self::merger($left, $right);
104 | }
105 |
106 | /**
107 | * @param mixed[] $input
108 | *
109 | * @return mixed[]
110 | */
111 | public static function quick1(
112 | array &$input,
113 | ?int $left = 0,
114 | ?int $right = null
115 | ): array {
116 | $right = $right ?? count($input) - 1;
117 |
118 | if ($right - $left < 1) {
119 | return [];
120 | }
121 |
122 | $pivotIndex = self::pivot($input, $left, $right);
123 |
124 | self::quick1($input, $left, $pivotIndex - 1);
125 | self::quick1($input, $pivotIndex + 1, $right);
126 |
127 | return $input;
128 | }
129 |
130 | /**
131 | * @param mixed[] $input
132 | *
133 | * @return mixed[]
134 | */
135 | public static function quick2(
136 | array &$input,
137 | ?int $left = 0,
138 | ?int $right = null
139 | ): void {
140 | $pivot = $left;
141 | $right = $right ?? count($input) - 1;
142 |
143 | if ($right - $left < 1) {
144 | return;
145 | }
146 |
147 | // Avoid bad performance when sorting an already sorted array
148 | self::swap($input[intdiv($left + $right, 2)], $input[$right]);
149 |
150 | for ($i = $left; $i < $right; ++$i) {
151 | if ($input[$i] < $input[$right]) {
152 | self::swap($input[$i], $input[$pivot]);
153 | ++$pivot;
154 | }
155 | }
156 |
157 | self::swap($input[$pivot], $input[$right]);
158 |
159 | self::quick2($input, $left, $pivot - 1);
160 | self::quick2($input, $pivot + 1, $right);
161 | }
162 |
163 | /**
164 | * Only for unsigned integers from 0 to n.
165 | *
166 | * @param mixed[] $input
167 | *
168 | * @return mixed[]
169 | */
170 | public static function radix(array $input): array
171 | {
172 | $digitsMax = self::mostDigits($input);
173 |
174 | foreach (range(0, $digitsMax) as $i) {
175 | $bucket = array_fill(0, 10, []);
176 | foreach ($input as $item) {
177 | $digit = self::getDigit($item, $i);
178 | $bucket[$digit][] = $item;
179 | }
180 | $input = array_merge(...$bucket);
181 | }
182 |
183 | return $input;
184 | }
185 |
186 | /** @param int[] $input */
187 | private static function mostDigits(array $input): int
188 | {
189 | $maxDigits = 0;
190 | foreach ($input as $iValue) {
191 | $maxDigits = max($maxDigits, self::digitCount($iValue));
192 | }
193 |
194 | return $maxDigits;
195 | }
196 |
197 | private static function digitCount(int $value): int
198 | {
199 | if ($value === 0) {
200 | return 1;
201 | }
202 |
203 | return (int) floor(log10(abs($value))) + 1;
204 | }
205 |
206 | private static function getDigit(int $value, int $position): int
207 | {
208 | return floor(abs($value) / 10 ** $position) % 10;
209 | }
210 |
211 | /**
212 | * @param mixed $x
213 | * @param mixed $y
214 | */
215 | private static function swap(&$x, &$y): void
216 | {
217 | $tmp = $x;
218 | $x = $y;
219 | $y = $tmp;
220 | }
221 |
222 | /**
223 | * Helper method for Merge sort.
224 | *
225 | * @param mixed[] $left
226 | * @param mixed[] $right
227 | *
228 | * @return mixed[]
229 | */
230 | private static function merger(array $left, array $right): array
231 | {
232 | $results = [];
233 |
234 | while (count($left) && count($right)) {
235 | if ($left[0] < $right[0]) {
236 | $results[] = array_shift($left);
237 | } else {
238 | $results[] = array_shift($right);
239 | }
240 | }
241 |
242 | return array_merge($results, $left, $right);
243 | }
244 |
245 | /** @param mixed[] $input */
246 | private static function pivot(
247 | array &$input,
248 | ?int $start = 0,
249 | ?int $end = null
250 | ): int {
251 | $end = $end ?? count($input) - 1;
252 | $pivot = $input[$start];
253 | $swapIndex = $start;
254 |
255 | for ($i = $start + 1; $i <= $end; ++$i) {
256 | if ($input[$i] < $pivot) {
257 | self::swap($input[++$swapIndex], $input[$i]);
258 | }
259 | }
260 |
261 | self::swap($input[$start], $input[$swapIndex]);
262 |
263 | return $swapIndex;
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/tests/LinkedList/LinkedListTest.php:
--------------------------------------------------------------------------------
1 | list = new LinkedList();
19 | }
20 |
21 | public function testInsertFirst(): void
22 | {
23 | self::markTestSkipped();
24 | $this->list->insertFirst(0);
25 | $this->list->insertFirst(1);
26 | $this->list->insertFirst(2);
27 |
28 | self::assertSame(2, $this->list->head->data);
29 | self::assertSame(1, $this->list->head->next->data);
30 | self::assertSame(0, $this->list->head->next->next->data);
31 | }
32 |
33 | public function testInsertLast(): void
34 | {
35 | self::markTestSkipped();
36 | $this->list->insertLast(0);
37 | $this->list->insertLast(1);
38 | $this->list->insertLast(2);
39 |
40 | self::assertSame(0, $this->list->head->data);
41 | self::assertSame(1, $this->list->head->next->data);
42 | self::assertSame(2, $this->list->head->next->next->data);
43 | }
44 |
45 | public function testSize(): void
46 | {
47 | self::markTestSkipped();
48 | $this->list->head = new Node(0, new Node(1));
49 |
50 | self::assertSame(2, $this->list->size());
51 | }
52 |
53 | public function testGetFirst(): void
54 | {
55 | self::markTestSkipped();
56 | $this->list->head = new Node(0, new Node(1));
57 |
58 | $first = $this->list->getFirst();
59 | self::assertSame(0, $first->data);
60 | self::assertInstanceOf(Node::class, $first->next);
61 | self::assertSame(1, $first->next->data);
62 | }
63 |
64 | public function testGetLast(): void
65 | {
66 | self::markTestSkipped();
67 | $this->list->head = new Node(0, new Node(1));
68 |
69 | $last = $this->list->getLast();
70 | self::assertSame(1, $last->data);
71 | self::assertNull($last->next);
72 | }
73 |
74 | public function testClear(): void
75 | {
76 | self::markTestSkipped();
77 | $this->list->head = new Node(0, new Node(1));
78 |
79 | $this->list->clear();
80 |
81 | $last = $this->list->head;
82 | self::assertNull($last);
83 | }
84 |
85 | public function testRemoveFirst(): void
86 | {
87 | self::markTestSkipped();
88 | $this->list->head = new Node(0, new Node(1, new Node(2)));
89 |
90 | $this->list->removeFirst();
91 |
92 | $first = $this->list->head;
93 | self::assertSame(1, $first->data);
94 | self::assertInstanceOf(Node::class, $first->next);
95 | self::assertSame(2, $first->next->data);
96 | }
97 |
98 | public function testRemoveFirstEmpty(): void
99 | {
100 | self::markTestSkipped();
101 | $this->list->removeFirst();
102 | self::assertTrue(true);
103 | }
104 |
105 | public function testRemoveLast(): void
106 | {
107 | self::markTestSkipped();
108 | $this->list->head = new Node(0, new Node(1, new Node(2)));
109 |
110 | $this->list->removeLast();
111 |
112 | $last = $this->list->head->next;
113 | self::assertSame(1, $last->data);
114 | self::assertNull($last->next);
115 | }
116 |
117 | public function testRemoveLastEmpty(): void
118 | {
119 | self::markTestSkipped();
120 | $this->list->removeLast();
121 | self::assertTrue(true);
122 | }
123 |
124 | public function testGetAt(): void
125 | {
126 | self::markTestSkipped();
127 | $this->list->head = new Node(0, new Node(1, new Node(2)));
128 |
129 | $first = $this->list->getAt(0);
130 | self::assertSame(0, $first->data);
131 |
132 | $last = $this->list->getAt(2);
133 | self::assertSame(2, $last->data);
134 | }
135 |
136 | public function testGetAtEmpty(): void
137 | {
138 | self::markTestSkipped();
139 | $first = $this->list->getAt(0);
140 | self::assertNull($first);
141 |
142 | $second = $this->list->getAt(1);
143 | self::assertNull($second);
144 | }
145 |
146 | public function testRemoveAtEmpty(): void
147 | {
148 | self::markTestSkipped();
149 | $this->list->removeAt(0);
150 | $this->list->removeAt(1);
151 | $this->list->removeAt(2);
152 | self::assertTrue(true);
153 | }
154 |
155 | public function testRemoveAtOutOfBound(): void
156 | {
157 | self::markTestSkipped();
158 | $this->list->head = new Node(0);
159 |
160 | $this->list->removeAt(1);
161 |
162 | $first = $this->list->head;
163 | self::assertNull($first);
164 | }
165 |
166 | public function testRemoveAtFirst(): void
167 | {
168 | self::markTestSkipped();
169 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
170 |
171 | $first = $this->list->head;
172 | self::assertSame(0, $first->data);
173 |
174 | $this->list->removeAt(0);
175 |
176 | $first = $this->list->head;
177 | self::assertSame(1, $first->data);
178 | }
179 |
180 | public function testRemoveAtIndex(): void
181 | {
182 | self::markTestSkipped();
183 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
184 |
185 | $first = $this->list->head->next;
186 | self::assertSame(1, $first->data);
187 |
188 | $this->list->removeAt(0);
189 |
190 | $first = $this->list->head->next;
191 | self::assertSame(2, $first->data);
192 | }
193 |
194 | public function testRemoveAtLast(): void
195 | {
196 | self::markTestSkipped();
197 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
198 |
199 | $first = $this->list->head->next->next->next;
200 | self::assertSame(3, $first->data);
201 |
202 | $this->list->removeAt(3);
203 |
204 | $first = $this->list->head->next->next;
205 | self::assertSame(2, $first->data);
206 | }
207 |
208 | public function testInsertAtEmpty(): void
209 | {
210 | self::markTestSkipped();
211 | $this->list->insertAt('a', 0);
212 |
213 | self::assertSame('a', $this->list->head->data);
214 | }
215 |
216 | public function testInsertAtNegativeOutOfBound(): void
217 | {
218 | self::markTestSkipped();
219 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
220 |
221 | $this->list->insertAt('a', -10);
222 |
223 | self::assertSame('a', $this->list->head->data);
224 | self::assertSame(0, $this->list->head->next->data);
225 | }
226 |
227 | public function testInsertAt0(): void
228 | {
229 | self::markTestSkipped();
230 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
231 |
232 | $this->list->insertAt('a', 0);
233 |
234 | self::assertSame('a', $this->list->head->data);
235 | self::assertSame(0, $this->list->head->next->data);
236 | }
237 |
238 | public function testInsertAtMiddle(): void
239 | {
240 | self::markTestSkipped();
241 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
242 |
243 | $this->list->insertAt('a', 2);
244 |
245 | self::assertSame(0, $this->list->head->data);
246 | self::assertSame(1, $this->list->head->next->data);
247 | self::assertSame('a', $this->list->head->next->next->data);
248 | self::assertSame(2, $this->list->head->next->next->next->data);
249 | self::assertSame(3, $this->list->head->next->next->next->next->data);
250 | }
251 |
252 | public function testInsertAtLast(): void
253 | {
254 | self::markTestSkipped();
255 | $this->list->head = new Node(0, new Node(1));
256 |
257 | $this->list->insertAt('hi', 2);
258 |
259 | self::assertSame(0, $this->list->head->data);
260 | self::assertSame(1, $this->list->head->next->data);
261 | self::assertSame('hi', $this->list->head->next->next->data);
262 | }
263 |
264 | public function testInsertAtOutOfBound(): void
265 | {
266 | self::markTestSkipped();
267 | $this->list->head = new Node(0, new Node(1));
268 |
269 | $this->list->insertAt('hi', 20);
270 |
271 | self::assertSame(0, $this->list->head->data);
272 | self::assertSame(1, $this->list->head->next->data);
273 | self::assertSame('hi', $this->list->head->next->next->data);
274 | }
275 |
276 | public function testForEach(): void
277 | {
278 | self::markTestSkipped();
279 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
280 |
281 | $keys = [];
282 | $this->list->forEach(static function (&$data, $index) use (&$keys): void {
283 | $keys[] = $index;
284 | $data *= 10;
285 | });
286 |
287 | self::assertSame([0, 1, 2, 3], $keys);
288 | self::assertSame(0, $this->list->head->data);
289 | self::assertSame(10, $this->list->head->next->data);
290 | self::assertSame(20, $this->list->head->next->next->data);
291 | self::assertSame(30, $this->list->head->next->next->next->data);
292 | }
293 |
294 | public function testForEachAs(): void
295 | {
296 | self::markTestSkipped();
297 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
298 |
299 | $keys = [];
300 | foreach ($this->list as $key => $item) {
301 | $keys[] = $key;
302 | $item->data *= 10;
303 | }
304 |
305 | self::assertSame([0, 1, 2, 3], $keys);
306 | self::assertSame(0, $this->list->head->data);
307 | self::assertSame(10, $this->list->head->next->data);
308 | self::assertSame(20, $this->list->head->next->next->data);
309 | self::assertSame(30, $this->list->head->next->next->next->data);
310 | }
311 |
312 | public function testForEachAsEmpty(): void
313 | {
314 | self::markTestSkipped();
315 | foreach ($this->list as $item) {
316 | $item->data *= 10;
317 | }
318 | self::assertTrue(true);
319 | }
320 |
321 | public function testMidpointEmpty(): void
322 | {
323 | self::markTestSkipped();
324 | $midpoint = LinkedList::midpoint($this->list);
325 |
326 | self::assertNull($midpoint);
327 | }
328 |
329 | public function testMidpointOne(): void
330 | {
331 | self::markTestSkipped();
332 | $this->list->head = new Node(0);
333 |
334 | $midpoint = LinkedList::midpoint($this->list);
335 |
336 | self::assertSame(0, $midpoint->data);
337 | }
338 |
339 | public function testMidpointTwo(): void
340 | {
341 | self::markTestSkipped();
342 | $this->list->head = new Node(0, new Node(1));
343 |
344 | $midpoint = LinkedList::midpoint($this->list);
345 |
346 | self::assertSame(0, $midpoint->data);
347 | }
348 |
349 | public function testMidpointOdd(): void
350 | {
351 | self::markTestSkipped();
352 | $this->list->head = new Node(0, new Node(1, new Node(2)));
353 |
354 | $midpoint = LinkedList::midpoint($this->list);
355 |
356 | self::assertSame(1, $midpoint->data);
357 | }
358 |
359 | public function testMidpointEven(): void
360 | {
361 | self::markTestSkipped();
362 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
363 |
364 | $midpoint = LinkedList::midpoint($this->list);
365 |
366 | self::assertSame(1, $midpoint->data);
367 | }
368 |
369 | public function testCircularEmpty(): void
370 | {
371 | self::markTestSkipped();
372 | self::assertFalse(LinkedList::circular($this->list));
373 | }
374 |
375 | public function testCircular(): void
376 | {
377 | self::markTestSkipped();
378 | $list = new LinkedList();
379 | $a = new Node('a');
380 | $b = new Node('b');
381 | $c = new Node('c');
382 |
383 | $list->head = $a;
384 | $a->next = $b;
385 | $b->next = $c;
386 | $c->next = $b;
387 |
388 | self::assertTrue(LinkedList::circular($list));
389 | }
390 |
391 | public function testCircularHead(): void
392 | {
393 | self::markTestSkipped();
394 | $list = new LinkedList();
395 | $a = new Node('a');
396 | $b = new Node('b');
397 | $c = new Node('c');
398 |
399 | $list->head = $a;
400 | $a->next = $b;
401 | $b->next = $c;
402 | $c->next = $a;
403 |
404 | self::assertTrue(LinkedList::circular($list));
405 | }
406 |
407 | public function testNonCircular(): void
408 | {
409 | self::markTestSkipped();
410 | $list = new LinkedList();
411 | $a = new Node('a');
412 | $b = new Node('b');
413 | $c = new Node('c');
414 |
415 | $list->head = $a;
416 | $a->next = $b;
417 | $b->next = $c;
418 | $c->next = null;
419 |
420 | self::assertFalse(LinkedList::circular($list));
421 | }
422 |
423 | public function testFromLastEmpty(): void
424 | {
425 | self::markTestSkipped();
426 | self::assertNull(LinkedList::fromLast($this->list, 2));
427 | }
428 |
429 | public function testFromLast(): void
430 | {
431 | self::markTestSkipped();
432 | $this->list->head = new Node(0, new Node(1, new Node(2, new Node(3))));
433 |
434 | $fromLast = LinkedList::fromLast($this->list, 1);
435 |
436 | self::assertSame(2, $fromLast->data);
437 | }
438 | }
439 |
--------------------------------------------------------------------------------