Skip to content

Commit 2a4af5d

Browse files
authored
Merge pull request kodecocodes#582 from newboadki/singlyLinkedListValueSemantics
[Swift 4] Singly Linked List with value semantics, copy-on-write and …
2 parents 7364263 + c76252a commit 2a4af5d

File tree

15 files changed

+2054
-0
lines changed

15 files changed

+2054
-0
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ script:
2828
- xcodebuild test -project ./Insertion\ Sort/Tests/Tests.xcodeproj -scheme Tests
2929
- xcodebuild test -project ./K-Means/Tests/Tests.xcodeproj -scheme Tests
3030
- xcodebuild test -project ./Linked\ List/Tests/Tests.xcodeproj -scheme Tests
31+
- xcodebuild test -project ./Singly\ Linked\ List/Tests/Tests.xcodeproj -scheme Tests
3132
- xcodebuild test -project ./Longest\ Common\ Subsequence/Tests/Tests.xcodeproj -scheme Tests
3233
- xcodebuild test -project ./Minimum\ Spanning\ Tree\ \(Unweighted\)/Tests/Tests.xcodeproj -scheme Tests
3334
- xcodebuild test -project ./Priority\ Queue/Tests/Tests.xcodeproj -scheme Tests
Loading
Loading

Singly Linked List/KeyValuePair.swift

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/// Conformers of this protocol represent a pair of comparable values
2+
/// This can be useful in many data structures and algorithms where items
3+
/// stored contain a value, but are ordered or retrieved according to a key.
4+
public protocol KeyValuePair : Comparable {
5+
6+
associatedtype K : Comparable, Hashable
7+
associatedtype V : Comparable, Hashable
8+
9+
// Identifier used in many algorithms to search by, order by, etc
10+
var key : K {get set}
11+
12+
// A data container
13+
var value : V {get set}
14+
15+
16+
/// Initializer
17+
///
18+
/// - Parameters:
19+
/// - key: Identifier used in many algorithms to search by, order by, etc.
20+
/// - value: A data container.
21+
init(key: K, value: V)
22+
23+
24+
/// Creates a copy
25+
///
26+
/// - Abstract: Conformers of this class can be either value or reference types.
27+
/// Some algorithms might need to guarantee that a conformer instance gets copied.
28+
/// This will perform an innecessary in the case of value types.
29+
/// TODO: is there a better way?
30+
/// - Returns: A new instance with the old one's values copied.
31+
func copy() -> Self
32+
}
33+
34+
35+
/// Conformance to Equatable and Comparable protocols
36+
extension KeyValuePair {
37+
38+
// MARK: - Equatable protocol
39+
public static func ==(lhs: Self, rhs: Self) -> Bool {
40+
return lhs.key == rhs.key
41+
}
42+
43+
44+
45+
// MARK: - Comparable protocol
46+
47+
public static func <(lhs: Self, rhs: Self) -> Bool {
48+
return lhs.key < rhs.key
49+
}
50+
51+
public static func <=(lhs: Self, rhs: Self) -> Bool {
52+
return lhs.key <= rhs.key
53+
}
54+
55+
public static func >=(lhs: Self, rhs: Self) -> Bool {
56+
return lhs.key >= rhs.key
57+
}
58+
59+
public static func >(lhs: Self, rhs: Self) -> Bool {
60+
return lhs.key > rhs.key
61+
}
62+
}
63+
64+
65+
66+
/// Concrete impletation of a KeyValuePair where both the key and the value
67+
/// are Integers.
68+
struct IntegerPair : KeyValuePair {
69+
70+
// MARK - KeyValuePair protocol
71+
var key : Int
72+
var value : Int
73+
74+
func copy() -> IntegerPair {
75+
return IntegerPair(key: self.key, value: self.value)
76+
}
77+
}

