init
This commit is contained in:
29
README.md
Normal file
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# ZiRBTree
|
||||
|
||||
Intrusive [Red-Black Trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) for [Zig](https://ziglang.org/).
|
||||
|
||||
There are a generic and a non-generic implementation.
|
||||
|
||||
The generic one is generally easier to use as you just need one comparison function and just have to
|
||||
provide it once. But of course the code is generated for each different specialization.
|
||||
|
||||
The non-generic one is not as ergonomic because of the need of `@fieldParentPtr` to access the
|
||||
containing struct, but allows for cases when a single node is part of multiple trees without long
|
||||
hierarchy chains, that would be created by the generic one. It's also just compiled one time instead
|
||||
of once for each different specialization.
|
||||
|
||||
## Installation
|
||||
|
||||
Just vendor the files and import them as necessary.
|
||||
|
||||
## Examples
|
||||
|
||||
`main_generic.zig` is an example demonstrating the usage of the generic Red-Black Tree and
|
||||
`main.zig` shows usage of the non-generic Red-Black Tree. You can also look at the tests in the
|
||||
implementation files.
|
||||
|
||||
You can run these examples using `zig run main_generic.zig` and `zig run main.zig` respectively.
|
||||
|
||||
## License
|
||||
|
||||
The code is licensed under MIT.
|
||||
1080
RedBlackTree.zig
Normal file
1080
RedBlackTree.zig
Normal file
File diff suppressed because it is too large
Load Diff
1007
generic_red_black_tree.zig
Normal file
1007
generic_red_black_tree.zig
Normal file
File diff suppressed because it is too large
Load Diff
91
main.zig
Normal file
91
main.zig
Normal file
@@ -0,0 +1,91 @@
|
||||
const std = @import("std");
|
||||
const zirb = @import("RedBlackTree.zig");
|
||||
|
||||
// Define the data structure that embeds the tree node.
|
||||
const MyDataNode = struct {
|
||||
id: u32,
|
||||
name: []const u8,
|
||||
node: zirb.Node,
|
||||
};
|
||||
|
||||
// Helper to get the containing MyDataNode from a tree node.
|
||||
fn getNodeData(n: *const zirb.Node) *const MyDataNode {
|
||||
return @fieldParentPtr("node", n);
|
||||
}
|
||||
|
||||
// Comparison function for inserting nodes.
|
||||
fn compareNodes(context: void, lhs: *const zirb.Node, rhs: *const zirb.Node) std.math.Order {
|
||||
_ = context;
|
||||
const lhs_data = getNodeData(lhs);
|
||||
const rhs_data = getNodeData(rhs);
|
||||
return std.math.order(lhs_data.id, rhs_data.id);
|
||||
}
|
||||
|
||||
// Comparison function for searching by key.
|
||||
fn compareKeyToNode(context: void, key: comptime_int, node: *const zirb.Node) std.math.Order {
|
||||
_ = context;
|
||||
const node_data = getNodeData(node);
|
||||
return std.math.order(key, node_data.id);
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// Initialize the tree.
|
||||
var tree = zirb.RedBlackTree{};
|
||||
|
||||
// Create and insert nodes.
|
||||
const data_to_insert = [_]struct {
|
||||
id: u32,
|
||||
name: []const u8,
|
||||
}{
|
||||
.{ .id = 10, .name = "ten" },
|
||||
.{ .id = 5, .name = "five" },
|
||||
.{ .id = 15, .name = "fifteen" },
|
||||
.{ .id = 1, .name = "one" },
|
||||
.{ .id = 7, .name = "seven" },
|
||||
};
|
||||
|
||||
var nodes = std.ArrayList(*MyDataNode).init(allocator);
|
||||
defer {
|
||||
for (nodes.items) |node| {
|
||||
allocator.destroy(node);
|
||||
}
|
||||
nodes.deinit();
|
||||
}
|
||||
|
||||
std.debug.print("Inserting nodes...\n", .{});
|
||||
for (data_to_insert) |data| {
|
||||
const node = try allocator.create(MyDataNode);
|
||||
node.* = .{ .id = data.id, .name = data.name, .node = .{} };
|
||||
try nodes.append(node);
|
||||
tree.insert(&node.node, {}, compareNodes);
|
||||
std.debug.print("\tInserted: id = {d}, name = {s}\n", .{ data.id, data.name });
|
||||
}
|
||||
|
||||
// Iterate over the tree in order and print the data.
|
||||
std.debug.print("\nIn-order traversal:\n", .{});
|
||||
var it = tree.inorder();
|
||||
while (it.next()) |node_ptr| {
|
||||
const data_node = getNodeData(node_ptr);
|
||||
std.debug.print("\tid = {d}, name = {s}\n", .{ data_node.id, data_node.name });
|
||||
}
|
||||
|
||||
// Find and remove a node.
|
||||
std.debug.print("\nRemoving node with id = 10...\n", .{});
|
||||
const node_to_remove = tree.search(10, {}, compareKeyToNode) orelse {
|
||||
std.debug.print("Node not found!\n", .{});
|
||||
return;
|
||||
};
|
||||
tree.remove(node_to_remove);
|
||||
|
||||
// Iterate again to show the node has been removed.
|
||||
std.debug.print("\nIn-order traversal after removal:\n", .{});
|
||||
var it_after_remove = tree.inorder();
|
||||
while (it_after_remove.next()) |node_ptr| {
|
||||
const data_node = getNodeData(node_ptr);
|
||||
std.debug.print("\tid = {d}, name = {s}\n", .{ data_node.id, data_node.name });
|
||||
}
|
||||
}
|
||||
74
main_generic.zig
Normal file
74
main_generic.zig
Normal file
@@ -0,0 +1,74 @@
|
||||
const std = @import("std");
|
||||
const zirb = @import("generic_red_black_tree.zig");
|
||||
|
||||
// Define the data structure to be stored in the tree.
|
||||
const MyData = struct {
|
||||
id: u32,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
// Define the comparison function for MyData.
|
||||
fn compareMyData(context: void, lhs: MyData, rhs: MyData) std.math.Order {
|
||||
_ = context;
|
||||
return std.math.order(lhs.id, rhs.id);
|
||||
}
|
||||
|
||||
// Define the tree type using the generic RedBlackTree.
|
||||
const MyTree = zirb.RedBlackTree(MyData, void, compareMyData);
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
// Initialize the tree.
|
||||
var tree = MyTree{ .context = {} };
|
||||
|
||||
// Create and insert nodes.
|
||||
const data_to_insert = [_]MyData{
|
||||
.{ .id = 10, .name = "ten" },
|
||||
.{ .id = 5, .name = "five" },
|
||||
.{ .id = 15, .name = "fifteen" },
|
||||
.{ .id = 1, .name = "one" },
|
||||
.{ .id = 7, .name = "seven" },
|
||||
};
|
||||
|
||||
var nodes = std.ArrayList(*MyTree.Node).init(allocator);
|
||||
defer {
|
||||
for (nodes.items) |node| {
|
||||
allocator.destroy(node);
|
||||
}
|
||||
nodes.deinit();
|
||||
}
|
||||
|
||||
std.debug.print("Inserting nodes...\n", .{});
|
||||
for (data_to_insert) |data| {
|
||||
const node = try allocator.create(MyTree.Node);
|
||||
node.* = .{ .payload = data };
|
||||
try nodes.append(node);
|
||||
tree.insert(node);
|
||||
std.debug.print("\tInserted: id = {d}, name = {s}\n", .{ data.id, data.name });
|
||||
}
|
||||
|
||||
// Iterate over the tree in order and print the data.
|
||||
std.debug.print("\nIn-order traversal:\n", .{});
|
||||
var it = tree.inorder();
|
||||
while (it.next()) |node| {
|
||||
std.debug.print("\tid = {d}, name = {s}\n", .{ node.payload.id, node.payload.name });
|
||||
}
|
||||
|
||||
// Find and remove a node.
|
||||
std.debug.print("\nRemoving node with id = 10...\n", .{});
|
||||
const node_to_remove = tree.search(.{ .id = 10, .name = "" }) orelse {
|
||||
std.debug.print("Node not found!\n", .{});
|
||||
return;
|
||||
};
|
||||
tree.remove(node_to_remove);
|
||||
|
||||
// Iterate again to show the node has been removed.
|
||||
std.debug.print("\nIn-order traversal after removal:\n", .{});
|
||||
var it_after_remove = tree.inorder();
|
||||
while (it_after_remove.next()) |node| {
|
||||
std.debug.print("\tid = {d}, name = {s}\n", .{ node.payload.id, node.payload.name });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user