Skip to content
Merged
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
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,14 @@
],
"difficulty": 8
},
{
"slug": "satellite",
"name": "Satellite",
"uuid": "88ff7fda-41e5-47d7-ac32-f7fee5d7a010",
"practices": [],
"prerequisites": [],
"difficulty": 9
},
{
"slug": "zebra-puzzle",
"name": "Zebra Puzzle",
Expand Down
27 changes: 27 additions & 0 deletions exercises/practice/satellite/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Instructions

Imagine you need to transmit a binary tree to a satellite approaching Alpha Centauri and you have limited bandwidth.
Since the tree has no repeating items it can be uniquely represented by its [pre-order and in-order traversals][wiki].

Write the software for the satellite to rebuild the tree from the traversals.

A pre-order traversal reads the value of the current node before (hence "pre") reading the left subtree in pre-order.
Afterwards the right subtree is read in pre-order.

An in-order traversal reads the left subtree in-order then the current node and finally the right subtree in-order.
So in order from left to right.

For example the pre-order traversal of this tree is [a, i, x, f, r].
The in-order traversal of this tree is [i, a, f, x, r]

```text
a
/ \
i x
/ \
f r
```

Note: the first item in the pre-order traversal is always the root.

[wiki]: https://en.wikipedia.org/wiki/Tree_traversal
17 changes: 17 additions & 0 deletions exercises/practice/satellite/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"authors": [
"keiravillekode"
],
"files": {
"solution": [
"satellite.zig"
],
"test": [
"test_satellite.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "Rebuild binary trees from pre-order and in-order traversals."
}
121 changes: 121 additions & 0 deletions exercises/practice/satellite/.meta/example.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const std = @import("std");
const mem = std.mem;

pub const TraversalError = error{
DifferentLengths,
DifferentItems,
NonUniqueItems,
};

pub const Node = struct {
left: ?*Node,
right: ?*Node,
data: i32,

fn init(allocator: mem.Allocator, left: ?*Node, right: ?*Node, data: i32) mem.Allocator.Error!*Node {
const self = try allocator.create(Node);
self.left = left;
self.right = right;
self.data = data;
return self;
}

fn deinit(self: *Node, allocator: mem.Allocator) void {
if (self.left) |child| {
child.deinit(allocator);
}
if (self.right) |child| {
child.deinit(allocator);
}
allocator.destroy(self);
}

fn write(self: *const Node, list: *std.array_list.Managed(i32)) mem.Allocator.Error!void {
if (self.left) |child| {
try child.write(list);
}
if (self.right) |child| {
try child.write(list);
}
try list.append(self.data);
}
};

pub const Tree = struct {
allocator: mem.Allocator,
root: ?*Node,

pub fn initFromTraversals(allocator: mem.Allocator, preorder: []const u8, inorder: []const u8) (mem.Allocator.Error || TraversalError)!Tree {
if (preorder.len != inorder.len) {
return TraversalError.DifferentLengths;
}
if (preorder.len == 0) {
return .{
.allocator = allocator,
.root = null,
};
}
if (repeats(preorder) or repeats(inorder)) {
return TraversalError.NonUniqueItems;
}

var preorder_index: usize = 0;
var inorder_index: usize = 0;
return .{
.allocator = allocator,
.root = try traverse(allocator, preorder, inorder, &preorder_index, &inorder_index, null),
};
}

pub fn deinit(self: *Tree) void {
if (self.root) |child| {
child.deinit(self.allocator);
}
}
};

fn repeats(items: []const u8) bool {
for (1..(items.len)) |i| {
for (0..i) |j| {
if (items[j] == items[i]) {
return true;
}
}
}
return false;
}

fn traverse(allocator: mem.Allocator, preorder: []const u8, inorder: []const u8, preorder_index: *usize, inorder_index: *usize, successor: ?u8) (mem.Allocator.Error || TraversalError)!?*Node {
if (successor) |s| {
if (inorder_index.* < inorder.len and inorder[inorder_index.*] == s) {
return null; // our parent has empty left subtree
}
}
if (preorder_index.* == preorder.len) {
return null;
}

const data = preorder[preorder_index.*];
preorder_index.* += 1;

const left = try traverse(allocator, preorder, inorder, preorder_index, inorder_index, data);
errdefer {
if (left) |child| {
child.deinit(allocator);
}
}

if (inorder_index.* == inorder.len or inorder[inorder_index.*] != data) {
return TraversalError.DifferentItems;
}
inorder_index.* += 1;

const right = try traverse(allocator, preorder, inorder, preorder_index, inorder_index, successor);
errdefer {
if (right) |child| {
child.deinit(allocator);
}
}

return try Node.init(allocator, left, right, data);
}
28 changes: 28 additions & 0 deletions exercises/practice/satellite/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[8df3fa26-811a-4165-9286-ff9ac0850d19]
description = "Empty tree"

[f945ccfc-05e3-47d7-825b-0270559d43ad]
description = "Tree with one item"

[a0121d5f-37b0-48dd-9c64-cba4c4464135]
description = "Tree with many items"

[6074041f-4891-4d81-a128-401050c2a3b0]
description = "Reject traversals of different length"

[27916ce4-45f3-4d8b-8528-496fedc157ca]
description = "Reject inconsistent traversals of same length"

[d86a3d72-76a9-43b5-9d3a-e64cb1216035]
description = "Reject traversals with repeated items"
30 changes: 30 additions & 0 deletions exercises/practice/satellite/satellite.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const std = @import("std");
const mem = std.mem;

pub const TraversalError = error{
DifferentLengths,
DifferentItems,
NonUniqueItems,
};

pub const Node = struct {
// This struct, as well as its fields and methods, needs to be implemented.
};

pub const Tree = struct {
// This struct, as well as its fields and methods, needs to be implemented.

root: ?*Node,

pub fn initFromTraversals(allocator: mem.Allocator, preorder: []const u8, inorder: []const u8) (mem.Allocator.Error || TraversalError)!Tree {
_ = allocator;
_ = preorder;
_ = inorder;
@compileError("please implement the initFromTraversals method");
}

pub fn deinit(self: *Tree) void {
_ = self;
@compileError("please implement the deinit method");
}
};
84 changes: 84 additions & 0 deletions exercises/practice/satellite/test_satellite.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const std = @import("std");
const testing = std.testing;

const satellite = @import("satellite.zig");
const Tree = satellite.Tree;
const TraversalError = satellite.TraversalError;

test "Empty tree" {
const preorder = [_]u8{};
const inorder = [_]u8{};
var tree = try Tree.initFromTraversals(testing.allocator, &preorder, &inorder);
defer tree.deinit();
try testing.expectEqual(null, tree.root);
}

test "Tree with one item" {
const preorder = [_]u8{'a'};
const inorder = [_]u8{'a'};
var tree = try Tree.initFromTraversals(testing.allocator, &preorder, &inorder);
defer tree.deinit();
if (tree.root) |o| {
try testing.expectEqual('a', o.data);
try testing.expectEqual(null, o.left);
try testing.expectEqual(null, o.right);
} else {
try testing.expectEqual(false, true); // tree.root should not be null
}
}

test "Tree with many items" {
const preorder = [_]u8{ 'a', 'i', 'x', 'f', 'r' };
const inorder = [_]u8{ 'i', 'a', 'f', 'x', 'r' };
var tree = try Tree.initFromTraversals(testing.allocator, &preorder, &inorder);
defer tree.deinit();
if (tree.root) |o| {
try testing.expectEqual('a', o.data);
if (o.left) |l| {
try testing.expectEqual('i', l.data);
try testing.expectEqual(null, l.left);
try testing.expectEqual(null, l.right);
} else {
try testing.expectEqual(false, true); // o.left should not be null
}
if (o.right) |r| {
try testing.expectEqual('x', r.data);
if (r.left) |rl| {
try testing.expectEqual('f', rl.data);
try testing.expectEqual(null, rl.left);
try testing.expectEqual(null, rl.right);
} else {
try testing.expectEqual(false, true); // r.left should not be null
}
if (r.right) |rr| {
try testing.expectEqual('r', rr.data);
try testing.expectEqual(null, rr.left);
try testing.expectEqual(null, rr.right);
} else {
try testing.expectEqual(false, true); // r.right should not be null
}
} else {
try testing.expectEqual(false, true); // o.right should not be null
}
} else {
try testing.expectEqual(false, true); // tree.root should not be null
}
}

test "Reject traversals of different length" {
const preorder = [_]u8{ 'a', 'b' };
const inorder = [_]u8{ 'b', 'a', 'r' };
try testing.expectError(TraversalError.DifferentLengths, Tree.initFromTraversals(testing.allocator, &preorder, &inorder));
}

test "Reject inconsistent traversals of same length" {
const preorder = [_]u8{ 'x', 'y', 'z' };
const inorder = [_]u8{ 'a', 'b', 'c' };
try testing.expectError(TraversalError.DifferentItems, Tree.initFromTraversals(testing.allocator, &preorder, &inorder));
}

test "Reject traversals with repeated items" {
const preorder = [_]u8{ 'a', 'b', 'a' };
const inorder = [_]u8{ 'b', 'a', 'a' };
try testing.expectError(TraversalError.NonUniqueItems, Tree.initFromTraversals(testing.allocator, &preorder, &inorder));
}