authorgravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2019-05-04 13:50:37+10:00
committergravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2019-06-10 15:41:40+10:00
logddf7942aaa6a41296d9338423dcdfb93b915e4df
tree8499d5f796c207cd8a218cde182c41516add43ee
parented41d10a068065ea80f93736926d9c3240d62c49
signaturelock-open Commit is signed but in an unrecognized format.

std: Add singly linked list


2 files changed, 180 insertions(+), 0 deletions(-)

std/linked_list.zig+179
...@@ -5,6 +5,185 @@ const testing = std.testing;...@@ -5,6 +5,185 @@ const testing = std.testing;
5const mem = std.mem;5const mem = std.mem;
6const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
77
8/// A singly-linked list is headed by a single forward pointer. The elements
9/// are singly linked for minimum space and pointer manipulation overhead at
10/// the expense of O(n) removal for arbitrary elements. New elements can be
11/// added to the list after an existing element or at the head of the list.
12/// A singly-linked list may only be traversed in the forward direction.
13/// Singly-linked lists are ideal for applications with large datasets and
14/// few or no removals or for implementing a LIFO queue.
15pub fn SinglyLinkedList(comptime T: type) type {
16 return struct {
17 const Self = @This();
18
19 /// Node inside the linked list wrapping the actual data.
20 pub const Node = struct {
21 next: ?*Node,
22 data: T,
23
24 pub fn init(data: T) Node {
25 return Node{
26 .next = null,
27 .data = data,
28 };
29 }
30
31 /// Insert a new node after the current one.
32 ///
33 /// Arguments:
34 /// new_node: Pointer to the new node to insert.
35 pub fn insertAfter(node: *Node, new_node: *Node) void {
36 new_node.next = node.next;
37 node.next = new_node;
38 }
39
40 /// Remove a node from the list.
41 ///
42 /// Arguments:
43 /// node: Pointer to the node to be removed.
44 /// Returns:
45 /// node removed
46 pub fn removeNext(node: *Node) ?*Node {
47 const next_node = node.next orelse return null;
48 node.next = next_node.next;
49 return next_node;
50 }
51 };
52
53 first: ?*Node,
54
55 /// Initialize a linked list.
56 ///
57 /// Returns:
58 /// An empty linked list.
59 pub fn init() Self {
60 return Self{
61 .first = null,
62 };
63 }
64
65 /// Insert a new node after an existing one.
66 ///
67 /// Arguments:
68 /// node: Pointer to a node in the list.
69 /// new_node: Pointer to the new node to insert.
70 pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void {
71 node.insertAfter(new_node);
72 }
73
74 /// Insert a new node at the head.
75 ///
76 /// Arguments:
77 /// new_node: Pointer to the new node to insert.
78 pub fn prepend(list: *Self, new_node: *Node) void {
79 new_node.next = list.first;
80 list.first = new_node;
81 }
82
83 /// Remove a node from the list.
84 ///
85 /// Arguments:
86 /// node: Pointer to the node to be removed.
87 pub fn remove(list: *Self, node: *Node) void {
88 if (list.first == node) {
89 list.first = node.next;
90 } else {
91 var current_elm = list.first.?;
92 while (current_elm.next != node) {
93 current_elm = current_elm.next.?;
94 }
95 current_elm.next = node.next;
96 }
97 }
98
99 /// Remove and return the first node in the list.
100 ///
101 /// Returns:
102 /// A pointer to the first node in the list.
103 pub fn popFirst(list: *Self) ?*Node {
104 const first = list.first orelse return null;
105 list.first = first.next;
106 return first;
107 }
108
109 /// Allocate a new node.
110 ///
111 /// Arguments:
112 /// allocator: Dynamic memory allocator.
113 ///
114 /// Returns:
115 /// A pointer to the new node.
116 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
117 return allocator.create(Node);
118 }
119
120 /// Deallocate a node.
121 ///
122 /// Arguments:
123 /// node: Pointer to the node to deallocate.
124 /// allocator: Dynamic memory allocator.
125 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
126 allocator.destroy(node);
127 }
128
129 /// Allocate and initialize a node and its data.
130 ///
131 /// Arguments:
132 /// data: The data to put inside the node.
133 /// allocator: Dynamic memory allocator.
134 ///
135 /// Returns:
136 /// A pointer to the new node.
137 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
138 var node = try list.allocateNode(allocator);
139 node.* = Node.init(data);
140 return node;
141 }
142 };
143}
144
145test "basic SinglyLinkedList test" {
146 const allocator = debug.global_allocator;
147 var list = SinglyLinkedList(u32).init();
148
149 var one = try list.createNode(1, allocator);
150 var two = try list.createNode(2, allocator);
151 var three = try list.createNode(3, allocator);
152 var four = try list.createNode(4, allocator);
153 var five = try list.createNode(5, allocator);
154 defer {
155 list.destroyNode(one, allocator);
156 list.destroyNode(two, allocator);
157 list.destroyNode(three, allocator);
158 list.destroyNode(four, allocator);
159 list.destroyNode(five, allocator);
160 }
161
162 list.prepend(two); // {2}
163 list.insertAfter(two, five); // {2, 5}
164 list.prepend(one); // {1, 2, 5}
165 list.insertAfter(two, three); // {1, 2, 3, 5}
166 list.insertAfter(three, four); // {1, 2, 3, 4, 5}
167
168 // Traverse forwards.
169 {
170 var it = list.first;
171 var index: u32 = 1;
172 while (it) |node| : (it = node.next) {
173 testing.expect(node.data == index);
174 index += 1;
175 }
176 }
177
178 _ = list.popFirst(); // {2, 3, 4, 5}
179 _ = list.remove(five); // {2, 3, 4}
180 _ = two.removeNext(); // {2, 4}
181
182 testing.expect(list.first.?.data == 2);
183 testing.expect(list.first.?.next.?.data == 4);
184 testing.expect(list.first.?.next.?.next == null);
185}
186
8/// A tail queue is headed by a pair of pointers, one to the head of the187/// A tail queue is headed by a pair of pointers, one to the head of the
9/// list and the other to the tail of the list. The elements are doubly188/// list and the other to the tail of the list. The elements are doubly
10/// linked so that an arbitrary element can be removed without a need to189/// linked so that an arbitrary element can be removed without a need to
std/std.zig+1
...@@ -13,6 +13,7 @@ pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;...@@ -13,6 +13,7 @@ pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
13pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;13pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
14pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;14pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
15pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;15pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
16pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
16pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;17pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
17pub const SegmentedList = @import("segmented_list.zig").SegmentedList;18pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
18pub const SpinLock = @import("spinlock.zig").SpinLock;19pub const SpinLock = @import("spinlock.zig").SpinLock;