Skip to content

Implemented Disjoint Set (Union-Find) Data Structure #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
* [Speedconversion](./Conversions/SpeedConversion.php)

## Datastructures
* Disjointsets
* [Disjointset](./DataStructures/DisjointSets/DisjointSet.php)
* [Disjointsetnode](./DataStructures/DisjointSets/DisjointSetNode.php)
* [Doublylinkedlist](./DataStructures/DoublyLinkedList.php)
* [Node](./DataStructures/Node.php)
* [Queue](./DataStructures/Queue.php)
Expand Down Expand Up @@ -111,6 +114,7 @@
* Conversions
* [Conversionstest](./tests/Conversions/ConversionsTest.php)
* Datastructures
* [Disjointsettest](./tests/DataStructures/DisjointSetTest.php)
* [Doublylinkedlisttest](./tests/DataStructures/DoublyLinkedListTest.php)
* [Queuetest](./tests/DataStructures/QueueTest.php)
* [Singlylinkedlisttest](./tests/DataStructures/SinglyLinkedListTest.php)
Expand Down
41 changes: 41 additions & 0 deletions DataStructures/DisjointSets/DisjointSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace DataStructures\DisjointSets;

class DisjointSet
{
/**
* Finds the representative of the set that contains the node.
*/
public function findSet(DisjointSetNode $node): DisjointSetNode
{
if ($node !== $node->parent) {
// Path compression: make the parent point directly to the root
$node->parent = $this->findSet($node->parent);
}
return $node->parent;
}

/**
* Unites the sets that contain x and y.
*/
public function unionSet(DisjointSetNode $nodeX, DisjointSetNode $nodeY): void
{
$rootX = $this->findSet($nodeX);
$rootY = $this->findSet($nodeY);

if ($rootX === $rootY) {
return; // They are already in the same set
}

// Union by rank: attach the smaller tree under the larger tree
if ($rootX->rank > $rootY->rank) {
$rootY->parent = $rootX;
} else {
$rootX->parent = $rootY;
if ($rootX->rank === $rootY->rank) {
$rootY->rank += 1;
}
}
}
}
21 changes: 21 additions & 0 deletions DataStructures/DisjointSets/DisjointSetNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace DataStructures\DisjointSets;

class DisjointSetNode
{
/**
* @var int|string|float|null
*/
// PHP7.4: Defined with annotations
public $data; # replace with type hint "mixed" as of PHP 8.0^.
public int $rank;
public DisjointSetNode $parent;

public function __construct($data = null)
{
$this->data = $data;
$this->rank = 0;
$this->parent = $this; // Initialize parent to itself
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name" : "thealgorithms/php",
"name": "thealgorithms/php",
"description": "All Algorithms implemented in PHP",
"config": {
"platform": {
Expand All @@ -19,3 +19,4 @@
"test": "vendor/bin/phpunit tests"
}
}

87 changes: 87 additions & 0 deletions tests/DataStructures/DisjointSetTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

namespace DataStructures;

require_once __DIR__ . '/../../DataStructures/DisjointSets/DisjointSet.php';
require_once __DIR__ . '/../../DataStructures/DisjointSets/DisjointSetNode.php';

use DataStructures\DisjointSets\DisjointSet;
use DataStructures\DisjointSets\DisjointSetNode;
use PHPUnit\Framework\TestCase;

class DisjointSetTest extends TestCase
{
private DisjointSet $ds;
private array $nodes;

protected function setUp(): void
{
$this->ds = new DisjointSet();
$this->nodes = [];

// Create 20 nodes
for ($i = 0; $i < 20; $i++) {
$this->nodes[$i] = new DisjointSetNode($i);
}

// Perform union operations to form several disjoint sets
$this->ds->unionSet($this->nodes[0], $this->nodes[1]);
$this->ds->unionSet($this->nodes[1], $this->nodes[2]);

$this->ds->unionSet($this->nodes[3], $this->nodes[4]);
$this->ds->unionSet($this->nodes[4], $this->nodes[5]);

$this->ds->unionSet($this->nodes[6], $this->nodes[7]);
$this->ds->unionSet($this->nodes[7], $this->nodes[8]);

$this->ds->unionSet($this->nodes[9], $this->nodes[10]);
$this->ds->unionSet($this->nodes[10], $this->nodes[11]);

$this->ds->unionSet($this->nodes[12], $this->nodes[13]);
$this->ds->unionSet($this->nodes[13], $this->nodes[14]);

$this->ds->unionSet($this->nodes[15], $this->nodes[16]);
$this->ds->unionSet($this->nodes[16], $this->nodes[17]);

$this->ds->unionSet($this->nodes[18], $this->nodes[19]);
}

public function testFindSet(): void
{
// Nodes in the same sets should have the same root
for ($i = 0; $i < 6; $i++) {
for ($j = 0; $j < 6; $j++) {
$setI = $this->ds->findSet($this->nodes[$i]);
$setJ = $this->ds->findSet($this->nodes[$j]);

if ($this->inSameSet($i, $j)) {
$this->assertSame($setI, $setJ, "Nodes $i and $j should be in the same set");
} else {
$this->assertNotSame($setI, $setJ, "Nodes $i and $j should be in different sets");
}
}
}
}

private function inSameSet(int $i, int $j): bool
{
// Define which nodes should be in the same set based on union operations
$sets = [
[0, 1, 2], // Set A
[3, 4, 5], // Set B
[6, 7, 8], // Set C
[9, 10, 11], // Set D
[12, 13, 14], // Set E
[15, 16, 17], // Set F
[18, 19] // Set G
];

foreach ($sets as $set) {
if (in_array($i, $set) && in_array($j, $set)) {
return true;
}
}

return false;
}
}
Loading