Skip to content

Commit 2091a82

Browse files
Gabenoobkassane
andauthored
Add Heap data structure implementation with tests (#34)
* Add Heap data structure implementation with tests * Add tests to vlidate the size of heap. change the heap initialization method to .empty --------- Co-authored-by: Matheus C. França <matheus-catarino@hotmail.com>
1 parent ffd44fb commit 2091a82

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Every project is managed by the `build.zig` file.
1717
│ └── ThreadPool.zig
1818
├── dataStructures
1919
│ ├── doublyLinkedList.zig
20+
│ ├── heap.zig
2021
│ ├── linkedList.zig
2122
│ ├── lruCache.zig
2223
│ ├── queue.zig

build.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ pub fn build(b: *std.Build) void {
8181
});
8282

8383
// Data Structures algorithms
84+
if (std.mem.eql(u8, op, "ds/heap"))
85+
buildAlgorithm(b, .{
86+
.optimize = optimize,
87+
.target = target,
88+
.name = "heap.zig",
89+
.category = "dataStructures",
90+
});
8491
if (std.mem.eql(u8, op, "ds/trie"))
8592
buildAlgorithm(b, .{
8693
.optimize = optimize,

dataStructures/heap.zig

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
const std = @import("std");
2+
const testing = std.testing;
3+
4+
/// Returns a Heap type.
5+
/// Arguments:
6+
/// T: the type of the elements
7+
/// compare: function that returns true if a should be higher in the heap than b (e.g. for MinHeap, a < b)
8+
pub fn Heap(comptime T: type, comptime compare: fn (a: T, b: T) bool) type {
9+
return struct {
10+
const Self = @This();
11+
12+
items: std.ArrayList(T),
13+
allocator: std.mem.Allocator,
14+
15+
/// Initialize the heap with an allocator
16+
pub fn init(allocator: std.mem.Allocator) Self {
17+
return Self{
18+
.items = .empty,
19+
.allocator = allocator,
20+
};
21+
}
22+
23+
/// Free the memory used by the heap
24+
pub fn deinit(self: *Self) void {
25+
self.items.deinit(self.allocator);
26+
}
27+
28+
/// Insert an element into the heap
29+
/// Runs in O(log n)
30+
pub fn insert(self: *Self, item: T) !void {
31+
try self.items.append(self.allocator, item);
32+
self.siftUp(self.items.items.len - 1);
33+
}
34+
35+
/// Peek at the top element of the heap (min/max depending on compare)
36+
/// Runs in O(1)
37+
pub fn peek(self: Self) ?T {
38+
if (self.items.items.len == 0) return null;
39+
return self.items.items[0];
40+
}
41+
42+
/// Extract the top element from the heap
43+
/// Runs in O(log n)
44+
pub fn extract(self: *Self) ?T {
45+
if (self.items.items.len == 0) return null;
46+
47+
const root = self.items.items[0];
48+
const last = self.items.pop().?;
49+
50+
if (self.items.items.len > 0) {
51+
self.items.items[0] = last;
52+
self.siftDown(0);
53+
}
54+
55+
return root;
56+
}
57+
58+
/// Get the current number of elements
59+
pub fn size(self: Self) usize {
60+
return self.items.items.len;
61+
}
62+
63+
fn siftUp(self: *Self, start_index: usize) void {
64+
var index = start_index;
65+
const items = self.items.items;
66+
67+
while (index > 0) {
68+
const parent_idx = (index - 1) / 2;
69+
if (!compare(items[index], items[parent_idx])) break;
70+
71+
std.mem.swap(T, &items[index], &items[parent_idx]);
72+
index = parent_idx;
73+
}
74+
}
75+
76+
fn siftDown(self: *Self, start_index: usize) void {
77+
var index = start_index;
78+
const items = self.items.items;
79+
const len = items.len;
80+
81+
while (true) {
82+
const left_child = 2 * index + 1;
83+
const right_child = 2 * index + 2;
84+
var swap_idx = index;
85+
86+
if (left_child < len and compare(items[left_child], items[swap_idx])) {
87+
swap_idx = left_child;
88+
}
89+
90+
if (right_child < len and compare(items[right_child], items[swap_idx])) {
91+
swap_idx = right_child;
92+
}
93+
94+
if (swap_idx == index) break;
95+
96+
std.mem.swap(T, &items[index], &items[swap_idx]);
97+
index = swap_idx;
98+
}
99+
}
100+
};
101+
}
102+
103+
fn lessThan(a: i32, b: i32) bool {
104+
return a < b;
105+
}
106+
107+
fn greaterThan(a: i32, b: i32) bool {
108+
return a > b;
109+
}
110+
111+
test "MinHeap operations" {
112+
const MinHeap = Heap(i32, lessThan);
113+
var heap = MinHeap.init(testing.allocator);
114+
defer heap.deinit();
115+
116+
try heap.insert(5);
117+
try heap.insert(3);
118+
try heap.insert(10);
119+
try heap.insert(1);
120+
121+
try testing.expect(4 == heap.size());
122+
123+
try testing.expectEqual(@as(?i32, 1), heap.peek());
124+
try testing.expectEqual(@as(?i32, 1), heap.extract());
125+
try testing.expectEqual(@as(?i32, 3), heap.extract());
126+
try testing.expectEqual(@as(?i32, 5), heap.extract());
127+
try testing.expectEqual(@as(?i32, 10), heap.extract());
128+
try testing.expectEqual(@as(?i32, null), heap.extract());
129+
}
130+
131+
test "MaxHeap operations" {
132+
const MaxHeap = Heap(i32, greaterThan);
133+
var heap = MaxHeap.init(testing.allocator);
134+
defer heap.deinit();
135+
136+
try heap.insert(5);
137+
try heap.insert(3);
138+
139+
try testing.expect(2 == heap.size());
140+
141+
try heap.insert(10);
142+
try heap.insert(1);
143+
144+
try testing.expect(4 == heap.size());
145+
146+
try testing.expectEqual(@as(?i32, 10), heap.peek());
147+
try testing.expectEqual(@as(?i32, 10), heap.extract());
148+
try testing.expectEqual(@as(?i32, 5), heap.extract());
149+
try testing.expectEqual(@as(?i32, 3), heap.extract());
150+
try testing.expectEqual(@as(?i32, 1), heap.extract());
151+
try testing.expectEqual(@as(?i32, null), heap.extract());
152+
}

runall.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub fn main() !void {
2020
try runTest(allocator, "ds/doublylinkedlist");
2121
try runTest(allocator, "ds/lrucache");
2222
try runTest(allocator, "ds/stack");
23+
try runTest(allocator, "ds/heap");
2324
try runTest(allocator, "ds/queue");
2425

2526
// Dynamic Programming

0 commit comments

Comments
 (0)