Skip to content

Commit

Permalink
Implemented Segment Tree Data Structure
Browse files Browse the repository at this point in the history
  • Loading branch information
Ramy-Badr-Ahmed committed Sep 22, 2024
1 parent 7d0c07d commit d61d023
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 60 deletions.
1 change: 0 additions & 1 deletion DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
* SegmentTree
* [SegmentTree](./DataStructures/SegmentTree/SegmentTree.php)
* [SegmentTreeNode](./DataStructures/SegmentTree/SegmentTreeNode.php)

* [Singlylinkedlist](./DataStructures/SinglyLinkedList.php)
* [Stack](./DataStructures/Stack.php)
* Trie
Expand Down
100 changes: 50 additions & 50 deletions DataStructures/SegmentTree/SegmentTree.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class SegmentTree

/**
* Initializes the segment tree with the provided array and optional callback for aggregation.
* Default aggregation is Sum
* Default aggregation is Sum.
*
* Example usage:
* $segmentTree = new SegmentTree($array, fn($a, $b) => max($a, $b));
*
* @param array $arr The input array for the segment tree
* @param callable|null $callback Optional callback function for custom aggregation logic
* @throws InvalidArgumentException if the array is empty, contains non-numeric values, or is associative
* @param callable|null $callback Optional callback function for custom aggregation logic.
* @throws InvalidArgumentException if the array is empty, contains non-numeric values, or is associative.
*/
public function __construct(array $arr, callable $callback = null)
{
Expand All @@ -43,23 +43,23 @@ private function isUnsupportedArray(): bool
}

/**
* @return bool True if any element is non-numeric, false otherwise
* @return bool True if any element is non-numeric, false otherwise.
*/
private function isNonNumeric(): bool
{
return !array_reduce($this->currentArray, fn($carry, $item) => $carry && is_numeric($item), true);
}

/**
* @return bool True if the array is associative, false otherwise
* @return bool True if the array is associative, false otherwise.
*/
private function isAssociative(): bool
{
return array_keys($this->currentArray) !== range(0, $this->arraySize - 1);
}

