Skip to content

Commit f028cb4

Browse files
committed
max-heap: draft base implementation
1 parent dc62599 commit f028cb4

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
/**
6+
* TODO:
7+
* - optimizations
8+
* - changeset
9+
* - add tests
10+
* - add docs
11+
* - add base impl link here
12+
*
13+
* @dev Library for managing a max heap data structure.
14+
*
15+
* A max heap is a complete binary tree where each node has a value greater than or equal to its children.
16+
* The root node contains the maximum value in the heap.
17+
*
18+
* This library provides functions to insert, update, and remove elements from the max heap, as well as to
19+
* retrieve the maximum element (peek) and check the validity of the heap.
20+
*
21+
* The max heap has the following properties:
22+
*
23+
* - Insertion: O(log n)
24+
* - Deletion of maximum element: O(log n)
25+
* - Retrieval of maximum element (peek): O(1)
26+
* - Update of an element: O(log n)
27+
*
28+
* The max heap is implemented using two mappings:
29+
* - `tree`: Maps the position in the heap to the item ID.
30+
* - `items`: Maps the item ID to its corresponding `Node` struct, which contains the value and heap index.
31+
*
32+
* Example usage:
33+
*
34+
* ```solidity
35+
* contract Example {
36+
* using MaxHeap for MaxHeap.MaxHeap;
37+
*
38+
* MaxHeap.MaxHeap private heap;
39+
*
40+
* function addItem(uint256 itemId, uint256 value) public {
41+
* heap.insert(itemId, value);
42+
* }
43+
*
44+
* function removeMax() public returns (uint256, uint256) {
45+
* return heap.pop();
46+
* }
47+
*
48+
* function getMax() public view returns (uint256, uint256) {
49+
* return heap.peek();
50+
* }
51+
* }
52+
* ```
53+
*/
54+
library MaxHeap {
55+
/**
56+
* @dev The position doesn't have a _parent as it's the root.
57+
*/
58+
error InvalidPositionZero();
59+
60+
struct Node {
61+
uint256 value;
62+
uint256 heapIndex;
63+
}
64+
65+
struct MaxHeap {
66+
mapping(uint256 => uint256) tree;
67+
mapping(uint256 => Node) items;
68+
uint256 size;
69+
}
70+
71+
function _parent(uint256 pos) private pure returns (uint256) {
72+
if (pos == 0) revert InvalidPositionZero();
73+
return (pos - 1) / 2;
74+
}
75+
76+
function _swap(MaxHeap storage heap, uint256 fpos, uint256 spos) private {
77+
(heap.tree[fpos], heap.tree[spos]) = (heap.tree[spos], heap.tree[fpos]);
78+
(heap.items[heap.tree[fpos]].heapIndex, heap.items[heap.tree[spos]].heapIndex) = (fpos, spos);
79+
}
80+
81+
function heapify(MaxHeap storage heap, uint256 pos) internal {
82+
if (pos >= (heap.size / 2) && pos <= heap.size) return;
83+
84+
uint256 left = 2 * pos + 1;
85+
uint256 right = left + 1;
86+
87+
uint256 leftValue = left < heap.size ? heap.items[heap.tree[left]].value : 0;
88+
uint256 rightValue = right < heap.size ? heap.items[heap.tree[right]].value : 0;
89+
uint256 posValue = heap.items[heap.tree[pos]].value;
90+
91+
if (posValue < leftValue || posValue < rightValue) {
92+
if (leftValue > rightValue) {
93+
_swap(heap, pos, left);
94+
heapify(heap, left);
95+
} else {
96+
_swap(heap, pos, right);
97+
heapify(heap, right);
98+
}
99+
}
100+
}
101+
102+
function insert(MaxHeap storage heap, uint256 itemId, uint256 value) internal {
103+
heap.tree[heap.size] = itemId;
104+
heap.items[itemId] = Node({value: value, heapIndex: heap.size});
105+
106+
uint256 current = heap.size;
107+
uint256 parentOfCurrent = _parent(current);
108+
109+
while (current != 0 && heap.items[heap.tree[current]].value > heap.items[heap.tree[parentOfCurrent]].value) {
110+
uint256 parentOfCurrent = _parent(current);
111+
_swap(heap, current, parentOfCurrent);
112+
current = parentOfCurrent;
113+
parentOfCurrent = _parent(current);
114+
}
115+
heap.size++;
116+
}
117+
118+
function update(MaxHeap storage heap, uint256 itemId, uint256 newValue) internal {
119+
// Check that itemId exists in heap
120+
// TODO: update return with revert?
121+
if (heap.items[itemId].heapIndex >= heap.size) return;
122+
123+
uint256 position = heap.items[itemId].heapIndex;
124+
uint256 oldValue = heap.items[itemId].value;
125+
126+
heap.items[itemId].value = newValue;
127+
128+
if (newValue > oldValue) {
129+
while (
130+
position != 0 && heap.items[heap.tree[position]].value > heap.items[heap.tree[_parent(position)]].value
131+
) {
132+
uint256 parentOfPosition = _parent(position);
133+
_swap(heap, position, parentOfPosition);
134+
position = parentOfPosition;
135+
}
136+
} else heapify(heap, position);
137+
}
138+
139+
function pop(MaxHeap storage heap) internal returns (uint256, uint256) {
140+
// TODO: should it revert if empty?
141+
142+
uint256 popped = heap.tree[0];
143+
uint256 returnValue = heap.items[popped].value;
144+
145+
delete heap.items[popped];
146+
147+
heap.tree[0] = heap.tree[--heap.size];
148+
149+
heap.items[heap.tree[0]].heapIndex = 0;
150+
151+
delete heap.tree[heap.size];
152+
153+
heapify(heap, 0);
154+
155+
return (popped, returnValue);
156+
}
157+
158+
function peek(MaxHeap storage heap) internal view returns (uint256, uint256) {
159+
// TODO: should it revert if empty?
160+
return (heap.tree[0], heap.items[heap.tree[0]].value);
161+
}
162+
}

0 commit comments

Comments
 (0)