Skip to content

Commit 9c0c0d1

Browse files
socpitekassane
authored andcommitted
added trie
1 parent 5b9cf67 commit 9c0c0d1

File tree

4 files changed

+212
-0
lines changed

4 files changed

+212
-0
lines changed

build.zig

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

6262
// Data Structures algorithms
63+
if (std.mem.eql(u8, op, "ds/trie"))
64+
buildAlgorithm(b, .{
65+
.optimize = optimize,
66+
.target = target,
67+
.name = "trie.zig",
68+
.category = "dataStructures",
69+
});
6370
if (std.mem.eql(u8, op, "ds/linkedlist"))
6471
buildAlgorithm(b, .{
6572
.optimize = optimize,

dataStructures/trie.zig

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
const std = @import("std");
2+
const Allocator = std.mem.Allocator;
3+
const HashMap = std.AutoArrayHashMap;
4+
5+
const TrieError = error{
6+
InvalidNode,
7+
};
8+
9+
pub fn TrieNode(comptime T: type) type {
10+
return struct {
11+
const Self = @This();
12+
node_data: T,
13+
children: HashMap(u8, *Self),
14+
parent: ?*Self,
15+
16+
fn init(node_data: T, allocator: Allocator, parent: ?*Self) TrieNode(T) {
17+
return TrieNode(T){
18+
.node_data = node_data,
19+
.children = HashMap(u8, *Self).init(allocator),
20+
.parent = parent,
21+
};
22+
}
23+
};
24+
}
25+
26+
/// Interface to traverse the trie
27+
fn TrieIterator(comptime T: type) type {
28+
return struct {
29+
const Self = @This();
30+
const NodeType = TrieNode(T);
31+
/// Can be dereferenced to modify node data
32+
node_at_iterator: *NodeType,
33+
fn init(node_ptr: *NodeType) Self {
34+
return Self{
35+
.node_at_iterator = node_ptr,
36+
};
37+
}
38+
39+
/// Returns an optional iterator pointing to the child following the `char` edge
40+
pub fn go_to_child(self: Self, char: u8) ?Self {
41+
if (self.node_at_iterator.children.get(char)) |new_ptr| {
42+
return Self{
43+
.node_at_iterator = new_ptr,
44+
};
45+
} else {
46+
return null;
47+
}
48+
}
49+
50+
/// Returns an optional iterator pointing to the parent
51+
pub fn go_to_parent(self: Self) ?Self {
52+
if (self.node_at_iterator.parent) |new_ptr| {
53+
return Self{
54+
.node_at_iterator = new_ptr,
55+
};
56+
} else {
57+
return null;
58+
}
59+
}
60+
};
61+
}
62+
63+
pub fn Trie(comptime T: type) type {
64+
return struct {
65+
const Self = @This();
66+
const NodeType = TrieNode(T);
67+
const IteratorType = TrieIterator(T);
68+
trie_root: *NodeType,
69+
allocator: Allocator,
70+
71+
/// Allocate a new node and return its pointer
72+
fn new_node(self: Self, node_data: T, parent: ?*NodeType) !*NodeType {
73+
const node_ptr = try self.allocator.create(NodeType);
74+
node_ptr.* = NodeType.init(
75+
node_data,
76+
self.allocator,
77+
parent,
78+
);
79+
return node_ptr;
80+
}
81+
82+
pub fn init(root_data: T, allocator: Allocator) !Self {
83+
const node_ptr = try allocator.create(NodeType);
84+
node_ptr.* = NodeType.init(root_data, allocator, null);
85+
return Self{
86+
.trie_root = node_ptr,
87+
.allocator = allocator,
88+
};
89+
}
90+
91+
/// Returns an iterator pointing to the root
92+
pub fn get_root_iterator(self: Self) IteratorType {
93+
return IteratorType.init(self.trie_root);
94+
}
95+
96+
/// Add a string to the trie, assigning newly created node's data with `new_value`
97+
pub fn add_string(self: Self, new_string: []const u8, new_value: T) !IteratorType {
98+
var iterator = self.get_root_iterator();
99+
for (new_string) |char| {
100+
if (iterator.go_to_child(char)) |new_iterator| {
101+
iterator = new_iterator;
102+
} else {
103+
const node = try self.new_node(
104+
new_value,
105+
iterator.node_at_iterator,
106+
);
107+
try iterator.node_at_iterator.children.put(char, node);
108+
iterator = iterator.go_to_child(char).?;
109+
}
110+
}
111+
return iterator;
112+
}
113+
114+
/// traverse and write u8 to `output_buffer` for each edge traversed in dfs order
115+
pub fn traverse(self: Self, iterator: IteratorType, output_buffer: []u8) usize {
116+
var it = iterator.node_at_iterator.children.iterator();
117+
var write_position: usize = 0;
118+
while (it.next()) |entry| {
119+
output_buffer[write_position] = entry.key_ptr.*;
120+
write_position += 1;
121+
write_position += self.traverse(
122+
IteratorType.init(entry.value_ptr.*),
123+
output_buffer[write_position..output_buffer.len],
124+
);
125+
}
126+
return write_position;
127+
}
128+
129+
/// Apply a function to every node in the trie.
130+
/// If `top_down = true`, apply the function before applying to children, and vice versa.
131+
pub fn apply(
132+
self: Self,
133+
iterator: IteratorType,
134+
func: ?*fn (node: *NodeType) void,
135+
top_down: bool,
136+
) void {
137+
if (func) |f| {
138+
if (top_down) {
139+
f(iterator.node_at_iterator);
140+
} else {
141+
defer f(iterator.node_at_iterator);
142+
}
143+
}
144+
var it = iterator.node_at_iterator.children.iterator();
145+
while (it.next()) |entry| {
146+
self.apply(
147+
IteratorType.init(entry.value_ptr.*),
148+
func,
149+
top_down,
150+
);
151+
}
152+
}
153+
154+
fn recursive_free(self: Self, iterator: IteratorType) void {
155+
var it = iterator.node_at_iterator.children.iterator();
156+
while (it.next()) |entry| {
157+
self.recursive_free(IteratorType.init(entry.value_ptr.*));
158+
}
159+
iterator.node_at_iterator.children.deinit();
160+
self.allocator.destroy(iterator.node_at_iterator);
161+
}
162+
163+
pub fn deinit(self: Self) void {
164+
self.recursive_free(self.get_root_iterator());
165+
}
166+
};
167+
}
168+
169+
test "basic traverse" {
170+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
171+
const debug_allocator = gpa.allocator();
172+
const trie = try Trie(i32).init(0, debug_allocator);
173+
defer trie.deinit();
174+
_ = try trie.add_string("aaa", 0);
175+
_ = try trie.add_string("abb", 0);
176+
_ = try trie.add_string("abc", 0);
177+
// a
178+
// / \
179+
// a b
180+
// / / \
181+
// a b c
182+
const answer = "aaabbc";
183+
var buffer: [6]u8 = undefined;
184+
_ = trie.traverse(trie.get_root_iterator(), buffer[0..6]);
185+
try std.testing.expectEqualSlices(u8, buffer[0..6], answer[0..6]);
186+
}
187+
188+
test "iterator traverse" {
189+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
190+
const debug_allocator = gpa.allocator();
191+
const trie = try Trie(i32).init(0, debug_allocator);
192+
defer trie.deinit();
193+
var it = try trie.add_string("abc", 0); // "abc"
194+
try std.testing.expectEqual(null, it.go_to_child('a'));
195+
it = it.go_to_parent().?; // "ab"
196+
it = it.go_to_parent().?; // "a"
197+
it = it.go_to_parent().?; // ""
198+
try std.testing.expectEqual(null, it.go_to_parent());
199+
const it2 = try trie.add_string("ae", 0); // "ae"
200+
it = it.go_to_child('a').?; // "a"
201+
it = it.go_to_child('e').?; // "ae"
202+
try std.testing.expectEqual(it, it2);
203+
}

runall.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ rem Math
1818
%ZIG_TEST% -Dalgorithm=math/gcd %Args%
1919

2020
rem Data Structures
21+
%ZIG_TEST% -Dalgorithm=ds/trie %Args%
2122
%ZIG_TEST% -Dalgorithm=ds/linkedlist %Args%
2223
%ZIG_TEST% -Dalgorithm=ds/doublylinkedlist %Args%
2324
%ZIG_TEST% -Dalgorithm=ds/lrucache %Args%

runall.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ $ZIG_TEST -Dalgorithm=math/euclidianGCDivisor $Args
1818
$ZIG_TEST -Dalgorithm=math/gcd $Args
1919

2020
# Data Structures
21+
$ZIG_TEST -Dalgorithm=ds/trie $Args
2122
$ZIG_TEST -Dalgorithm=ds/linkedlist $Args
2223
$ZIG_TEST -Dalgorithm=ds/doublylinkedlist $Args
2324
$ZIG_TEST -Dalgorithm=ds/lrucache $Args

0 commit comments

Comments
 (0)