Skip to content

Implemented trie #22

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jobs:
submodules: recursive
fetch-depth: 0
- uses: mlugg/setup-zig@v1
with:
version: 0.13.0

- name: Testing (Linux & MacOS)
if: (startsWith(matrix.runs-on, 'ubuntu')) || (startsWith(matrix.runs-on, 'macos'))
Expand Down
9 changes: 9 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@
* [Threadpool](https://github.com/TheAlgorithms/Zig/blob/HEAD/concurrency/threads/ThreadPool.zig)

## Datastructures
* [Doublylinkedlist](https://github.com/TheAlgorithms/Zig/blob/HEAD/dataStructures/doublyLinkedList.zig)
* [Linkedlist](https://github.com/TheAlgorithms/Zig/blob/HEAD/dataStructures/linkedList.zig)
* [Lrucache](https://github.com/TheAlgorithms/Zig/blob/HEAD/dataStructures/lruCache.zig)
* [Stack](https://github.com/TheAlgorithms/Zig/blob/HEAD/dataStructures/stack.zig)
* [Trie](https://github.com/TheAlgorithms/Zig/blob/HEAD/dataStructures/trie.zig)

## Dynamicprogramming
* [Coinchange](https://github.com/TheAlgorithms/Zig/blob/HEAD/dynamicProgramming/coinChange.zig)
* [Editdistance](https://github.com/TheAlgorithms/Zig/blob/HEAD/dynamicProgramming/editDistance.zig)
* [Knapsack](https://github.com/TheAlgorithms/Zig/blob/HEAD/dynamicProgramming/knapsack.zig)
* [Longestincreasingsubsequence](https://github.com/TheAlgorithms/Zig/blob/HEAD/dynamicProgramming/longestIncreasingSubsequence.zig)

## Math
* [Ceil](https://github.com/TheAlgorithms/Zig/blob/HEAD/math/ceil.zig)
Expand Down
15 changes: 11 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ pub fn build(b: *std.Build) void {
});

// Data Structures algorithms
if (std.mem.eql(u8, op, "ds/trie"))
build_algorithm(b, .{
.optimize = optimize,
.target = target,
.name = "trie.zig",
.category = "dataStructures",
});
if (std.mem.eql(u8, op, "ds/linkedlist"))
build_algorithm(b, .{
.optimize = optimize,
Expand Down Expand Up @@ -95,28 +102,28 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.target = target,
.name = "coinChange.zig",
.category = "dynamicProgramming"
.category = "dynamicProgramming",
});
if (std.mem.eql(u8, op, "dp/knapsack"))
build_algorithm(b, .{
.optimize = optimize,
.target = target,
.name = "knapsack.zig",
.category = "dynamicProgramming"
.category = "dynamicProgramming",
});
if (std.mem.eql(u8, op, "dp/longestIncreasingSubsequence"))
build_algorithm(b, .{
.optimize = optimize,
.target = target,
.name = "longestIncreasingSubsequence.zig",
.category = "dynamicProgramming"
.category = "dynamicProgramming",
});
if (std.mem.eql(u8, op, "dp/editDistance"))
build_algorithm(b, .{
.optimize = optimize,
.target = target,
.name = "editDistance.zig",
.category = "dynamicProgramming"
.category = "dynamicProgramming",
});

// Math algorithms
Expand Down
203 changes: 203 additions & 0 deletions dataStructures/trie.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const HashMap = std.AutoArrayHashMap;

const TrieError = error{
InvalidNode,
};

pub fn TrieNode(comptime T: type) type {
return struct {
const Self = @This();
node_data: T,
children: HashMap(u8, *Self),
parent: ?*Self,

fn init(node_data: T, allocator: Allocator, parent: ?*Self) TrieNode(T) {
return TrieNode(T){
.node_data = node_data,
.children = HashMap(u8, *Self).init(allocator),
.parent = parent,
};
}
};
}

/// Interface to traverse the trie
fn TrieIterator(comptime T: type) type {
return struct {
const Self = @This();
const NodeType = TrieNode(T);
/// Can be dereferenced to modify node data
node_at_iterator: *NodeType,
fn init(node_ptr: *NodeType) Self {
return Self{
.node_at_iterator = node_ptr,
};
}

/// Returns an optional iterator pointing to the child following the `char` edge
pub fn go_to_child(self: Self, char: u8) ?Self {
if (self.node_at_iterator.children.get(char)) |new_ptr| {
return Self{
.node_at_iterator = new_ptr,
};
} else {
return null;
}
}

/// Returns an optional iterator pointing to the parent
pub fn go_to_parent(self: Self) ?Self {
if (self.node_at_iterator.parent) |new_ptr| {
return Self{
.node_at_iterator = new_ptr,
};
} else {
return null;
}
}
};
}

pub fn Trie(comptime T: type) type {
return struct {
const Self = @This();
const NodeType = TrieNode(T);
const IteratorType = TrieIterator(T);
trie_root: *NodeType,
allocator: Allocator,

/// Allocate a new node and return its pointer
fn new_node(self: Self, node_data: T, parent: ?*NodeType) !*NodeType {
const node_ptr = try self.allocator.create(NodeType);
node_ptr.* = NodeType.init(
node_data,
self.allocator,
parent,
);
return node_ptr;
}

pub fn init(root_data: T, allocator: Allocator) !Self {
const node_ptr = try allocator.create(NodeType);
node_ptr.* = NodeType.init(root_data, allocator, null);
return Self{
.trie_root = node_ptr,
.allocator = allocator,
};
}

/// Returns an iterator pointing to the root
pub fn get_root_iterator(self: Self) IteratorType {
return IteratorType.init(self.trie_root);
}

/// Add a string to the trie, assigning newly created node's data with `new_value`
pub fn add_string(self: Self, new_string: []const u8, new_value: T) !IteratorType {
var iterator = self.get_root_iterator();
for (new_string) |char| {
if (iterator.go_to_child(char)) |new_iterator| {
iterator = new_iterator;
} else {
const node = try self.new_node(
new_value,
iterator.node_at_iterator,
);
try iterator.node_at_iterator.children.put(char, node);
iterator = iterator.go_to_child(char).?;
}
}
return iterator;
}

/// traverse and write u8 to `output_buffer` for each edge traversed in dfs order
pub fn traverse(self: Self, iterator: IteratorType, output_buffer: []u8) usize {
var it = iterator.node_at_iterator.children.iterator();
var write_position: usize = 0;
while (it.next()) |entry| {
output_buffer[write_position] = entry.key_ptr.*;
write_position += 1;
write_position += self.traverse(
IteratorType.init(entry.value_ptr.*),
output_buffer[write_position..output_buffer.len],
);
}
return write_position;
}

/// Apply a function to every node in the trie.
/// If `top_down = true`, apply the function before applying to children, and vice versa.
pub fn apply(
self: Self,
iterator: IteratorType,
func: ?*fn (node: *NodeType) void,
top_down: bool,
) void {
if (func) |f| {
if (top_down) {
f(iterator.node_at_iterator);
} else {
defer f(iterator.node_at_iterator);
}
}
var it = iterator.node_at_iterator.children.iterator();
while (it.next()) |entry| {
self.apply(
IteratorType.init(entry.value_ptr.*),
func,
top_down,
);
}
}

fn recursive_free(self: Self, iterator: IteratorType) void {
var it = iterator.node_at_iterator.children.iterator();
while (it.next()) |entry| {
self.recursive_free(IteratorType.init(entry.value_ptr.*));
}
iterator.node_at_iterator.children.deinit();
self.allocator.destroy(iterator.node_at_iterator);
}

pub fn deinit(self: Self) void {
self.recursive_free(self.get_root_iterator());
}
};
}

test "basic traverse" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const debug_allocator = gpa.allocator();
const trie = try Trie(i32).init(0, debug_allocator);
defer trie.deinit();
_ = try trie.add_string("aaa", 0);
_ = try trie.add_string("abb", 0);
_ = try trie.add_string("abc", 0);
// a
// / \
// a b
// / / \
// a b c
const answer = "aaabbc";
var buffer: [6]u8 = undefined;
_ = trie.traverse(trie.get_root_iterator(), buffer[0..6]);
try std.testing.expectEqualSlices(u8, buffer[0..6], answer[0..6]);
}

test "iterator traverse" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const debug_allocator = gpa.allocator();
const trie = try Trie(i32).init(0, debug_allocator);
defer trie.deinit();
var it = try trie.add_string("abc", 0); // "abc"
try std.testing.expectEqual(null, it.go_to_child('a'));
it = it.go_to_parent().?; // "ab"
it = it.go_to_parent().?; // "a"
it = it.go_to_parent().?; // ""
try std.testing.expectEqual(null, it.go_to_parent());
const it2 = try trie.add_string("ae", 0); // "ae"
it = it.go_to_child('a').?; // "a"
it = it.go_to_child('e').?; // "ae"
try std.testing.expectEqual(it, it2);
}
1 change: 1 addition & 0 deletions runall.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rem Math
%ZIG_TEST% -Dalgorithm=math/gcd %Args%

rem Data Structures
%ZIG_TEST% -Dalgorithm=ds/trie %Args%
%ZIG_TEST% -Dalgorithm=ds/linkedlist %Args%
%ZIG_TEST% -Dalgorithm=ds/doublylinkedlist %Args%
%ZIG_TEST% -Dalgorithm=ds/lrucache %Args%
Expand Down
1 change: 1 addition & 0 deletions runall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ $ZIG_TEST -Dalgorithm=math/euclidianGCDivisor $Args
$ZIG_TEST -Dalgorithm=math/gcd $Args

# Data Structures
$ZIG_TEST -Dalgorithm=ds/trie $Args
$ZIG_TEST -Dalgorithm=ds/linkedlist $Args
$ZIG_TEST -Dalgorithm=ds/doublylinkedlist $Args
$ZIG_TEST -Dalgorithm=ds/lrucache $Args
Expand Down