authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-07 12:47:28-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-08-07 12:47:28-04:00
log034363a86c683de479429c8b275e6279a77eb8c1
treedd7360d123f097bee4a3e81973f5db2cb5e64009
parentdcf3869acd0055b561561506ef25b82d9ed7074d
parentbbbb26f4d3271064ab35c17d214b504eac5a0ef9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1338 from shawnl/master

std: add red-black tree implementation

3 files changed, 583 insertions(+), 5 deletions(-)

std/index.zig+1
...@@ -32,6 +32,7 @@ pub const mem = @import("mem.zig");...@@ -32,6 +32,7 @@ pub const mem = @import("mem.zig");
32pub const net = @import("net.zig");32pub const net = @import("net.zig");
33pub const os = @import("os/index.zig");33pub const os = @import("os/index.zig");
34pub const rand = @import("rand/index.zig");34pub const rand = @import("rand/index.zig");
35pub const rb = @import("rb.zig");
35pub const sort = @import("sort.zig");36pub const sort = @import("sort.zig");
36pub const unicode = @import("unicode.zig");37pub const unicode = @import("unicode.zig");
37pub const zig = @import("zig/index.zig");38pub const zig = @import("zig/index.zig");
std/mem.zig+41-5
...@@ -135,6 +135,12 @@ pub const Allocator = struct {...@@ -135,6 +135,12 @@ pub const Allocator = struct {
135 }135 }
136};136};
137137
138const Compare = enum {
139 LessThan,
140 Equal,
141 GreaterThan,
142};
143
138/// Copy all of source into dest at position 0.144/// Copy all of source into dest at position 0.
139/// dest.len must be >= source.len.145/// dest.len must be >= source.len.
140/// dest.ptr must be <= src.ptr.146/// dest.ptr must be <= src.ptr.
...@@ -169,16 +175,46 @@ pub fn set(comptime T: type, dest: []T, value: T) void {...@@ -169,16 +175,46 @@ pub fn set(comptime T: type, dest: []T, value: T) void {
169 d.* = value;175 d.* = value;
170}176}
171177
172/// Returns true if lhs < rhs, false otherwise178pub fn compare(comptime T: type, lhs: []const T, rhs: []const T) Compare {
173pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool {
174 const n = math.min(lhs.len, rhs.len);179 const n = math.min(lhs.len, rhs.len);
175 var i: usize = 0;180 var i: usize = 0;
176 while (i < n) : (i += 1) {181 while (i < n) : (i += 1) {
177 if (lhs[i] == rhs[i]) continue;182 if (lhs[i] == rhs[i]) {
178 return lhs[i] < rhs[i];183 continue;
184 } else if (lhs[i] < rhs[i]) {
185 return Compare.LessThan;
186 } else if (lhs[i] > rhs[i]) {
187 return Compare.GreaterThan;
188 } else {
189 unreachable;
190 }
179 }191 }
180192
181 return lhs.len < rhs.len;193 if (lhs.len == rhs.len) {
194 return Compare.Equal;
195 } else if (lhs.len < rhs.len) {
196 return Compare.LessThan;
197 } else if (lhs.len > rhs.len) {
198 return Compare.GreaterThan;
199 }
200 unreachable;
201}
202
203test "mem.compare" {
204 assert(compare(u8, "abcd", "bee") == Compare.LessThan);
205 assert(compare(u8, "abc", "abc") == Compare.Equal);
206 assert(compare(u8, "abc", "abc0") == Compare.LessThan);
207 assert(compare(u8, "", "") == Compare.Equal);
208 assert(compare(u8, "", "a") == Compare.LessThan);
209}
210
211/// Returns true if lhs < rhs, false otherwise
212pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool {
213 var result = compare(T, lhs, rhs);
214 if (result == Compare.LessThan) {
215 return true;
216 } else
217 return false;
182}218}
183219
184test "mem.lessThan" {220test "mem.lessThan" {
std/rb.zig created+541
...@@ -0,0 +1,541 @@
1const assert = @import("std").debug.assert;
2const mem = @import("std").mem; // For mem.Compare
3
4const Color = enum(u1) {
5 Black,
6 Red,
7};
8const Red = Color.Red;
9const Black = Color.Black;
10
11const ReplaceError = error {
12 NotEqual,
13};
14
15/// Insert this into your struct that you want to add to a red-black tree.
16/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
17/// (after resolving optionals) to your structure using @fieldParentPtr(). Example:
18///
19/// const Number = struct {
20/// node: rb.Node,
21/// value: i32,
22/// };
23/// fn number(node: *Node) Number {
24/// return @fieldParentPtr(Number, "node", node);
25/// }
26pub const Node = struct {
27 left: ?*Node,
28 right: ?*Node,
29 parent_and_color: usize, /// parent | color
30
31 pub fn next(constnode: *Node) ?*Node {
32 var node = constnode;
33
34 if (node.right) |right| {
35 var n = right;
36 while (n.left) |left|
37 n = left;
38 return n;
39 }
40
41 while (true) {
42 var parent = node.get_parent();
43 if (parent) |p| {
44 if (node != p.right)
45 return p;
46 node = p;
47 } else
48 return null;
49 }
50 }
51
52 pub fn prev(constnode: *Node) ?*Node {
53 var node = constnode;
54
55 if (node.left) |left| {
56 var n = left;
57 while (n.right) |right|
58 n = right;
59 return n;
60 }
61
62 while (true) {
63 var parent = node.get_parent();
64 if (parent) |p| {
65 if (node != p.left)
66 return p;
67 node = p;
68 } else
69 return null;
70 }
71 }
72
73 pub fn is_root(node: *Node) bool {
74 return node.get_parent() == null;
75 }
76
77 fn is_red(node: *Node) bool {
78 return node.get_color() == Red;
79 }
80
81 fn is_black(node: *Node) bool {
82 return node.get_color() == Black;
83 }
84
85 fn set_parent(node: *Node, parent: ?*Node) void {
86 node.parent_and_color = @ptrToInt(parent) | (node.parent_and_color & 1);
87 }
88
89 fn get_parent(node: *Node) ?*Node {
90 const mask: usize = 1;
91 comptime {
92 assert(@alignOf(*Node) >= 2);
93 }
94 return @intToPtr(*Node, node.parent_and_color & ~mask);
95 }
96
97 fn set_color(node: *Node, color: Color) void {
98 const mask: usize = 1;
99 node.parent_and_color = (node.parent_and_color & ~mask) | @enumToInt(color);
100 }
101
102 fn get_color(node: *Node) Color {
103 return @intToEnum(Color, @intCast(u1, node.parent_and_color & 1));
104 }
105
106 fn set_child(node: *Node, child: ?*Node, is_left: bool) void {
107 if (is_left) {
108 node.left = child;
109 } else {
110 node.right = child;
111 }
112 }
113
114 fn get_first(nodeconst: *Node) *Node {
115 var node = nodeconst;
116 while (node.left) |left| {
117 node = left;
118 }
119 return node;
120 }
121
122 fn get_last(node: *Node) *Node {
123 while (node.right) |right| {
124 node = right;
125 }
126 return node;
127 }
128};
129
130pub const Tree = struct {
131 root: ?*Node,
132 compare_fn: fn(*Node, *Node) mem.Compare,
133
134 pub fn first(tree: *Tree) ?*Node {
135 var node: *Node = tree.root orelse return null;
136
137 while (node.left) |left| {
138 node = left;
139 }
140
141 return node;
142 }
143
144 pub fn last(tree: *Tree) ?*Node {
145 var node: *Node = tree.root orelse return null;
146
147 while (node.right) |right| {
148 node = right;
149 }
150
151 return node;
152 }
153
154 /// Duplicate keys are not allowed. The item with the same key already in the
155 /// tree will be returned, and the item will not be inserted.
156 pub fn insert(tree: *Tree, node_const: *Node) ?*Node {
157 var node = node_const;
158 var maybe_key: ?*Node = undefined;
159 var maybe_parent: ?*Node = undefined;
160 var is_left: bool = undefined;
161
162 maybe_key = do_lookup(node, tree, &maybe_parent, &is_left);
163 if (maybe_key) |key| {
164 return key;
165 }
166
167 node.left = null;
168 node.right = null;
169 node.set_color(Red);
170 node.set_parent(maybe_parent);
171
172 if (maybe_parent) |parent| {
173 parent.set_child(node, is_left);
174 } else {
175 tree.root = node;
176 }
177
178 while (node.get_parent()) |*parent| {
179 if (parent.*.is_black())
180 break;
181 // the root is always black
182 var grandpa = parent.*.get_parent() orelse unreachable;
183
184 if (parent.* == grandpa.left) {
185 var maybe_uncle = grandpa.right;
186
187 if (maybe_uncle) |uncle| {
188 if (uncle.is_black())
189 break;
190
191 parent.*.set_color(Black);
192 uncle.set_color(Black);
193 grandpa.set_color(Red);
194 node = grandpa;
195 } else {
196 if (node == parent.*.right) {
197 rotate_left(parent.*, tree);
198 node = parent.*;
199 parent.* = node.get_parent().?; // Just rotated
200 }
201 parent.*.set_color(Black);
202 grandpa.set_color(Red);
203 rotate_right(grandpa, tree);
204 }
205 } else {
206 var maybe_uncle = grandpa.left;
207
208 if (maybe_uncle) |uncle| {
209 if (uncle.is_black())
210 break;
211
212 parent.*.set_color(Black);
213 uncle.set_color(Black);
214 grandpa.set_color(Red);
215 node = grandpa;
216 } else {
217 if (node == parent.*.left) {
218 rotate_right(parent.*, tree);
219 node = parent.*;
220 parent.* = node.get_parent().?; // Just rotated
221 }
222 parent.*.set_color(Black);
223 grandpa.set_color(Red);
224 rotate_left(grandpa, tree);
225 }
226 }
227 }
228 // This was an insert, there is at least one node.
229 tree.root.?.set_color(Black);
230 return null;
231 }
232
233 pub fn lookup(tree: *Tree, key: *Node) ?*Node {
234 var parent: *Node = undefined;
235 var is_left: bool = undefined;
236
237 return do_lookup(key, tree, &parent, &is_left);
238 }
239
240 pub fn remove(tree: *Tree, nodeconst: *Node) void {
241 var node = nodeconst;
242 // as this has the same value as node, it is unsafe to access node after newnode
243 var newnode: ?*Node = nodeconst;
244 var maybe_parent: ?*Node = node.get_parent();
245 var color: Color = undefined;
246 var next: *Node = undefined;
247
248 // This clause is to avoid optionals
249 if (node.left == null and node.right == null) {
250 if (maybe_parent) |parent| {
251 parent.set_child(null, parent.left == node);
252 } else
253 tree.root = null;
254 color = node.get_color();
255 newnode = null;
256 } else {
257 if (node.left == null) {
258 next = node.right.?; // Not both null as per above
259 } else if (node.right == null) {
260 next = node.left.?; // Not both null as per above
261 } else
262 next = node.right.?.get_first(); // Just checked for null above
263
264 if (maybe_parent) |parent| {
265 parent.set_child(next, parent.left == node);
266 } else
267 tree.root = next;
268
269 if (node.left != null and node.right != null) {
270 const left = node.left.?;
271 const right = node.right.?;
272
273 color = next.get_color();
274 next.set_color(node.get_color());
275
276 next.left = left;
277 left.set_parent(next);
278
279 if (next != right) {
280 var parent = next.get_parent().?; // Was traversed via child node (right/left)
281 next.set_parent(node.get_parent());
282
283 newnode = next.right;
284 parent.left = node;
285
286 next.right = right;
287 right.set_parent(next);
288 } else {
289 next.set_parent(maybe_parent);
290 maybe_parent = next;
291 newnode = next.right;
292 }
293 } else {
294 color = node.get_color();
295 newnode = next;
296 }
297 }
298
299 if (newnode) |n|
300 n.set_parent(maybe_parent);
301
302 if (color == Red)
303 return;
304 if (newnode) |n| {
305 n.set_color(Black);
306 return;
307 }
308
309 while (node == tree.root) {
310 // If not root, there must be parent
311 var parent = maybe_parent.?;
312 if (node == parent.left) {
313 var sibling = parent.right.?; // Same number of black nodes.
314
315 if (sibling.is_red()) {
316 sibling.set_color(Black);
317 parent.set_color(Red);
318 rotate_left(parent, tree);
319 sibling = parent.right.?; // Just rotated
320 }
321 if ((if (sibling.left) |n| n.is_black() else true) and
322 (if (sibling.right) |n| n.is_black() else true)) {
323 sibling.set_color(Red);
324 node = parent;
325 maybe_parent = parent.get_parent();
326 continue;
327 }
328 if (if (sibling.right) |n| n.is_black() else true) {
329 sibling.left.?.set_color(Black); // Same number of black nodes.
330 sibling.set_color(Red);
331 rotate_right(sibling, tree);
332 sibling = parent.right.?; // Just rotated
333 }
334 sibling.set_color(parent.get_color());
335 parent.set_color(Black);
336 sibling.right.?.set_color(Black); // Same number of black nodes.
337 rotate_left(parent, tree);
338 newnode = tree.root;
339 break;
340 } else {
341 var sibling = parent.left.?; // Same number of black nodes.
342
343 if (sibling.is_red()) {
344 sibling.set_color(Black);
345 parent.set_color(Red);
346 rotate_right(parent, tree);
347 sibling = parent.left.?; // Just rotated
348 }
349 if ((if (sibling.left) |n| n.is_black() else true) and
350 (if (sibling.right) |n| n.is_black() else true)) {
351 sibling.set_color(Red);
352 node = parent;
353 maybe_parent = parent.get_parent();
354 continue;
355 }
356 if (if (sibling.left) |n| n.is_black() else true) {
357 sibling.right.?.set_color(Black); // Same number of black nodes
358 sibling.set_color(Red);
359 rotate_left(sibling, tree);
360 sibling = parent.left.?; // Just rotated
361 }
362 sibling.set_color(parent.get_color());
363 parent.set_color(Black);
364 sibling.left.?.set_color(Black); // Same number of black nodes
365 rotate_right(parent, tree);
366 newnode = tree.root;
367 break;
368 }
369
370 if (node.is_red())
371 break;
372 }
373
374 if (newnode) |n|
375 n.set_color(Black);
376 }
377
378 /// This is a shortcut to avoid removing and re-inserting an item with the same key.
379 pub fn replace(tree: *Tree, old: *Node, newconst: *Node) !void {
380 var new = newconst;
381
382 // I assume this can get optimized out if the caller already knows.
383 if (tree.compare_fn(old, new) != mem.Compare.Equal) return ReplaceError.NotEqual;
384
385 if (old.get_parent()) |parent| {
386 parent.set_child(new, parent.left == old);
387 } else
388 tree.root = new;
389
390 if (old.left) |left|
391 left.set_parent(new);
392 if (old.right) |right|
393 right.set_parent(new);
394
395 new.* = old.*;
396 }
397
398 pub fn init(tree: *Tree, f: fn(*Node, *Node) mem.Compare) void {
399 tree.root = null;
400 tree.compare_fn = f;
401 }
402};
403
404fn rotate_left(node: *Node, tree: *Tree) void {
405 var p: *Node = node;
406 var q: *Node = node.right orelse unreachable;
407 var parent: *Node = undefined;
408
409 if (!p.is_root()) {
410 parent = p.get_parent().?;
411 if (parent.left == p) {
412 parent.left = q;
413 } else {
414 parent.right = q;
415 }
416 q.set_parent(parent);
417 } else {
418 tree.root = q;
419 q.set_parent(null);
420 }
421 p.set_parent(q);
422
423 p.right = q.left;
424 if (p.right) |right| {
425 right.set_parent(p);
426 }
427 q.left = p;
428}
429
430fn rotate_right(node: *Node, tree: *Tree) void {
431 var p: *Node = node;
432 var q: *Node = node.left orelse unreachable;
433 var parent: *Node = undefined;
434
435 if (!p.is_root()) {
436 parent = p.get_parent().?;
437 if (parent.left == p) {
438 parent.left = q;
439 } else {
440 parent.right = q;
441 }
442 q.set_parent(parent);
443 } else {
444 tree.root = q;
445 q.set_parent(null);
446 }
447 p.set_parent(q);
448
449 p.left = q.right;
450 if (p.left) |left| {
451 left.set_parent(p);
452 }
453 q.right = p;
454}
455
456fn do_lookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {
457 var maybe_node: ?*Node = tree.root;
458
459 pparent.* = null;
460 is_left.* = false;
461
462 while (maybe_node) |node| {
463 var res: mem.Compare = tree.compare_fn(node, key);
464 if (res == mem.Compare.Equal) {
465 return node;
466 }
467 pparent.* = node;
468 if (res == mem.Compare.GreaterThan) {
469 is_left.* = true;
470 maybe_node = node.left;
471 } else if (res == mem.Compare.LessThan) {
472 is_left.* = false;
473 maybe_node = node.right;
474 } else {
475 unreachable;
476 }
477 }
478 return null;
479}
480
481const testNumber = struct {
482 node: Node,
483 value: usize,
484};
485
486fn testGetNumber(node: *Node) *testNumber {
487 return @fieldParentPtr(testNumber, "node", node);
488}
489
490fn testCompare(l: *Node, r: *Node) mem.Compare {
491 var left = testGetNumber(l);
492 var right = testGetNumber(r);
493
494 if (left.value < right.value) {
495 return mem.Compare.LessThan;
496 } else if (left.value == right.value) {
497 return mem.Compare.Equal;
498 } else if (left.value > right.value) {
499 return mem.Compare.GreaterThan;
500 }
501 unreachable;
502}
503
504test "populate, remove, and replace, depulicate keys" {
505 var tree: Tree = undefined;
506 var ns: [10]testNumber = undefined;
507 ns[0].value = 42;
508 ns[1].value = 41;
509 ns[2].value = 40;
510 ns[3].value = 39;
511 ns[4].value = 38;
512 ns[5].value = 39;
513 ns[6].value = 3453;
514 ns[7].value = 32345;
515 ns[8].value = 392345;
516 ns[9].value = 4;
517
518 var dup: testNumber = undefined;
519 dup.value = 32345;
520
521 tree.init(testCompare);
522 _ = tree.insert(&ns[1].node);
523 _ = tree.insert(&ns[2].node);
524 _ = tree.insert(&ns[3].node);
525 _ = tree.insert(&ns[4].node);
526 _ = tree.insert(&ns[5].node);
527 _ = tree.insert(&ns[6].node);
528 _ = tree.insert(&ns[7].node);
529 _ = tree.insert(&ns[8].node);
530 _ = tree.insert(&ns[9].node);
531 tree.remove(&ns[3].node);
532 assert(tree.insert(&dup.node) == &ns[7].node);
533 try tree.replace(&ns[7].node, &dup.node);
534
535 var num: *testNumber = undefined;
536 num = testGetNumber(tree.first().?);
537 while (num.node.next() != null) {
538 assert(testGetNumber(num.node.next().?).value > num.value);
539 num = testGetNumber(num.node.next().?);
540 }
541}