/**
* @return SegmentTreeNode The root node of the segment tree
* @return SegmentTreeNode The root node of the segment tree.
*/
public function getRoot(): SegmentTreeNode
{
Expand All @@ -77,29 +77,29 @@ public function getCurrentArray(): array
/**
* Builds the segment tree recursively.
*
* @param array $arr The input array
* @param int $start The starting index of the segment
* @param int $end The ending index of the segment
* @return SegmentTreeNode The root node of the constructed segment
* @param array $arr The input array.
* @param int $start The starting index of the segment.
* @param int $end The ending index of the segment.
* @return SegmentTreeNode The root node of the constructed segment.
*/
private function buildTree(array $arr, int $start, int $end): SegmentTreeNode
{
// Leaf node
// Leaf node
if ($start == $end) {
return new SegmentTreeNode($start, $end, $arr[$start]);
}

$mid = $start + (int)(($end - $start) / 2);

// Recursively build left and right children
// Recursively build left and right children
$leftChild = $this->buildTree($arr, $start, $mid);
$rightChild = $this->buildTree($arr, $mid + 1, $end);

$node = new SegmentTreeNode($start, $end, $this->callback
? ($this->callback)($leftChild->value, $rightChild->value)
: $leftChild->value + $rightChild->value);

// Link the children to the parent node
// Link the children to the parent node
$node->left = $leftChild;
$node->right = $rightChild;

Expand All @@ -109,10 +109,10 @@ private function buildTree(array $arr, int $start, int $end): SegmentTreeNode
/**
* Queries the aggregated value over a specified range.
*
* @param int $start The starting index of the range
* @param int $end The ending index of the range
* @return int|float The aggregated value for the range
* @throws OutOfBoundsException if the range is invalid
* @param int $start The starting index of the range.
* @param int $end The ending index of the range.
* @return int|float The aggregated value for the range.
* @throws OutOfBoundsException if the range is invalid.
*/
public function query(int $start, int $end)
{
Expand All @@ -126,10 +126,10 @@ public function query(int $start, int $end)
/**
* Recursively queries the segment tree for a specific range.
*
* @param SegmentTreeNode $node The current node
* @param int $start The starting index of the query range
* @param int $end The ending index of the query range
* @return int|float The aggregated value for the range
* @param SegmentTreeNode $node The current node.
* @param int $start The starting index of the query range.
* @param int $end The ending index of the query range.
* @return int|float The aggregated value for the range.
*/
private function queryTree(SegmentTreeNode $node, int $start, int $end)
{
Expand All @@ -139,7 +139,7 @@ private function queryTree(SegmentTreeNode $node, int $start, int $end)

$mid = $node->start + (int)(($node->end - $node->start) / 2);

// Determine which segment of the tree to query
// Determine which segment of the tree to query
if ($end <= $mid) {
return $this->queryTree($node->left, $start, $end); // Query left child
} elseif ($start > $mid) {
Expand All @@ -158,9 +158,9 @@ private function queryTree(SegmentTreeNode $node, int $start, int $end)
/**
* Updates the value at a specified index in the segment tree.
*
* @param int $index The index to update
* @param int|float $value The new value to set
* @throws OutOfBoundsException if the index is out of bounds
* @param int $index The index to update.
* @param int|float $value The new value to set.
* @throws OutOfBoundsException if the index is out of bounds.
*/
public function update(int $index, int $value): void
{
Expand All @@ -176,28 +176,28 @@ public function update(int $index, int $value): void
/**
* Recursively updates the segment tree.
*
* @param SegmentTreeNode $node The current node
* @param int $index The index to update
* @param int|float $value The new value
* @param SegmentTreeNode $node The current node.
* @param int $index The index to update.
* @param int|float $value The new value.
*/
private function updateTree(SegmentTreeNode $node, int $index, $value): void
{
// Leaf node
// Leaf node
if ($node->start == $node->end) {
$node->value = $value;
return;
}

$mid = $node->start + (int)(($node->end - $node->start) / 2);

// Decide whether to go to the left or right child
// Decide whether to go to the left or right child
if ($index <= $mid) {
$this->updateTree($node->left, $index, $value);
} else {
$this->updateTree($node->right, $index, $value);
}

// Recompute the value of the current node after the update
// Recompute the value of the current node after the update
$node->value = $this->callback
? ($this->callback)($node->left->value, $node->right->value)
: $node->left->value + $node->right->value;
Expand All @@ -206,10 +206,10 @@ private function updateTree(SegmentTreeNode $node, int $index, $value): void
/**
* Performs a range update on a specified segment.
*
* @param int $start The starting index of the range
* @param int $end The ending index of the range
* @param int|float $value The value to set for the range
* @throws OutOfBoundsException if the range is invalid
* @param int $start The starting index of the range.
* @param int $end The ending index of the range.
* @param int|float $value The value to set for the range.
* @throws OutOfBoundsException if the range is invalid.
*/
public function rangeUpdate(int $start, int $end, $value): void
{
Expand All @@ -218,29 +218,29 @@ public function rangeUpdate(int $start, int $end, $value): void
}
$this->rangeUpdateTree($this->root, $start, $end, $value);

// Update the original array to reflect the range update
// Update the original array to reflect the range update
$this->currentArray = array_replace($this->currentArray, array_fill_keys(range($start, $end), $value));
}

/**
* Recursively performs a range update in the segment tree.
*
* @param SegmentTreeNode $node The current node
* @param int $start The starting index of the range
* @param int $end The ending index of the range
* @param int|float $value The new value for the range
* @param SegmentTreeNode $node The current node.
* @param int $start The starting index of the range.
* @param int $end The ending index of the range.
* @param int|float $value The new value for the range.
*/
private function rangeUpdateTree(SegmentTreeNode $node, int $start, int $end, $value): void
{
// Leaf node
// Leaf node
if ($node->start == $node->end) {
$node->value = $value;
return;
}

$mid = $node->start + (int)(($node->end - $node->start) / 2);

// Determine which segment of the tree to update (Left, Right, Split respectively)
// Determine which segment of the tree to update (Left, Right, Split respectively)
if ($end <= $mid) {
$this->rangeUpdateTree($node->left, $start, $end, $value); // Entire range is in the left child
} elseif ($start > $mid) {
Expand All @@ -251,7 +251,7 @@ private function rangeUpdateTree(SegmentTreeNode $node, int $start, int $end, $v
$this->rangeUpdateTree($node->right, $mid + 1, $end, $value);
}

// Recompute the value of the current node after the update
// Recompute the value of the current node after the update
$node->value = $this->callback
? ($this->callback)($node->left->value, $node->right->value)
: $node->left->value + $node->right->value;
Expand All @@ -260,7 +260,7 @@ private function rangeUpdateTree(SegmentTreeNode $node, int $start, int $end, $v
/**
* Serializes the segment tree into a JSON string.
*
* @return string The serialized segment tree as a JSON string
* @return string The serialized segment tree as a JSON string.
*/
public function serialize(): string
{
Expand All @@ -270,8 +270,8 @@ public function serialize(): string
/**
* Recursively serializes the segment tree.
*
* @param SegmentTreeNode|null $node The current node
* @return array The serialized representation of the node
* @param SegmentTreeNode|null $node The current node.
* @return array The serialized representation of the node.
*/
private function serializeTree(?SegmentTreeNode $node): array
{
Expand All @@ -290,8 +290,8 @@ private function serializeTree(?SegmentTreeNode $node): array
/**
* Deserializes a JSON string into a SegmentTree object.
*
* @param string $data The JSON string to deserialize
* @return SegmentTree The deserialized segment tree
* @param string $data The JSON string to deserialize.
* @return SegmentTree The deserialized segment tree.
*/
public static function deserialize(string $data): self
{
Expand All @@ -307,8 +307,8 @@ public static function deserialize(string $data): self
/**
* Recursively deserializes a segment tree from an array representation.
*
* @param array $data The serialized data for the node
* @return SegmentTreeNode|null The deserialized node
* @param array $data The serialized data for the node.
* @return SegmentTreeNode|null The deserialized node.
*/
private function deserializeTree(array $data): ?SegmentTreeNode
{
Expand Down
6 changes: 3 additions & 3 deletions DataStructures/SegmentTree/SegmentTreeNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class SegmentTreeNode
public ?SegmentTreeNode $right;

/**
* @param int $start The starting index of the range
* @param int $end The ending index of the range
* @param int|float $value The initial aggregated value for this range (e.g. sum, min, or max)
* @param int $start The starting index of the range.
* @param int $end The ending index of the range.
* @param int|float $value The initial aggregated value for this range (e.g. sum, min, or max).
* calculated using a callback. Defaults to sum.
*/
public function __construct(int $start, int $end, $value)
Expand Down
Loading

0 comments on commit d61d023

Please sign in to comment.