1 /* 2 Copyright (c) 2011-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Singly linked list 31 * 32 * Copyright: Timur Gafarov 2011-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov, Andrey Penechko, Roman Chistokhodov, ijet 35 */ 36 module dlib.container.linkedlist; 37 38 import dlib.core.memory; 39 40 /** 41 * Element of single linked list. 42 */ 43 struct LinkedListElement(T) 44 { 45 LinkedListElement!(T)* next = null; 46 T datum; 47 48 this(LinkedListElement!(T)* n) 49 { 50 next = n; 51 datum = T.init; 52 } 53 } 54 55 /** 56 * GC-free single linked list implementation. 57 */ 58 struct LinkedList(T, bool ordered = true) 59 { 60 ///Head of the list. 61 LinkedListElement!(T)* head = null; 62 63 ///Tail of the list. 64 LinkedListElement!(T)* tail = null; 65 66 ///Number of elements in the list. 67 size_t length = 0; 68 69 /** 70 * Check if list has no elements. 71 */ 72 @property bool empty() 73 { 74 return length == 0; 75 } 76 77 /// 78 unittest 79 { 80 LinkedList!int list; 81 assert(list.empty); 82 } 83 84 /** 85 * Remove all elements and free used memory. 86 */ 87 void free() 88 { 89 LinkedListElement!(T)* element = head; 90 while (element !is null) 91 { 92 auto e = element; 93 element = element.next; 94 Delete(e); 95 } 96 head = null; 97 tail = null; 98 length = 0; 99 } 100 101 /** 102 * Iterating over list via foreach. 103 */ 104 int opApply(scope int delegate(size_t, ref T) dg) 105 { 106 int result = 0; 107 uint index = 0; 108 109 LinkedListElement!(T)* element = head; 110 while (element !is null) 111 { 112 result = dg(index, element.datum); 113 if (result) 114 break; 115 element = element.next; 116 index++; 117 } 118 119 return result; 120 } 121 122 /// 123 unittest 124 { 125 LinkedList!int list; 126 scope(exit) list.free(); 127 128 list.append(1); 129 list.append(2); 130 list.append(3); 131 list.append(4); 132 133 int[4] values; 134 135 foreach(size_t i, ref int val; list) { 136 values[i] = val; 137 } 138 139 assert(values[] == [1,2,3,4]); 140 } 141 142 /** 143 * Iterating over list via foreach. 144 */ 145 int opApply(scope int delegate(ref T) dg) 146 { 147 int result = 0; 148 149 LinkedListElement!(T)* element = head; 150 while (element !is null) 151 { 152 result = dg(element.datum); 153 if (result) 154 break; 155 element = element.next; 156 } 157 158 return result; 159 } 160 161 /// 162 unittest 163 { 164 LinkedList!int list; 165 scope(exit) list.free(); 166 167 list.append(1); 168 list.append(2); 169 list.append(3); 170 list.append(4); 171 172 int[] values; 173 174 foreach(ref int val; list) { 175 values ~= val; 176 } 177 178 assert(values[] == [1,2,3,4]); 179 } 180 181 /** 182 * Appen value v to the end. 183 * Returns: Pointer to added list element. 184 */ 185 LinkedListElement!(T)* insertBack(T v) 186 { 187 length++; 188 189 if (tail is null) 190 { 191 tail = New!(LinkedListElement!(T))(null); 192 tail.datum = v; 193 } 194 else 195 { 196 tail.next = New!(LinkedListElement!(T))(null); 197 tail.next.datum = v; 198 tail = tail.next; 199 } 200 201 if (head is null) head = tail; 202 203 return tail; 204 } 205 206 // Insert operator 207 auto opCatAssign(T v) 208 { 209 insertBack(v); 210 return this; 211 } 212 213 /// 214 unittest 215 { 216 LinkedList!int list; 217 scope(exit) list.free(); 218 219 auto element = list.append(13); 220 assert(element.datum == 13); 221 assert(list.length == 1); 222 element = list.append(42); 223 assert(element.datum == 42); 224 assert(list.length == 2); 225 } 226 227 /** 228 * Insert value v after element. 229 * Returns: Pointer to inserted element. 230 * Note: element must be not null. 231 */ 232 LinkedListElement!(T)* insertAfter(LinkedListElement!(T)* element, T v) 233 { 234 length++; 235 auto newElement = New!(LinkedListElement!(T))(null); 236 newElement.datum = v; 237 newElement.next = element.next; 238 element.next = newElement; 239 if (element is tail) tail = newElement; 240 return newElement; 241 } 242 243 /// 244 unittest 245 { 246 LinkedList!int list; 247 scope(exit) list.free(); 248 249 auto first = list.append(1); 250 auto last = list.append(2); 251 list.insertAfter(first, 3); 252 253 assert(list.length == 3); 254 auto arr = list.toArray(); 255 assert(arr == [1,3,2]); 256 Delete(arr); 257 } 258 259 /** 260 * Insert value v at the beginning. 261 */ 262 LinkedListElement!(T)* insertFront(T v) 263 { 264 length++; 265 auto newElement = New!(LinkedListElement!(T))(null); 266 newElement.datum = v; 267 newElement.next = head; 268 head = newElement; 269 if (tail is null) { 270 tail = head; 271 } 272 return newElement; 273 } 274 275 /// 276 unittest 277 { 278 LinkedList!int list; 279 scope(exit) list.free(); 280 281 list.insertBeginning(1); 282 list.insertBack(2); 283 list.insertBeginning(0); 284 285 import std.algorithm : equal; 286 assert(equal(list.byElement(), [0,1,2])); 287 } 288 289 /** 290 * Remove value after element. 291 * Note: element must be not null. 292 */ 293 void removeAfter(LinkedListElement!(T)* element) 294 { 295 length--; 296 auto obsolete = element.next; 297 if (obsolete !is null) 298 { 299 if (obsolete is tail) tail = element; 300 element.next = obsolete.next; 301 Delete(obsolete); 302 } 303 } 304 305 /// 306 unittest 307 { 308 LinkedList!int list; 309 scope(exit) list.free(); 310 311 auto first = list.insertBack(1); 312 auto second = list.insertBack(2); 313 auto third = list.insertBack(3); 314 list.removeAfter(first); 315 316 import std.algorithm : equal; 317 assert(equal(list.byElement(), [1,3])); 318 } 319 320 /** 321 * Remove the first element. 322 * Note: list must be non-empty. 323 */ 324 void removeFront() 325 { 326 length--; 327 auto obsolete = head; 328 if (obsolete !is null) 329 { 330 head = obsolete.next; 331 Delete(obsolete); 332 } 333 } 334 335 /// 336 unittest 337 { 338 LinkedList!int list; 339 scope(exit) list.free(); 340 341 list.insertBack(0); 342 list.removeFront(); 343 assert(list.length == 0); 344 345 list.insertBack(1); 346 list.insertBack(2); 347 list.insertBack(3); 348 list.removeFront(); 349 assert(list.length == 2); 350 import std.algorithm : equal; 351 assert(equal(list.byElement(), [2,3])); 352 } 353 354 /** 355 * Append other list. 356 * Note: Appended list should not be freed. It becomes part of this list. 357 */ 358 void appendList(LinkedList!(T) list) 359 { 360 length += list.length; 361 if (tail !is null) { 362 tail.next = list.head; 363 } 364 if (head is null) { 365 head = list.head; 366 } 367 tail = list.tail; 368 } 369 370 /// 371 unittest 372 { 373 LinkedList!int list1; 374 scope(exit) list1.free(); 375 LinkedList!int list2; 376 LinkedList!int list3; 377 378 list2.insertBack(1); 379 list2.insertBack(2); 380 381 list1.appendList(list2); 382 383 import std.algorithm : equal; 384 assert(equal(list1.byElement(), [1,2])); 385 386 list3.insertBack(3); 387 list3.insertBack(4); 388 list1.appendList(list3); 389 390 assert(equal(list1.byElement(), [1,2,3,4])); 391 } 392 393 /** 394 * Search for element with value v. 395 * Returns: Found element or null if could not find. 396 */ 397 LinkedListElement!(T)* find(T v) 398 { 399 LinkedListElement!(T)* element = head; 400 LinkedListElement!(T)* prevElement = head; 401 while (element !is null) 402 { 403 if (element.datum == v) 404 { 405 static if (!ordered) 406 { 407 /* 408 * Move-to-front heuristic: 409 * Move an element to the beginning of the list once it is found. 410 * This scheme ensures that the most recently used items are also 411 * the quickest to find again. 412 */ 413 prevElement.next = element.next; 414 element.next = head; 415 head = element; 416 } 417 418 return element; 419 } 420 421 prevElement = element; 422 element = element.next; 423 } 424 425 return null; 426 } 427 428 /// 429 unittest 430 { 431 LinkedList!int list; 432 scope(exit) list.free(); 433 434 assert(list.find(42) is null); 435 436 list.insertBack(13); 437 list.insertBack(42); 438 439 auto first = list.find(13); 440 assert(first && first.datum == 13); 441 442 auto second = list.find(42); 443 assert(second && second.datum == 42); 444 445 assert(list.find(0) is null); 446 } 447 448 /** 449 * Convert to array. 450 */ 451 T[] toArray() 452 { 453 T[] arr = New!(T[])(length); 454 foreach(i, v; this) 455 arr[i] = v; 456 return arr; 457 } 458 459 /// 460 unittest 461 { 462 LinkedList!int list; 463 scope(exit) list.free(); 464 465 list.insertBack(1); 466 list.insertBack(2); 467 list.insertBack(3); 468 469 auto arr = list.toArray(); 470 assert(arr == [1,2,3]); 471 Delete(arr); 472 } 473 474 auto byElement() 475 { 476 struct ByElement 477 { 478 private: 479 LinkedListElement!(T)* _first; 480 481 public: 482 @property bool empty() { 483 return _first is null; 484 } 485 486 @property T front() { 487 return _first.datum; 488 } 489 490 void popFront() { 491 _first = _first.next; 492 } 493 494 auto save() { 495 return this; 496 } 497 } 498 499 return ByElement(head); 500 } 501 502 /// 503 unittest 504 { 505 LinkedList!int list; 506 scope(exit) list.free(); 507 508 assert(list.byElement().empty); 509 510 list.insertBack(1); 511 list.insertBack(2); 512 list.insertBack(3); 513 514 auto range = list.byElement(); 515 import std.range : isInputRange; 516 import std.algorithm : equal; 517 static assert(isInputRange!(typeof(range))); 518 519 assert(equal(range, [1,2,3])); 520 521 range = list.byElement(); 522 auto saved = range.save(); 523 range.popFront(); 524 assert(equal(range,[2,3])); 525 assert(equal(saved,[1,2,3])); 526 } 527 528 // For backward compatibility 529 alias append = insertBack; 530 alias insertBeginning = insertFront; 531 alias removeBeginning = removeFront; 532 alias search = find; 533 }