Singly Linked List/README.markdown

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Singly-Linked List
2+
3+
#### How is this different to the Linked List implementation?
4+
The existing Linked list implementation represents the same concept. However, the existing implementation has reference semantics and does not conform to the Collection protocol implemented in the Swift's standard Library. What SinglyLinkedList aims to contribute is a more idiomatic Swift implementation, that uses value semantics and copy-on-write as well as conforms to the collection protocol.
5+
6+
#### Conceptual representation
7+
A Singly linked list is a non-contiguous sequence of data items in memory. Each element links to the next via a memory reference. Additionally, this implementation keeps track of the last element, which can be retrived in order O(1). However, the list can only be traversed from head to tail.
8+
9+
+--------+ +--------+ +--------+ +--------+
10+
| | | | | | | |
11+
| node 0 |--->| node 1 |--->| node 2 |--->| node 3 |---> nil
12+
| | | | | | | |
13+
+--------+ +--------+ +--------+ +--------+
14+
^ ^
15+
| |
16+
Head Tail
17+
18+
Each element in the list is represented with an instance of SinglyLinkedListNode class, which basically contains some data and a reference of optional type to the next node, which means that the last node's next reference is `nil`.
19+
20+
#### In-memory representation
21+
In Swift, data types can have value or reference semantics. This implementation of a singly-linked list uses value semantics. Support for copy-on-write has been added in order to improve performance and delay copying the elements of the array until strictly necessary.
22+
23+
The image below shows how initially, after variable `l2` is assigned `l1`, a new instance of the struct SinglyLinkedList is created. Nevertheless, the indirect storage is still shared as indicated by the references that both l1 and l2 have pointing to a common area in memory.
24+
25+
![alt text](Images/SharedIndirectStorage.png "Two linked lists sharing the indirect storage")
26+
27+
Once a mutating operation happens --for example on `l2` to append a new element--, then the indirect storage and all nodes in the list is actually copied and the references from `l1` and `l2` are updated. This is ilustrated in the following figure.
28+
29+
![alt text](Images/CopiedIndirectStorage.png "A copy is created after editing one of the lists")
30+
31+
#### Implementation details
32+
1. Direct access to the tail in O(1) by keeping a reference that gets updated only when an operation to the list modifies the tail.
33+
2. Value semantics. This implementation of a singly-linked list uses a struct. When the list struct is assigned into another variable or passed as an argument to a function, a copy of the struct is made.
34+
3. Copy-on write. Instances of SinglyLinkedList have an internal reference to an indirect storage allocated in the heap. When a copy of the list is made (according to its value semantics) the indirect storage is initialy shared between the copies. This means that a potentially large list can be accessed to read values in it without an expensive copy having to take place. However, as soon as there is a write access to the indirect storage when there are more than one instances referencing to it, a copy will be performed to guarantee value semantics.
35+
4. Conformance to the Collection protocol.
36+
37+
38+
39+
40+
41+
## Performance of linked lists
42+
43+
EDITION
44+
- *append*: Appends a new node to the end of the list. This method will modify the list's tail. If the list is empty, it will also modify the head. This operation's time complexity is *O(1)* since there's a reference to the tail node in this implementation.
45+
- *prepend*: Inserts a new node at the start of the list. If the list is empty, it will also modify the head. This operation's time complexity is *O(1)* since there's a reference to the head node.
46+
- *delete*: Finds a node in the list and deletes it. This operation's time complexity has an upper bound described by a linear function; O(n).
47+
48+
SEARCH
49+
- find k-th to last element. Given a list with `n` number of elements and `k` being the passed parameter with `0` <= `k` <= `n`, this method has *O(k)*.
50+
51+
## Conforming to the Collection protocol
52+
Collections are sequences, therefore the first step is to conform to the Sequence protocol.
53+
54+
```
55+
extension SinglyLinkedList : Sequence
56+
{
57+
public func makeIterator() -> SinglyLinkedListForwardIterator<T>
58+
{
59+
return SinglyLinkedListForwardIterator(head: self.storage.head)
60+
}
61+
}
62+
```
63+
64+
We have used `SinglyLinkedListForwardIterator` an iterator class to keep track of the progress while iterating the structure:
65+
66+
```
67+
public struct SinglyLinkedListForwardIterator<T> : IteratorProtocol {
68+
69+
public typealias Element = T
70+
71+
private(set) var head: SinglyLinkedListNode<T>?
72+
73+
mutating public func next() -> T?
74+
{
75+
let result = self.head?.value
76+
self.head = self.head?.next
77+
return result
78+
}
79+
}
80+
```
81+
82+
Next, a collection needs to be indexable. Indexes are implemented by the data structure class. We have encapsulated this knowledge in instances of the class `SinglyLinkedListIndex`.
83+
84+
```
85+
public struct SinglyLinkedListIndex<T> : Comparable
86+
{
87+
fileprivate let node: SinglyLinkedListNode<T>?
88+
fileprivate let tag: Int
89+
90+
public static func==<T>(lhs: SinglyLinkedListIndex<T>, rhs: SinglyLinkedListIndex<T>) -> Bool {
91+
return (lhs.tag == rhs.tag)
92+
}
93+
94+
public static func< <T>(lhs: SinglyLinkedListIndex<T>, rhs: SinglyLinkedListIndex<T>) -> Bool {
95+
return (lhs.tag < rhs.tag)
96+
}
97+
}
98+
```
99+
100+
Finally, the methods in the collection to manipulate indexes are implemented below:
101+
- startIndex: Index of the first element. Can be calculated with O(1).
102+
- endIndex: Index that follows the last valid index in the collection. That is, the index that follows the tail's index. Can be calculated with O(1) if the count of elements is kept internally. However, the implementation below is O(n) with `n` being the number of elements in the list.
103+
104+
```
105+
extension SinglyLinkedList : Collection {
106+
107+
public typealias Index = SinglyLinkedListIndex<T>
108+
109+
public var startIndex: Index {
110+
get {
111+
return SinglyLinkedListIndex<T>(node: self.storage.head, tag: 0)
112+
}
113+
}
114+
115+
public var endIndex: Index {
116+
get {
117+
if let h = self.storage.head {
118+
let (_, numberOfElements) = findTail(in: h)
119+
return SinglyLinkedListIndex<T>(node: h, tag: numberOfElements)
120+
} else {
121+
return SinglyLinkedListIndex<T>(node: nil, tag: self.startIndex.tag)
122+
}
123+
}
124+
}
125+
126+
public subscript(position: Index) -> T {
127+
get {
128+
return position.node!.value
129+
}
130+
}
131+
132+
public func index(after idx: Index) -> Index {
133+
return SinglyLinkedListIndex<T>(node: idx.node?.next, tag: idx.tag+1)
134+
}
135+
}
136+
```
137+
138+
Conforming to the Collection protocol allows our class SinglyLinkedList to take adventage of all the collection methods included in the Stardard Library.
139+
140+
*Written by Borja Arias Drake*

0 commit comments

Comments
 (0)