forked from igorwojda/kotlin-coding-challenges
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Solution.kt
159 lines (120 loc) · 3.88 KB
/
Solution.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package com.igorwojda.queue.basic
/*
Linked List based implementation
Time complexity:
Insertion: O(1)
Removal: O(1)
Searching: O(n)
Access: O(n)
*/
private object Solution1 {
private class Queue<E> {
var size = 0
private set
private var first: Node<E>? = null
private var last: Node<E>? = null
fun add(element: E) {
val node = Node(element)
if (first == null) {
first = node
} else {
last?.next = node
}
size++
last = node
}
fun remove(): E? {
if (size == 0) return null
val node = first
first = node?.next
size--
return node?.data
}
fun peek() = first?.data
fun isEmpty() = first == null
}
private data class Node<E>(val data: E, var next: Node<E>? = null)
}
/*
List based implementation
It's important to notice that adding element to the beginning of the array or removing element from the beginning is
expensive operation (all subsequent items have to be re-indexed):
Option A - add to the end, remove from the beginning:
Insert at the end O(1)
Remove at the end O(1)
Option B - add to the beginning, remove from the end:
Bad idea to add elements at the beginning:
Insert at the beginning O(n)
Remove at the beginning O(n) - we have to re-index all the other elements in the list
If we would add new element to the beginning of the list (expensive), we would have to remove them from the end of the
list (cheap). If we would add new element to the end of the array (cheap) we would have to remove ti from the
beginning (expensive). Because of that the list based implementation can't be efficient. We could use linked list
based implementation instead.
Time complexity (add at the beginning and remove from the end):
Insertion: O(n)
Removal: O(1)
Searching: O(n)
Access: O(n)
Solution time complexity (add at the end and remove from the beginning):
Insertion: O(1)
Removal: O(n)
Searching: O(n)
Access: O(n)
*/
private object Solution2 {
private class Queue<E> {
private val list = mutableListOf<E>()
fun add(element: E) {
list.add(element)
}
fun remove() = if (list.isEmpty()) null else list.removeAt(0)
fun peek() = list.firstOrNull()
fun isEmpty() = list.isEmpty()
val size get() = list.size
}
}
// Two Stack based implementation
private object Solution3 {
private class Queue<E> {
val primaryStack = Stack<E>()
val temporaryStack = Stack<E>()
val size get() = primaryStack.size
fun add(element: E) {
primaryStack.add(element)
val a = 2
}
fun remove(): E? {
moveElementsToTemporaryStack()
val result = temporaryStack.remove()
moveElementsToPrimaryStack()
return result
}
fun peek(): E? {
moveElementsToTemporaryStack()
val result = temporaryStack.peek()
moveElementsToPrimaryStack()
return result
}
private fun moveElementsToTemporaryStack() {
while (primaryStack.peek() != null) {
primaryStack.remove()?.let { temporaryStack.add(it) }
}
}
private fun moveElementsToPrimaryStack() {
while (temporaryStack.peek() != null) {
temporaryStack.remove()?.let { primaryStack.add(it) }
}
}
fun isEmpty() = primaryStack.isEmpty()
}
private class Stack<E> {
private val list = mutableListOf<E>()
val size get() = list.size
fun add(element: E) {
list.add(element)
}
fun remove() = if (list.isEmpty()) null else list.removeAt(list.lastIndex)
fun peek() = list.lastOrNull()
fun isEmpty() = list.isEmpty()
}
}