-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
exercises(linked-list): implement (#496)
Refs: #490
- Loading branch information
Showing
7 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Instructions | ||
|
||
Implement a doubly linked list. | ||
|
||
Like an array, a linked list is a simple linear data structure. | ||
Several common data types can be implemented using linked lists, like queues, stacks, and associative arrays. | ||
|
||
A linked list is a collection of data elements called *nodes*. | ||
In a *singly linked list* each node holds a value and a link to the next node. | ||
In a *doubly linked list* each node also holds a link to the previous node. | ||
|
||
You will write an implementation of a doubly linked list. | ||
Implement a Node to hold a value and pointers to the next and previous nodes. | ||
Then implement a List which holds references to the first and last node and offers an array-like interface for adding and removing items: | ||
|
||
- `push` (*insert value at back*); | ||
- `pop` (*remove value at back*); | ||
- `shift` (*remove value at front*). | ||
- `unshift` (*insert value at front*); | ||
|
||
To keep your implementation simple, the tests will not cover error conditions. | ||
Specifically: `pop` or `shift` will never be called on an empty list. | ||
|
||
Read more about [linked lists on Wikipedia][linked-lists]. | ||
|
||
[linked-lists]: https://en.wikipedia.org/wiki/Linked_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"authors": [ | ||
"ee7" | ||
], | ||
"files": { | ||
"solution": [ | ||
"linked_list.nim" | ||
], | ||
"test": [ | ||
"test_linked_list.nim" | ||
], | ||
"example": [ | ||
".meta/example.nim" | ||
] | ||
}, | ||
"blurb": "Implement a doubly linked list.", | ||
"source": "Classic computer science topic" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
type | ||
Node[T] = ref object ## A node of a doubly linked list. | ||
prev: Node[T] | ||
next: Node[T] | ||
val: T | ||
|
||
LinkedList*[T] = object ## A doubly linked list. | ||
head: Node[T] | ||
tail: Node[T] | ||
count: int | ||
|
||
func len*[T](list: LinkedList[T]): int = | ||
## Returns the number of nodes in `list`. | ||
list.count | ||
|
||
proc push*[T](list: var LinkedList[T], val: T) = | ||
## Appends a node with the given `val` to `list`. | ||
let node = Node[T](prev: list.tail, next: nil, val: val) | ||
if list.tail != nil: | ||
list.tail.next = node | ||
list.tail = node | ||
if list.head == nil: | ||
list.head = node | ||
inc list.count | ||
|
||
proc unshift*[T](list: var LinkedList[T], val: T) = | ||
## Prepends a node with the given `val` to `list`. | ||
let node = Node[T](prev: nil, next: list.head, val: val) | ||
if list.head != nil: | ||
list.head.prev = node | ||
list.head = node | ||
if list.tail == nil: | ||
list.tail = node | ||
inc list.count | ||
|
||
proc pop*[T](list: var LinkedList[T]): T = | ||
## Removes the final node of `list` and returns its value. | ||
if list.tail != nil: | ||
result = list.tail.val | ||
list.tail = list.tail.prev | ||
dec list.count | ||
|
||
proc shift*[T](list: var LinkedList[T]): T = | ||
## Removes the first node of `list` and returns its value. | ||
if list.head != nil: | ||
result = list.head.val | ||
list.head = list.head.next | ||
dec list.count | ||
|
||
iterator nodes[T](list: LinkedList[T]): Node[T] = | ||
## Yields every node of `list`. Supports removing a node during traversal. | ||
var node = list.head | ||
while node != nil: | ||
let tmp = node.next | ||
yield node | ||
node = tmp | ||
|
||
proc delete*[T](list: var LinkedList[T], val: T) = | ||
## Removes a node with value `val` from `list`. | ||
for node in list.nodes(): | ||
if node.val == val: | ||
if node.prev == nil: | ||
list.head = node.next | ||
else: | ||
node.prev.next = node.next | ||
if node.next == nil: | ||
list.tail = node.prev | ||
else: | ||
node.next.prev = node.prev | ||
dec list.count | ||
break |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# 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. | ||
|
||
[7f7e3987-b954-41b8-8084-99beca08752c] | ||
description = "pop gets element from the list" | ||
|
||
[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885] | ||
description = "push/pop respectively add/remove at the end of the list" | ||
|
||
[00ea24ce-4f5c-4432-abb4-cc6e85462657] | ||
description = "shift gets an element from the list" | ||
|
||
[37962ee0-3324-4a29-b588-5a4c861e6564] | ||
description = "shift gets first element from the list" | ||
|
||
[30a3586b-e9dc-43fb-9a73-2770cec2c718] | ||
description = "unshift adds element at start of the list" | ||
|
||
[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d] | ||
description = "pop, push, shift, and unshift can be used in any order" | ||
|
||
[88f65c0c-4532-4093-8295-2384fb2f37df] | ||
description = "count an empty list" | ||
|
||
[fc055689-5cbe-4cd9-b994-02e2abbb40a5] | ||
description = "count a list with items" | ||
|
||
[8272cef5-130d-40ea-b7f6-5ffd0790d650] | ||
description = "count is correct after mutation" | ||
|
||
[229b8f7a-bd8a-4798-b64f-0dc0bb356d95] | ||
description = "popping to empty doesn't break the list" | ||
|
||
[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad] | ||
description = "shifting to empty doesn't break the list" | ||
|
||
[e8f7c600-d597-4f79-949d-8ad8bae895a6] | ||
description = "deletes the only element" | ||
|
||
[fd65e422-51f3-45c0-9fd0-c33da638f89b] | ||
description = "deletes the element with the specified value from the list" | ||
|
||
[59db191a-b17f-4ab7-9c5c-60711ec1d013] | ||
description = "deletes the element with the specified value from the list, re-assigns tail" | ||
|
||
[58242222-5d39-415b-951d-8128247f8993] | ||
description = "deletes the element with the specified value from the list, re-assigns head" | ||
|
||
[ee3729ee-3405-4bd2-9bad-de0d4aa5d647] | ||
description = "deletes the first of two elements" | ||
|
||
[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb] | ||
description = "deletes the second of two elements" | ||
|
||
[7b420958-f285-4922-b8f9-10d9dcab5179] | ||
description = "delete does not modify the list if the element is not found" | ||
|
||
[7e04828f-6082-44e3-a059-201c63252a76] | ||
description = "deletes only the first occurrence" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
type | ||
LinkedList*[T] = object ## A doubly linked list. | ||
|
||
proc len*[T](list: LinkedList[T]): int = | ||
## Returns the number of nodes in `list`. | ||
discard | ||
|
||
proc push*[T](list: var LinkedList[T], val: T) = | ||
## Appends a node with the given `val` to `list`. | ||
discard | ||
|
||
proc pop*[T](list: var LinkedList[T]): T = | ||
## Removes the final node of `list` and returns its value. | ||
discard | ||
|
||
proc shift*[T](list: var LinkedList[T]): T = | ||
## Removes the first node of `list` and returns its value. | ||
discard | ||
|
||
proc unshift*[T](list: var LinkedList[T], val: T) = | ||
## Prepends a node with the given `val` to `list`. | ||
discard | ||
|
||
proc delete*[T](list: var LinkedList[T], val: T) = | ||
## Removes a node with value `val` from `list`. | ||
discard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import std/unittest | ||
import linked_list | ||
|
||
suite "Linked List": | ||
test "pop gets element from the list": | ||
var list = LinkedList[int]() | ||
list.push(7) | ||
check list.pop() == 7 | ||
|
||
test "push/pop respectively add/remove at the end of the list": | ||
var list = LinkedList[int]() | ||
list.push(11) | ||
list.push(13) | ||
check list.pop() == 13 | ||
check list.pop() == 11 | ||
|
||
test "shift gets an element from the list": | ||
var list = LinkedList[int]() | ||
list.push(17) | ||
check list.shift() == 17 | ||
|
||
test "shift gets first element from the list": | ||
var list = LinkedList[int]() | ||
list.push(23) | ||
list.push(5) | ||
check list.shift() == 23 | ||
check list.shift() == 5 | ||
|
||
test "unshift adds element at start of the list": | ||
var list = LinkedList[int]() | ||
list.unshift(23) | ||
list.unshift(5) | ||
check list.shift() == 5 | ||
check list.shift() == 23 | ||
|
||
test "pop, push, shift, and unshift can be used in any order": | ||
var list = LinkedList[int]() | ||
list.push(1) | ||
list.push(2) | ||
check list.pop() == 2 | ||
list.push(3) | ||
check list.shift() == 1 | ||
list.unshift(4) | ||
list.push(5) | ||
check list.shift() == 4 | ||
check list.pop() == 5 | ||
check list.shift() == 3 | ||
|
||
test "count an empty list": | ||
var list = LinkedList[int]() | ||
check list.len == 0 | ||
|
||
test "count a list with items": | ||
var list = LinkedList[int]() | ||
list.push(37) | ||
list.push(1) | ||
check list.len == 2 | ||
|
||
test "count is correct after mutation": | ||
var list = LinkedList[int]() | ||
list.push(31) | ||
check list.len == 1 | ||
list.unshift(43) | ||
check list.len == 2 | ||
discard list.shift() | ||
check list.len == 1 | ||
discard list.pop() | ||
check list.len == 0 | ||
|
||
test "popping to empty doesn't break the list": | ||
var list = LinkedList[int]() | ||
list.push(41) | ||
list.push(59) | ||
discard list.pop() | ||
discard list.pop() | ||
list.push(47) | ||
check list.len == 1 | ||
check list.pop() == 47 | ||
|
||
test "shifting to empty doesn't break the list": | ||
var list = LinkedList[int]() | ||
list.push(41) | ||
list.push(59) | ||
discard list.shift() | ||
discard list.shift() | ||
list.push(47) | ||
check list.len == 1 | ||
check list.shift() == 47 | ||
|
||
test "deletes the only element": | ||
var list = LinkedList[int]() | ||
list.push(61) | ||
list.delete(61) | ||
check list.len == 0 | ||
|
||
test "deletes the element with the specified value from the list": | ||
var list = LinkedList[int]() | ||
list.push(71) | ||
list.push(83) | ||
list.push(79) | ||
list.delete(83) | ||
check list.len == 2 | ||
check list.pop() == 79 | ||
check list.shift() == 71 | ||
|
||
test "deletes the element with the specified value from the list, re-assigns tail": | ||
var list = LinkedList[int]() | ||
list.push(71) | ||
list.push(83) | ||
list.push(79) | ||
list.delete(83) | ||
check list.len == 2 | ||
check list.pop() == 79 | ||
check list.pop() == 71 | ||
|
||
test "deletes the element with the specified value from the list, re-assigns head": | ||
var list = LinkedList[int]() | ||
list.push(71) | ||
list.push(83) | ||
list.push(79) | ||
list.delete(83) | ||
check list.len == 2 | ||
check list.shift() == 71 | ||
check list.shift() == 79 | ||
|
||
test "deletes the first of two elements": | ||
var list = LinkedList[int]() | ||
list.push(97) | ||
list.push(101) | ||
list.delete(97) | ||
check list.len == 1 | ||
check list.pop() == 101 | ||
|
||
test "deletes the second of two elements": | ||
var list = LinkedList[int]() | ||
list.push(97) | ||
list.push(101) | ||
list.delete(101) | ||
check list.len == 1 | ||
check list.pop() == 97 | ||
|
||
test "delete does not modify the list if the element is not found": | ||
var list = LinkedList[int]() | ||
list.push(89) | ||
list.delete(103) | ||
check list.len == 1 | ||
|
||
test "deletes only the first occurrence": | ||
var list = LinkedList[int]() | ||
list.push(73) | ||
list.push(9) | ||
list.push(9) | ||
list.push(107) | ||
list.delete(9) | ||
check list.len == 3 | ||
check list.pop() == 107 | ||
check list.pop() == 9 | ||
check list.pop() == 73 |