Skip to content

Commit 18820b8

Browse files
authored
Linked list clean up. Add explainations to problems, fix titles and t… (#167)
1 parent 6be0c92 commit 18820b8

11 files changed

+88
-24
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Welcome to **Data Structures and Algorithms in Go**! 🎉 This project is design
4444
* [Join Two Sorted Linked Lists](./linkedlist/join_sorted_lists_test.go)
4545
* [Keep Repetitions](./linkedlist/keep_repetitions_test.go)
4646
* [Copy Linked List with Random Pointer](./linkedlist/copy_linklist_with_random_pointer_test.go)
47-
* [Implement LRU Cache](./linkedlist/lru_cache_test.go)
47+
* [LRU Cache](./linkedlist/lru_cache_test.go)
4848
* [Stacks](./stack/README.md)
4949
* [Max Stack](./stack/max_stack_test.go)
5050
* [Balancing Symbols](./stack/balancing_symbols_test.go)

Diff for: heap/merge_sorted_list_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestMergeSortedLists(t *testing.T) {
3030
for i, test := range tests {
3131
nodes := []*linkedlist.Node{}
3232
for _, sortedLinkedList := range test.sortedLinkedLists {
33-
nodes = append(nodes, linkedlist.Unserialize(sortedLinkedList))
33+
nodes = append(nodes, linkedlist.Deserialize(sortedLinkedList))
3434
}
3535
got := linkedlist.Serialize(MergeSortedLists(nodes))
3636
if got != test.merged {

Diff for: linkedlist/README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func addToFront(node *node) {
4444
}
4545
```
4646

47-
A pointer is typically stored for the first and last items in a singly linked list. Adding items to the front or back of the list is a constant-time operation. However, deleting the last item can be challenging, as the last item's pointer needs to be updated to the second-to-last item. This is where referencing the last item in each node proves useful. In contrast, doubly linked lists maintain pointers to the previous and next nodes, making deletion operations less expensive.
47+
A pointer is typically stored for the first and sometimes the last items in a singly linked list. Adding items to the front or back of the list is a constant-time operation. However, deleting the last item can be challenging, as the last item's pointer needs to be updated to the second-to-last item. This is where referencing the last item in each node proves useful. In contrast, doubly linked lists maintain pointers to the previous and next nodes, making deletion operations less expensive.
4848

4949
The Go standard library contains an implementation of [doubly linked lists](https://golang.org/pkg/container/list/). In the following example, numbers from 1 to 10 are added to the list. Even numbers are removed, and the resulting linked list containing odd numbers is printed.
5050

@@ -81,11 +81,11 @@ func main() {
8181

8282
Adding new items to the front or back of the linked list has the time complexity of O(1).
8383

84-
Deleting the first item is also O(1). Deleting the last item in a singly linked list is O(n), because the node before the last node must be found, and for that, every node must be visited. Deleting the last item can be done in O(1) time in a doubly linked list since nodes are connected to the previous and next nodes.
84+
Deleting the first item is also O(1). Deleting the last item in a singly linked list is O(n), because the node before the last node must be found, and for that, every node must be visited. Deleting the last item can be done in O(1) time in a doubly linked list since nodes are connected to the previous and next nodes, so we can simply find the node before the last node and remove its reference to the next node.
8585

8686
## Application
8787

88-
Linked lists can be useful where the order of items matters, especially if there is a need for flexible reordering of the items or having current, next, and previous items. Music players for example have playlists that play tracks in a predetermined order. At any given time, one track is playing while changing the current track with the previous or next ones is possible.
88+
Linked lists can be useful where the order of items matters, especially if there is a need for flexible reordering of the items or having current, next, and previous items. For example music players are a good candidate to implement using linked lists because they have playlists that play tracks in a predetermined order. At any given time, one track is playing while changing the current track with the previous or next ones is possible.
8989

9090
## Rehearsal
9191

@@ -94,4 +94,4 @@ Linked lists can be useful where the order of items matters, especially if there
9494
* [Join Two Sorted Linked Lists](./join_sorted_lists_test.go), [Solution](./join_sorted_lists.go)
9595
* [Keep Repetitions](./keep_repetitions_test.go), [Solution](./keep%20repetitions.go)
9696
* [Copy Linked List with Random Pointer](./copy_linklist_with_random_pointer_test.go), [Solution](./copy_linklist_with_random_pointer.go)
97-
* [Implement LRU Cache](./lru_cache_test.go), [Solution](./lru_cache.go)
97+
* [LRU Cache](./lru_cache_test.go), [Solution](./lru_cache.go)

Diff for: linkedlist/copy_linklist_with_random_pointer_test.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,23 @@ const (
1515
/*
1616
TestCopyLinkedListWithRandomPointer tests solution(s) with the following signature and problem description:
1717
18+
type RandomNode struct {
19+
Val int
20+
Next *RandomNode
21+
Random *RandomNode
22+
}
23+
1824
func CopyLinkedListWithRandomPointer(head *RandomNode) *RandomNode
1925
2026
Given a singly connected linked list in which each node may optionally be connected to another node in
2127
random order, return a deep copy of the linked list.
28+
29+
A deep copy of a linked list is a new linked list with the same values as the original linked list in
30+
which each node is a new node with a new memory address.
31+
32+
In the string representation below "1:nil->2:1->3:4->4:nil" is a linked list with 4 nodes:
33+
* Nodes 1,2,3,4 are sequentially connected to each other with the Next pointer.
34+
* Node 2 is randomly connected to node 1, and node 3 is randomly connected to node 4.
2235
*/
2336
func TestCopyLinkedListWithRandomPointer(t *testing.T) {
2437
tests := []struct {
@@ -33,7 +46,7 @@ func TestCopyLinkedListWithRandomPointer(t *testing.T) {
3346
}
3447

3548
for i, test := range tests {
36-
list := unserializeRandomNode(test.list)
49+
list := deserializeRandomNode(test.list)
3750
deepCopy := CopyLinkedListWithRandomPointer(list)
3851

3952
if list != nil {
@@ -89,7 +102,7 @@ func indicesMap(node *RandomNode) map[*RandomNode]int {
89102
return indices
90103
}
91104

92-
func unserializeRandomNode(stringRepresentation string) *RandomNode {
105+
func deserializeRandomNode(stringRepresentation string) *RandomNode {
93106
if stringRepresentation == "" {
94107
return nil
95108
}

Diff for: linkedlist/join_sorted_lists_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ TestJoinTwoSortedLinkedLists tests solution(s) with the following signature and
88
func JoinTwoSortedLinkedLists(l1, l2 *Node) *Node
99
1010
Given two sorted linked lists of integers, merge them into one sorted linked list.
11+
12+
For example if given 1->4->6 and 2->3->5->7, the output should be 1->2->3->4->5->6->7.
1113
*/
1214
func TestJoinTwoSortedLinkedLists(t *testing.T) {
1315
tests := []struct {
@@ -26,7 +28,7 @@ func TestJoinTwoSortedLinkedLists(t *testing.T) {
2628
}
2729

2830
for i, test := range tests {
29-
got := Serialize(JoinTwoSortedLinkedLists(Unserialize(test.list1), Unserialize(test.list2)))
31+
got := Serialize(JoinTwoSortedLinkedLists(Deserialize(test.list1), Deserialize(test.list2)))
3032
if got != test.joined {
3133
t.Fatalf("Failed test case #%d. Want %s got %s", i, test.joined, got)
3234
}

Diff for: linkedlist/keep_repetitions_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ TestKeepRepetitions tests solution(s) with the following signature and problem d
99
1010
Given a linked list of sorted integers, create a copy of the list that contains one example of
1111
each repeated item.
12+
13+
For example if the linked list is 1->1->4->4->6->6->7, the output should be 1->4->6 because
14+
1,4,6 are items that are repeated in this list and 7 is not repeated.
1215
*/
1316
func TestKeepRepetitions(t *testing.T) {
1417
tests := []struct {
@@ -21,11 +24,12 @@ func TestKeepRepetitions(t *testing.T) {
2124
{"1->4->4->4->6", "4"},
2225
{"1->1->4->4->4->6", "1->4"},
2326
{"1->1->4->4->4->6->6", "1->4->6"},
27+
{"1->1->4->4->6->6->7", "1->4->6"},
2428
{"1->1->4->4->4->6->7->7", "1->4->7"},
2529
}
2630

2731
for i, test := range tests {
28-
got := Serialize(KeepRepetitions(Unserialize(test.list)))
32+
got := Serialize(KeepRepetitions(Deserialize(test.list)))
2933
if got != test.solution {
3034
t.Fatalf("Failed test case #%d. Want %s got %s", i, test.solution, got)
3135
}

Diff for: linkedlist/lru_cache.go

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package linkedlist
22

33
import (
44
"container/list"
5+
"fmt"
6+
"strings"
57
)
68

79
type (
@@ -50,3 +52,12 @@ func (cache *lruCache) put(key int, value int) {
5052
cache.list.Remove(cache.list.Front())
5153
}
5254
}
55+
56+
// String represents the cache as a string.
57+
func (cache *lruCache) String() string {
58+
var pairs []string
59+
for e := cache.list.Front(); e != nil; e = e.Next() {
60+
pairs = append(pairs, fmt.Sprintf("%d:%d", e.Value.(*element).key, e.Value.(*element).value))
61+
}
62+
return strings.Join(pairs, ",")
63+
}

Diff for: linkedlist/lru_cache_test.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,30 @@ import "testing"
66
TestLRU tests solution(s) with the following signature and problem description:
77
88
func get(key int) int
9-
func put(key int, value int) {
9+
func put(key int, value int)
1010
11-
Implement a least recently used cache with integer keys and values, where the
12-
least recently used evicted upon insertion when cache is at full capacity.
11+
Implement a least recently used cache with integer keys and values, where the least
12+
recently used key is evicted upon insertion when the cache is at full capacity.
13+
14+
For example if we have a cache with capacity 2, and we perform:
15+
16+
put(0, 1) // put key 0 with value 1
17+
put(1, 2)
18+
get(1) // get value with key 1
19+
put(2, 3)
20+
21+
When putting 2,3, the cache is at capacity and it needs to evict. Since we used key 1 by
22+
getting it, the [1,2] key value pair which is the least used value will be evicted.
23+
What remains in the cache
24+
is [1:2] and [2:3].
1325
*/
1426
func TestLRU(t *testing.T) {
1527
tests := []struct {
1628
capacity int
1729
puts []int // n(i) element is key, n(i+1) element is value
1830
gets []int // n(i) element is the key to get, n(i+1) element is the expected value
1931
}{
20-
{1, []int{}, []int{1, -1}},
32+
{1, []int{}, []int{1, -1}}, // -1 means not found
2133
{1, []int{2, 1, 1, 1, 2, 3, 4, 1}, []int{2, -1, 4, 1}},
2234
{2, []int{2, 1, 1, 1, 2, 3, 4, 1}, []int{1, -1, 2, 3, 4, 1}},
2335
{3, []int{2, 1, 1, 1, 2, 3, 4, 1}, []int{1, 1, 2, 3, 4, 1}},
@@ -38,4 +50,16 @@ func TestLRU(t *testing.T) {
3850
}
3951
}
4052
}
53+
54+
lru := newLRU(2)
55+
lru.put(0, 1)
56+
lru.put(1, 2)
57+
lru.get(1)
58+
lru.put(2, 3)
59+
60+
got := lru.String()
61+
want := "1:2,2:3"
62+
if got != want {
63+
t.Fatalf("want %s, got %s", want, got)
64+
}
4165
}

Diff for: linkedlist/reverse_in_place_test.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ import "testing"
55
/*
66
TestReverseLinkedList tests solution(s) with the following signature and problem description:
77
8+
type Node struct {
9+
Val int
10+
Next *Node
11+
}
12+
813
func ReverseLinkedList(head *Node) *Node
914
10-
Reverse a given linked list in place.
15+
Reverse a given linked list in place. For example if the linked list is 1->2->3 return 3->2->1.
1116
*/
1217
func TestReverseLinkedList(t *testing.T) {
1318
tests := []struct {
@@ -20,7 +25,7 @@ func TestReverseLinkedList(t *testing.T) {
2025
}
2126

2227
for i, test := range tests {
23-
got := Serialize(ReverseLinkedList(Unserialize(test.list)))
28+
got := Serialize(ReverseLinkedList(Deserialize(test.list)))
2429
if got != test.reversed {
2530
t.Fatalf("Failed test case #%d. Want %s got %s", i, test.reversed, got)
2631
}

Diff for: linkedlist/serialization.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func Serialize(node *Node) string {
3939
return strings.TrimSuffix(output, separator)
4040
}
4141

42-
// Unserialize solves the problem in O(n) time and O(1) space.
43-
func Unserialize(stringRepresentation string) *Node {
42+
// Deserialize solves the problem in O(n) time and O(1) space.
43+
func Deserialize(stringRepresentation string) *Node {
4444
if stringRepresentation == "" {
4545
return nil
4646
}

Diff for: linkedlist/serialization_test.go

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@ package linkedlist
33
import "testing"
44

55
/*
6-
TestSerializeAndUnserializeLinkedList tests solution(s) with the following signature and problem description:
6+
TestSerializeAndDeserializeLinkedList tests solution(s) with the following signature and problem description:
7+
8+
type Node struct {
9+
Val int
10+
Next *Node
11+
}
712
813
func Serialize(node *Node) string
9-
func Unserialize(stringRepresentation string) *Node
14+
func Deserialize(stringRepresentation string) *Node
1015
11-
Write a function that turns a linked list into a string representation, and then a function that turns that
12-
string representation to an actual linked list.
16+
Write a function that turns a linked list into a string representation (Serialize), and then a function
17+
that turns that string representation to an actual linked list (Deserialize).
1318
*/
14-
func TestSerializeAndUnserializeLinkedList(t *testing.T) {
19+
func TestSerializeAndDeserializeLinkedList(t *testing.T) {
1520
tests := []string{
1621
"",
1722
"1",
1823
"1->2",
1924
"1->2->3->4->2->1",
2025
}
2126
for i, test := range tests {
22-
got := Serialize(Unserialize(test))
27+
got := Serialize(Deserialize(test))
2328
if got != test {
2429
t.Fatalf("Failed test case #%d. Want %#v got %#v", i, test, got)
2530
}

0 commit comments

Comments
 (0)