├── .gitignore └── src ├── array └── Array.kt ├── graph ├── AdjacencyList.kt ├── AdjacencyMatrix.kt ├── BellmanFordAlgorithm.kt ├── BreadthFirstSearch.kt ├── DepthFirstSearch.kt ├── DijkstraAlgorithm.kt ├── FloydWarshallAlgorithm.kt ├── KruskalAlgorithm.kt ├── PrimAlgorithm.kt ├── TopologicalSorting.kt └── UnionFind.kt ├── hash └── SeparateChainingHashMap.kt ├── heap ├── MaxHeap.kt └── MinHeap.kt ├── search ├── BinarySearch.kt └── ParametricSearch.kt ├── sort ├── BubbleSort.kt ├── CoutingSort.kt ├── InsertionSort.kt ├── MergeSort.kt ├── QuickSort.kt ├── RadixSort.kt └── SelectionSort.kt ├── text ├── KmpAlgorithm.kt ├── PatternMatchingAlgorithm.kt └── Trie.kt └── tree ├── BinaryTree.kt ├── InOrderTraversal.kt ├── LevelOrderTraversal.kt ├── PostOrderTraversal.kt └── PreOrderTraversal.kt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/macos,windows,java,kotlin,git,vim,androidstudi o,intellij,jetbrains 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,java,kotlin,git,vim,androidstudi o,intellij,jetbrains 3 | 4 | #!! ERROR: androidstudi o is undefined. Use list command to see defined gitignore types !!# 5 | 6 | ### Git ### 7 | # Created by git for backups. To disable backups in Git: 8 | # $ git config --global mergetool.keepBackup false 9 | *.orig 10 | 11 | # Created by git when using merge tools for conflicts 12 | *.BACKUP.* 13 | *.BASE.* 14 | *.LOCAL.* 15 | *.REMOTE.* 16 | *_BACKUP_*.txt 17 | *_BASE_*.txt 18 | *_LOCAL_*.txt 19 | *_REMOTE_*.txt 20 | 21 | ### Intellij ### 22 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 23 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 24 | 25 | # User-specific stuff 26 | .idea/**/workspace.xml 27 | .idea/**/tasks.xml 28 | .idea/**/usage.statistics.xml 29 | .idea/**/dictionaries 30 | .idea/**/shelf 31 | 32 | # AWS User-specific 33 | .idea/**/aws.xml 34 | 35 | # Generated files 36 | .idea/**/contentModel.xml 37 | 38 | # Sensitive or high-churn files 39 | .idea/**/dataSources/ 40 | .idea/**/dataSources.ids 41 | .idea/**/dataSources.local.xml 42 | .idea/**/sqlDataSources.xml 43 | .idea/**/dynamic.xml 44 | .idea/**/uiDesigner.xml 45 | .idea/**/dbnavigator.xml 46 | 47 | # Gradle 48 | .idea/**/gradle.xml 49 | .idea/**/libraries 50 | 51 | # Gradle and Maven with auto-import 52 | # When using Gradle or Maven with auto-import, you should exclude module files, 53 | # since they will be recreated, and may cause churn. Uncomment if using 54 | # auto-import. 55 | # .idea/artifacts 56 | # .idea/compiler.xml 57 | # .idea/jarRepositories.xml 58 | # .idea/modules.xml 59 | # .idea/*.iml 60 | # .idea/modules 61 | # *.iml 62 | # *.ipr 63 | 64 | # CMake 65 | cmake-build-*/ 66 | 67 | # Mongo Explorer plugin 68 | .idea/**/mongoSettings.xml 69 | 70 | # File-based project format 71 | *.iws 72 | 73 | # IntelliJ 74 | out/ 75 | 76 | # mpeltonen/sbt-idea plugin 77 | .idea_modules/ 78 | 79 | # JIRA plugin 80 | atlassian-ide-plugin.xml 81 | 82 | # Cursive Clojure plugin 83 | .idea/replstate.xml 84 | 85 | # SonarLint plugin 86 | .idea/sonarlint/ 87 | 88 | # Crashlytics plugin (for Android Studio and IntelliJ) 89 | com_crashlytics_export_strings.xml 90 | crashlytics.properties 91 | crashlytics-build.properties 92 | fabric.properties 93 | 94 | # Editor-based Rest Client 95 | .idea/httpRequests 96 | 97 | # Android studio 3.1+ serialized cache file 98 | .idea/caches/build_file_checksums.ser 99 | 100 | ### Intellij Patch ### 101 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 102 | 103 | # *.iml 104 | # modules.xml 105 | # .idea/misc.xml 106 | # *.ipr 107 | 108 | # Sonarlint plugin 109 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 110 | .idea/**/sonarlint/ 111 | 112 | # SonarQube Plugin 113 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 114 | .idea/**/sonarIssues.xml 115 | 116 | # Markdown Navigator plugin 117 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 118 | .idea/**/markdown-navigator.xml 119 | .idea/**/markdown-navigator-enh.xml 120 | .idea/**/markdown-navigator/ 121 | 122 | # Cache file creation bug 123 | # See https://youtrack.jetbrains.com/issue/JBR-2257 124 | .idea/$CACHE_FILE$ 125 | 126 | # CodeStream plugin 127 | # https://plugins.jetbrains.com/plugin/12206-codestream 128 | .idea/codestream.xml 129 | 130 | # Azure Toolkit for IntelliJ plugin 131 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 132 | .idea/**/azureSettings.xml 133 | 134 | ### Java ### 135 | # Compiled class file 136 | *.class 137 | 138 | # Log file 139 | *.log 140 | 141 | # BlueJ files 142 | *.ctxt 143 | 144 | # Mobile Tools for Java (J2ME) 145 | .mtj.tmp/ 146 | 147 | # Package Files # 148 | *.war 149 | *.nar 150 | *.ear 151 | *.zip 152 | *.tar.gz 153 | *.rar 154 | 155 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 156 | hs_err_pid* 157 | replay_pid* 158 | 159 | ### JetBrains ### 160 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 161 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 162 | 163 | # User-specific stuff 164 | 165 | # AWS User-specific 166 | 167 | # Generated files 168 | 169 | # Sensitive or high-churn files 170 | 171 | # Gradle 172 | 173 | # Gradle and Maven with auto-import 174 | # When using Gradle or Maven with auto-import, you should exclude module files, 175 | # since they will be recreated, and may cause churn. Uncomment if using 176 | # auto-import. 177 | # .idea/artifacts 178 | # .idea/compiler.xml 179 | # .idea/jarRepositories.xml 180 | # .idea/modules.xml 181 | # .idea/*.iml 182 | # .idea/modules 183 | # *.iml 184 | # *.ipr 185 | 186 | # CMake 187 | 188 | # Mongo Explorer plugin 189 | 190 | # File-based project format 191 | 192 | # IntelliJ 193 | 194 | # mpeltonen/sbt-idea plugin 195 | 196 | # JIRA plugin 197 | 198 | # Cursive Clojure plugin 199 | 200 | # SonarLint plugin 201 | 202 | # Crashlytics plugin (for Android Studio and IntelliJ) 203 | 204 | # Editor-based Rest Client 205 | 206 | # Android studio 3.1+ serialized cache file 207 | 208 | ### JetBrains Patch ### 209 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 210 | 211 | # *.iml 212 | # modules.xml 213 | # .idea/misc.xml 214 | # *.ipr 215 | 216 | # Sonarlint plugin 217 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 218 | 219 | # SonarQube Plugin 220 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 221 | 222 | # Markdown Navigator plugin 223 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 224 | 225 | # Cache file creation bug 226 | # See https://youtrack.jetbrains.com/issue/JBR-2257 227 | 228 | # CodeStream plugin 229 | # https://plugins.jetbrains.com/plugin/12206-codestream 230 | 231 | # Azure Toolkit for IntelliJ plugin 232 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 233 | 234 | ### Kotlin ### 235 | # Compiled class file 236 | 237 | # Log file 238 | 239 | # BlueJ files 240 | 241 | # Mobile Tools for Java (J2ME) 242 | 243 | # Package Files # 244 | 245 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 246 | 247 | ### macOS ### 248 | # General 249 | .DS_Store 250 | .AppleDouble 251 | .LSOverride 252 | 253 | # Icon must end with two \r 254 | Icon 255 | 256 | 257 | # Thumbnails 258 | ._* 259 | 260 | # Files that might appear in the root of a volume 261 | .DocumentRevisions-V100 262 | .fseventsd 263 | .Spotlight-V100 264 | .TemporaryItems 265 | .Trashes 266 | .VolumeIcon.icns 267 | .com.apple.timemachine.donotpresent 268 | 269 | # Directories potentially created on remote AFP share 270 | .AppleDB 271 | .AppleDesktop 272 | Network Trash Folder 273 | Temporary Items 274 | .apdisk 275 | 276 | ### macOS Patch ### 277 | # iCloud generated files 278 | *.icloud 279 | 280 | ### Vim ### 281 | # Swap 282 | [._]*.s[a-v][a-z] 283 | !*.svg # comment out if you don't need vector files 284 | [._]*.sw[a-p] 285 | [._]s[a-rt-v][a-z] 286 | [._]ss[a-gi-z] 287 | [._]sw[a-p] 288 | 289 | # Session 290 | Session.vim 291 | Sessionx.vim 292 | 293 | # Temporary 294 | .netrwhist 295 | *~ 296 | # Auto-generated tag files 297 | tags 298 | # Persistent undo 299 | [._]*.un~ 300 | 301 | ### Windows ### 302 | # Windows thumbnail cache files 303 | Thumbs.db 304 | Thumbs.db:encryptable 305 | ehthumbs.db 306 | ehthumbs_vista.db 307 | 308 | # Dump file 309 | *.stackdump 310 | 311 | # Folder config file 312 | [Dd]esktop.ini 313 | 314 | # Recycle Bin used on file shares 315 | $RECYCLE.BIN/ 316 | 317 | # Windows Installer files 318 | *.cab 319 | *.msi 320 | *.msix 321 | *.msm 322 | *.msp 323 | 324 | # Windows shortcuts 325 | *.lnk 326 | 327 | .idea/* 328 | *.iml 329 | 330 | # End of https://www.toptal.com/developers/gitignore/api/macos,windows,java,kotlin,git,vim,androidstudi o,intellij,jetbrains 331 | -------------------------------------------------------------------------------- /src/array/Array.kt: -------------------------------------------------------------------------------- 1 | package array 2 | 3 | /** 4 | * Time Complexity: 5 | * - Get: O(1) 6 | * - Add, Remove: O(n) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | val arr = IntArray(size = 10) { it } 11 | println("arr=${arr.joinToString()}") 12 | 13 | arr.add(i = 5, num = 100) 14 | println("arr=${arr.joinToString()}") 15 | 16 | arr.remove(i = 5) 17 | println("arr=${arr.joinToString()}") 18 | } 19 | 20 | private fun IntArray.add(i: Int, num: Int) { 21 | for (j in lastIndex - 1 downTo i) this[j + 1] = this[j] 22 | this[i] = num 23 | } 24 | 25 | private fun IntArray.remove(i: Int) { 26 | for (j in i + 1..lastIndex) this[j - 1] = this[j] 27 | } 28 | -------------------------------------------------------------------------------- /src/graph/AdjacencyList.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * Time Complexity: 7 | * - Add: O(1) 8 | * - Search, Remove: O(d(v)) (d = degree, v = vertex) 9 | * Space Complexity: O(v+e) (v = vertex, e = edge) 10 | */ 11 | fun main() { 12 | val weightBetween = Array(size = 10) { LinkedList>() } 13 | .apply { 14 | addBiDirection(u = 1, v = 2, w = 1) 15 | addBiDirection(u = 2, v = 3, w = 2) 16 | addBiDirection(u = 3, v = 7, w = 5) 17 | addBiDirection(u = 3, v = 9, w = 3) 18 | } 19 | 20 | for (u in weightBetween.indices) { 21 | weightBetween[u].forEach { (v, weight) -> 22 | if (weight < Int.MAX_VALUE) println("$u > $v : $weight") 23 | } 24 | } 25 | } 26 | 27 | private fun Array>>.addBiDirection(u: Int, v: Int, w: Int) { 28 | this[u].add(v to w) 29 | this[v].add(u to w) 30 | } 31 | -------------------------------------------------------------------------------- /src/graph/AdjacencyMatrix.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | /** 4 | * Time Complexity: O(1) 5 | * Space Complexity: O(v^2) (v = vertex) 6 | */ 7 | fun main() { 8 | val weightBetween = Array(size = 10) { IntArray(size = 10) { Int.MAX_VALUE } } 9 | .apply { 10 | addBiDirection(u = 1, v = 2, w = 1) 11 | addBiDirection(u = 2, v = 3, w = 2) 12 | addBiDirection(u = 3, v = 7, w = 5) 13 | addBiDirection(u = 3, v = 9, w = 3) 14 | } 15 | 16 | for (u in weightBetween.indices) { 17 | for (v in weightBetween[u].indices) { 18 | val weight = weightBetween[u][v] 19 | if (weight < Int.MAX_VALUE) println("$u > $v : $weight") 20 | } 21 | } 22 | } 23 | 24 | private fun Array.addBiDirection(u: Int, v: Int, w: Int) { 25 | this[u][v] = w 26 | this[v][u] = w 27 | } 28 | -------------------------------------------------------------------------------- /src/graph/BellmanFordAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | private const val INF: Int = Int.MAX_VALUE shr 1 4 | 5 | /** 6 | * Time Complexity: O(v*e) (v = vertex, e = edge) 7 | * Space Complexity: O(e) (e = edge) 8 | */ 9 | fun main() { 10 | val edges = arrayOf( 11 | Triple(first = 0, second = 1, third = 4), 12 | Triple(first = 0, second = 2, third = 3), 13 | Triple(first = 1, second = 2, third = -2), 14 | Triple(first = 1, second = 3, third = 1), 15 | Triple(first = 2, second = 3, third = 5), 16 | Triple(first = 2, second = 4, third = 2), 17 | Triple(first = 4, second = 3, third = -6) 18 | ) 19 | 20 | val minDistanceTo = IntArray(size = 5) { INF }.apply { this[0] = 0 } 21 | repeat(times = 5) { 22 | edges.forEach { (u, v, distance) -> 23 | if (minDistanceTo[u] != INF && minDistanceTo[u] + distance < minDistanceTo[v]) { 24 | minDistanceTo[v] = minDistanceTo[u] + distance 25 | } 26 | } 27 | } 28 | 29 | edges.forEach { (u, v, distance) -> 30 | if (minDistanceTo[u] != INF && minDistanceTo[u] + distance < minDistanceTo[v]) { 31 | return println("cycle") 32 | } 33 | } 34 | 35 | println(minDistanceTo.joinToString()) 36 | } 37 | -------------------------------------------------------------------------------- /src/graph/BreadthFirstSearch.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * Time Complexity: 7 | * - Adjacency List: O(v+e) (v = vertex, e = edge) 8 | * - Adjacency Matrix: O(v^2) (v = vertex) 9 | * 10 | * Space Complexity: 11 | * - Adjacency List: O(v+e) (v = vertex, e = edge) 12 | * - Adjacency Matrix: O(v^2) (v = vertex) 13 | */ 14 | fun main() { 15 | val weightBetween = Array(size = 10) { LinkedList>() } 16 | .apply { 17 | addBiDirection(u = 1, v = 2, w = 1) 18 | addBiDirection(u = 2, v = 3, w = 2) 19 | addBiDirection(u = 3, v = 7, w = 5) 20 | addBiDirection(u = 7, v = 5, w = 9) 21 | addBiDirection(u = 3, v = 9, w = 3) 22 | } 23 | 24 | val isVisited = BooleanArray(size = 10).apply { this[1] = true } 25 | val nextNodes = LinkedList>().apply { add(1 to 0) } 26 | while (nextNodes.isNotEmpty()) { 27 | val (u, weight) = nextNodes.poll() 28 | println("$u, $weight") 29 | 30 | weightBetween[u].forEach { (v, weight) -> 31 | if (isVisited[v]) return@forEach 32 | 33 | isVisited[v] = true 34 | nextNodes.offer(v to weight) 35 | } 36 | } 37 | } 38 | 39 | private fun Array>>.addBiDirection(u: Int, v: Int, w: Int) { 40 | this[u].add(v to w) 41 | this[v].add(u to w) 42 | } 43 | -------------------------------------------------------------------------------- /src/graph/DepthFirstSearch.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | 5 | private val weightBetween: Array>> = 6 | Array(size = 10) { LinkedList>() } 7 | private val isVisited: BooleanArray = BooleanArray(size = 10) 8 | 9 | /** 10 | * Time Complexity: 11 | * - Adjacency List: O(v+e) (v = vertex, e = edge) 12 | * - Adjacency Matrix: O(v^2) (v = vertex) 13 | * 14 | * Space Complexity: 15 | * - Adjacency List: O(v+e) (v = vertex, e = edge) 16 | * - Adjacency Matrix: O(v^2) (v = vertex) 17 | */ 18 | fun main() { 19 | weightBetween.apply { 20 | addBiDirection(u = 1, v = 2, w = 1) 21 | addBiDirection(u = 2, v = 3, w = 2) 22 | addBiDirection(u = 3, v = 7, w = 5) 23 | addBiDirection(u = 7, v = 5, w = 9) 24 | addBiDirection(u = 3, v = 9, w = 3) 25 | } 26 | 27 | isVisited[1] = true 28 | dfs(u = 1, weight = 0) 29 | } 30 | 31 | private fun Array>>.addBiDirection(u: Int, v: Int, w: Int) { 32 | this[u].add(v to w) 33 | this[v].add(u to w) 34 | } 35 | 36 | private fun dfs(u: Int, weight: Int) { 37 | println("$u, $weight") 38 | 39 | weightBetween[u].forEach { (v, weight) -> 40 | if (isVisited[v]) return@forEach 41 | 42 | isVisited[v] = true 43 | dfs(v, weight) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/graph/DijkstraAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | import java.util.PriorityQueue 5 | 6 | /** 7 | * Time Complexity: O(elogv) (v = vertex, e = edge) 8 | * Space Complexity: O(v+e) (v = vertex, e = edge) 9 | */ 10 | fun main() { 11 | val adjacencyList = Array(size = 7) { LinkedList>() }.apply { 12 | this[1].add(2 to 3) 13 | this[1].add(3 to 2) 14 | this[1].add(4 to 5) 15 | this[2].add(3 to 2) 16 | this[2].add(5 to 8) 17 | this[3].add(4 to 2) 18 | this[4].add(5 to 6) 19 | this[5].add(6 to 1) 20 | } 21 | 22 | val minDistanceTo = IntArray(size = 7) { Int.MAX_VALUE }.apply { this[1] = 0 } 23 | val nextNodes = PriorityQueue>(compareBy { it.second }).apply { offer(1 to 0) } 24 | while (nextNodes.isNotEmpty()) { 25 | val (u, distance) = nextNodes.poll() 26 | if (minDistanceTo[u] < distance) continue 27 | 28 | adjacencyList[u].forEach { (v, distance) -> 29 | val distanceToV = minDistanceTo[u] + distance 30 | if (minDistanceTo[v] <= distanceToV) return@forEach 31 | 32 | minDistanceTo[v] = distanceToV 33 | nextNodes.offer(v to distanceToV) 34 | } 35 | } 36 | 37 | println(minDistanceTo.joinToString()) 38 | } 39 | -------------------------------------------------------------------------------- /src/graph/FloydWarshallAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | private const val INF: Int = 999_999 4 | 5 | /** 6 | * Time Complexity: O(v^3) (v = vertex) 7 | * Space Complexity: O(v^2) (v = vertex) 8 | */ 9 | fun main() { 10 | val adjacencyMatrix = Array(size = 6) { IntArray(size = 6) { INF } }.apply { 11 | addBiDirection(u = 1, v = 1, w = 0) 12 | addBiDirection(u = 1, v = 2, w = 4) 13 | addBiDirection(u = 1, v = 3, w = 1) 14 | addBiDirection(u = 1, v = 4, w = 1) 15 | addBiDirection(u = 2, v = 2, w = 0) 16 | addBiDirection(u = 2, v = 5, w = 8) 17 | addBiDirection(u = 3, v = 3, w = 0) 18 | addBiDirection(u = 3, v = 4, w = 3) 19 | addBiDirection(u = 3, v = 5, w = 15) 20 | addBiDirection(u = 4, v = 4, w = 0) 21 | addBiDirection(u = 4, v = 5, w = 6) 22 | addBiDirection(u = 5, v = 5, w = 0) 23 | } 24 | print(adjacencyMatrix) 25 | 26 | for (v in 1..5) { 27 | for (u in 1..5) { 28 | for (t in 1..5) { 29 | adjacencyMatrix[u][t] = minOf( 30 | adjacencyMatrix[u][t], 31 | b = adjacencyMatrix[u][v] + adjacencyMatrix[v][t] 32 | ) 33 | } 34 | } 35 | } 36 | print(adjacencyMatrix) 37 | } 38 | 39 | private fun Array.addBiDirection(u: Int, v: Int, w: Int) { 40 | this[u][v] = w 41 | this[v][u] = w 42 | } 43 | 44 | private fun print(adjacencyMatrix: Array) { 45 | val text = buildString { 46 | for (u in 1..5) { 47 | for (v in 1..5) { 48 | append(adjacencyMatrix[u][v], ' ') 49 | } 50 | appendLine() 51 | } 52 | } 53 | 54 | System.out.bufferedWriter().use { 55 | it.write(text) 56 | it.flush() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/graph/KruskalAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | private val parentOf: IntArray = IntArray(size = 6) { it } 4 | 5 | /** 6 | * Time Complexity: 7 | * - Union-Find: O(eloge) (e = edge) 8 | * - Flood-Fill: O(eloge + ve) (e = edge, v = vertex) 9 | * 10 | * Space Complexity: O(e) (e = edge) 11 | */ 12 | fun main() { 13 | val edges = mutableListOf( 14 | Triple(first = 1, second = 3, third = 3), 15 | Triple(first = 1, second = 2, third = 4), 16 | Triple(first = 1, second = 4, third = 3), 17 | Triple(first = 2, second = 5, third = 8), 18 | Triple(first = 3, second = 4, third = 3), 19 | Triple(first = 3, second = 5, third = 5), 20 | Triple(first = 4, second = 5, third = 6) 21 | ).apply { sortByWeight() } 22 | println("edges=${edges.joinToString()}") 23 | 24 | val selectedEdges = mutableListOf>() 25 | for (i in edges.indices) { 26 | val (u, v, _) = edges[i] 27 | if (isCycleCreatedBy(u, v)) continue 28 | 29 | selectedEdges.add(edges[i]) 30 | if (selectedEdges.size == edges.size - 1) break 31 | } 32 | println("selectedEdges=${selectedEdges.joinToString()}") 33 | 34 | val minWeightSum = selectedEdges.sumOf { it.third } 35 | println("minWeightSum=$minWeightSum") 36 | } 37 | 38 | private fun MutableList>.sortByWeight() = sortWith( 39 | compareBy> { it.third }.thenBy { it.first }.thenBy { it.second } 40 | ) 41 | 42 | private fun isCycleCreatedBy(u: Int, v: Int): Boolean { 43 | val parentOfU = find(u) 44 | val parentOfV = find(v) 45 | if (parentOfU == parentOfV) return true 46 | 47 | if (parentOfU < parentOfV) { 48 | parentOf[parentOfV] = parentOfU 49 | } else { 50 | parentOf[parentOfU] = parentOfV 51 | } 52 | 53 | return false 54 | } 55 | 56 | private fun find(u: Int): Int = 57 | if (parentOf[u] == u) u else find(parentOf[u]).also { parentOf[u] = it } 58 | 59 | -------------------------------------------------------------------------------- /src/graph/PrimAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | import java.util.PriorityQueue 5 | 6 | /** 7 | * Time Complexity: O(eloge) (e = edge) 8 | * Space Complexity: O(v+e) (v = vertex, e = edge) 9 | */ 10 | fun main() { 11 | val adjacencyList = Array(size = 6) { LinkedList>() }.apply { 12 | this[1].add(3 to 3) 13 | this[1].add(2 to 4) 14 | this[1].add(4 to 3) 15 | this[2].add(5 to 8) 16 | this[3].add(4 to 3) 17 | this[3].add(5 to 5) 18 | this[4].add(5 to 6) 19 | } 20 | println("adjacencyList=${adjacencyList.joinToString()}") 21 | 22 | val isSelected = BooleanArray(size = 6).apply { this[1] = true } 23 | 24 | val nextEdges = PriorityQueue>(compareBy { it.third }) 25 | adjacencyList[1].forEach { (v, w) -> 26 | val nextEdge = Triple(first = 1, v, w) 27 | nextEdges.offer(nextEdge) 28 | } 29 | 30 | val selectedEdges = mutableListOf>() 31 | while (selectedEdges.size < 4) { 32 | val edge = nextEdges.poll() 33 | if (isSelected[edge.second]) continue 34 | 35 | isSelected[edge.second] = true 36 | selectedEdges.add(edge) 37 | adjacencyList[edge.second].forEach { (v, w) -> 38 | if (isSelected[v]) return@forEach 39 | 40 | val nextEdge = Triple(edge.second, v, w) 41 | nextEdges.offer(nextEdge) 42 | } 43 | } 44 | println("selectedEdges=${selectedEdges.joinToString()}") 45 | 46 | val minWeightSum = selectedEdges.sumOf { it.third } 47 | println("minWeightSum=$minWeightSum") 48 | } 49 | -------------------------------------------------------------------------------- /src/graph/TopologicalSorting.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * Time Complexity: 7 | * - Adjacency List: O(v+e) (v = vertex, e = edge) 8 | * - Adjacency Matrix: O(v^2) (v = vertex) 9 | * 10 | * Space Complexity: 11 | * - Adjacency List: O(v+e) (v = vertex, e = edge) 12 | * - Adjacency Matrix: O(v^2) (v = vertex) 13 | */ 14 | fun main() { 15 | val indegreeOf = IntArray(size = 8) 16 | val adjacencyList = Array(size = 8) { LinkedList() }.apply { 17 | this[1].add(2) 18 | ++indegreeOf[2] 19 | 20 | this[3].add(2) 21 | ++indegreeOf[2] 22 | 23 | this[3].add(4) 24 | ++indegreeOf[4] 25 | 26 | this[4].add(2) 27 | ++indegreeOf[2] 28 | 29 | this[4].add(5) 30 | ++indegreeOf[5] 31 | 32 | this[5].add(6) 33 | ++indegreeOf[6] 34 | 35 | this[7].add(5) 36 | ++indegreeOf[5] 37 | } 38 | 39 | println("adjacencyList=${adjacencyList.joinToString()}") 40 | println("indegreeOf=${indegreeOf.joinToString()}") 41 | 42 | val result = mutableListOf() 43 | val nextNodes = LinkedList().apply { 44 | for (u in 1..7) if (indegreeOf[u] == 0) add(u) 45 | } 46 | while (nextNodes.isNotEmpty()) { 47 | val u = nextNodes.poll() 48 | result.add(u) 49 | 50 | adjacencyList[u].forEach { v -> 51 | --indegreeOf[v] 52 | if (indegreeOf[v] == 0) nextNodes.offer(v) 53 | } 54 | } 55 | 56 | println("result=$result") 57 | } 58 | -------------------------------------------------------------------------------- /src/graph/UnionFind.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import java.util.LinkedList 4 | 5 | private val parentOf: IntArray = IntArray(size = 8) { it } 6 | 7 | fun main() { 8 | val adjacencyList = Array(size = 8) { LinkedList() }.apply { 9 | this[1].add(2) 10 | this[2].add(1) 11 | 12 | this[2].add(6) 13 | this[6].add(2) 14 | 15 | this[3].add(4) 16 | this[4].add(3) 17 | 18 | this[3].add(5) 19 | this[5].add(3) 20 | 21 | this[4].add(7) 22 | this[7].add(4) 23 | 24 | this[5].add(7) 25 | this[7].add(5) 26 | } 27 | println("adjacencyList=${adjacencyList.joinToString()}") 28 | 29 | for (u in 1..7) adjacencyList[u].forEach { v -> union(u, v) } 30 | println("parentOf=${parentOf.joinToString()}") 31 | 32 | val groupSize = parentOf.toSet().size - 1 33 | println("groupSize=$groupSize") 34 | } 35 | 36 | private fun union(u: Int, v: Int) { 37 | val parentOfU = find(u) 38 | val parentOfV = find(v) 39 | if (parentOfU == parentOfV) return 40 | 41 | if (parentOfU < parentOfV) { 42 | parentOf[parentOfV] = parentOfU 43 | } else { 44 | parentOf[parentOfU] = parentOfV 45 | } 46 | } 47 | 48 | private fun find(u: Int): Int = 49 | if (parentOf[u] == u) u else find(parentOf[u]).also { parentOf[u] = it } 50 | -------------------------------------------------------------------------------- /src/hash/SeparateChainingHashMap.kt: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * Time Complexity: 7 | * - Best: O(1) 8 | * - Worst: O(n) 9 | * Space Complexity: O(mn) (m = bucket, n = entry) 10 | */ 11 | fun main() { 12 | val map = SeparateChainingHashMap() 13 | map.add(key = "apple", value = 1) 14 | map.add(key = "banana", value = 2) 15 | map.add(key = "orange", value = 3) 16 | map.add(key = "orange", value = 0) 17 | println(map) 18 | 19 | map.remove(key = "apple") 20 | println(map) 21 | println(map.size()) 22 | println(map.get(key = "orange")) 23 | } 24 | 25 | class SeparateChainingHashMap { 26 | private var buckets: Array>> = Array(BUCKET_SIZE) { LinkedList() } 27 | private var entrySize: Int = 0 28 | 29 | fun add(key: KEY, value: VALUE): Boolean { 30 | val bucket = bucketOf(key) 31 | val entry = bucket.find { it.key == key } 32 | if (entry != null) { 33 | entry.value = value 34 | return false 35 | } 36 | 37 | bucket.add(Entry(key, value)) 38 | ++entrySize 39 | return true 40 | } 41 | 42 | fun remove(key: KEY): Boolean { 43 | val bucket = bucketOf(key) 44 | val i = bucket.indexOfFirst { it.key == key } 45 | if (i < 0) return false 46 | 47 | bucket.removeAt(i) 48 | --entrySize 49 | return true 50 | } 51 | 52 | fun get(key: KEY): VALUE? = bucketOf(key).find { it.key == key }?.value 53 | 54 | fun size(): Int = entrySize 55 | 56 | private fun bucketOf(key: KEY): LinkedList> { 57 | val i = (BUCKET_SIZE + key.hashCode() % BUCKET_SIZE) % BUCKET_SIZE 58 | return buckets[i] 59 | } 60 | 61 | override fun toString(): String = buckets.filter { it.isNotEmpty() }.flatten().joinToString() 62 | 63 | private data class Entry(val key: KEY, var value: VALUE) 64 | 65 | companion object { 66 | private const val BUCKET_SIZE: Int = 31 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/heap/MaxHeap.kt: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | /** 4 | * Time Complexity: 5 | * - Offer/Poll: O(logn) 6 | * - Peek: O(1) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | val nums = intArrayOf(1, 3, 2, 4, 1, 3, 5).apply { shuffle() } 11 | println(nums.joinToString()) 12 | 13 | val maxHeap = MaxHeap(nums.size) 14 | nums.forEach(maxHeap::offer) 15 | println(maxHeap) 16 | 17 | println(maxHeap.peek()) 18 | repeat(nums.size) { 19 | println(maxHeap.poll()) 20 | println(maxHeap) 21 | } 22 | } 23 | 24 | private class MaxHeap(capacity: Int) { 25 | private val tree: IntArray = IntArray(size = capacity shl 1) { Int.MIN_VALUE } 26 | private var size: Int = 0 27 | 28 | fun offer(x: Int) { 29 | var current = size + 1 30 | tree[current] = x 31 | 32 | while (true) { 33 | val parent = current shr 1 34 | if (parent == 0 || tree[current] <= tree[parent]) break 35 | 36 | val tmp = tree[parent] 37 | tree[parent] = tree[current] 38 | tree[current] = tmp 39 | 40 | current = parent 41 | } 42 | 43 | ++size 44 | } 45 | 46 | fun poll(): Int { 47 | check(0 < size) 48 | 49 | val peek = peek() 50 | tree[1] = tree[size] 51 | tree[size] = Int.MIN_VALUE 52 | --size 53 | var current = 1 54 | 55 | while (current < size) { 56 | val left = current shl 1 57 | val right = left + 1 58 | 59 | if (tree[right] < tree[left]) { 60 | if (tree[current] < tree[left]) { 61 | val tmp = tree[current] 62 | tree[current] = tree[left] 63 | tree[left] = tmp 64 | current = left 65 | } else { 66 | break 67 | } 68 | } else { 69 | if (tree[current] < tree[right]) { 70 | val tmp = tree[current] 71 | tree[current] = tree[right] 72 | tree[right] = tmp 73 | current = right 74 | } else { 75 | break 76 | } 77 | } 78 | } 79 | 80 | return peek 81 | } 82 | 83 | fun peek(): Int { 84 | check(0 < size) 85 | 86 | return tree[1] 87 | } 88 | 89 | override fun toString(): String = buildString { 90 | for (i in 1..size) append(tree[i], ' ') 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/heap/MinHeap.kt: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | /** 4 | * Time Complexity: 5 | * - Offer/Poll: O(logn) 6 | * - Peek: O(1) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | val nums = intArrayOf(1, 3, 2, 4, 1, 3, 5).apply { shuffle() } 11 | println(nums.joinToString()) 12 | 13 | val minHeap = MinHeap(nums.size) 14 | nums.forEach(minHeap::offer) 15 | println(minHeap) 16 | 17 | println(minHeap.peek()) 18 | repeat(nums.size) { 19 | println(minHeap.poll()) 20 | println(minHeap) 21 | } 22 | } 23 | 24 | private class MinHeap(capacity: Int) { 25 | private val tree: IntArray = IntArray(size = capacity shl 1) { Int.MAX_VALUE } 26 | private var size: Int = 0 27 | 28 | fun offer(x: Int) { 29 | var current = size + 1 30 | tree[current] = x 31 | 32 | while (true) { 33 | val parent = current shr 1 34 | if (parent == 0 || tree[parent] <= tree[current]) break 35 | 36 | val tmp = tree[parent] 37 | tree[parent] = tree[current] 38 | tree[current] = tmp 39 | 40 | current = parent 41 | } 42 | 43 | ++size 44 | } 45 | 46 | fun poll(): Int { 47 | check(0 < size) 48 | 49 | val peek = peek() 50 | tree[1] = tree[size] 51 | tree[size] = Int.MAX_VALUE 52 | --size 53 | var current = 1 54 | 55 | while (current < size) { 56 | val left = current shl 1 57 | val right = left + 1 58 | 59 | if (tree[left] < tree[right]) { 60 | if (tree[left] < tree[current]) { 61 | val tmp = tree[current] 62 | tree[current] = tree[left] 63 | tree[left] = tmp 64 | current = left 65 | } else { 66 | break 67 | } 68 | } else { 69 | if (tree[right] < tree[current]) { 70 | val tmp = tree[current] 71 | tree[current] = tree[right] 72 | tree[right] = tmp 73 | current = right 74 | } else { 75 | break 76 | } 77 | } 78 | } 79 | 80 | return peek 81 | } 82 | 83 | fun peek(): Int { 84 | check(0 < size) 85 | 86 | return tree[1] 87 | } 88 | 89 | override fun toString(): String = buildString { 90 | for (i in 1..size) append(tree[i], ' ') 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/search/BinarySearch.kt: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | /** 4 | * Time Complexity: 5 | * - Sorted: O(logn) 6 | * - Unsorted: O(nlogn) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | val arr = intArrayOf(-5, 3, 1, 2, 1, 0, 2, 4, 4, 6, -5, 7).apply { shuffle() } 11 | 12 | println(arr.lowerBound(x = 4)) 13 | println(arr.upperBound(x = 4)) 14 | } 15 | 16 | private fun IntArray.lowerBound(x: Int): Int { 17 | val sortedArr = sorted() 18 | println(sortedArr.joinToString()) 19 | 20 | var low = 0 21 | var high = size 22 | while (low < high) { 23 | val mid = low + high shr 1 24 | if (sortedArr[mid] < x) low = mid + 1 else high = mid 25 | } 26 | 27 | return high 28 | } 29 | 30 | private fun IntArray.upperBound(x: Int): Int { 31 | val sortedArr = sorted() 32 | println(sortedArr.joinToString()) 33 | 34 | var low = 0 35 | var high = size 36 | while (low < high) { 37 | val mid = low + high shr 1 38 | if (sortedArr[mid] <= x) low = mid + 1 else high = mid 39 | } 40 | 41 | return high 42 | } 43 | -------------------------------------------------------------------------------- /src/search/ParametricSearch.kt: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | /** 4 | * Time Complexity: 5 | * - Sorted: O(logn) 6 | * - Unsorted: O(nlogn) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | // Q. Get the maximum upper bound budget. 11 | val requestedBudgets = intArrayOf(70, 80, 30, 40, 100) 12 | val budget = 450 13 | 14 | requestedBudgets.sort() 15 | var min = 1 16 | var max = requestedBudgets.last() 17 | while (min <= max) { 18 | val mid = min + max shr 1 19 | val sum = requestedBudgets.sumOf { if (mid < it) mid else it } 20 | if (budget < sum) { 21 | max = mid - 1 22 | } else { 23 | min = mid + 1 24 | } 25 | } 26 | 27 | print(max) 28 | } 29 | -------------------------------------------------------------------------------- /src/sort/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | /** 4 | * Time Complexity: O(n^2) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | val arr = (-5..5).toMutableList().apply { shuffle() } 9 | println(arr.joinToString()) 10 | 11 | for (i in arr.lastIndex downTo 1) { 12 | for (j in 0 until i) { 13 | if (arr[j] <= arr[j + 1]) continue 14 | 15 | val tmp = arr[j] 16 | arr[j] = arr[j + 1] 17 | arr[j + 1] = tmp 18 | } 19 | } 20 | println(arr.joinToString()) 21 | } 22 | -------------------------------------------------------------------------------- /src/sort/CoutingSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | /** 4 | * Time Complexity: O(n) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | val arr = intArrayOf(0, 1, 2, 2, 3, 3, 4, 4, 4, 5, 6, 7, 7, 8, 8, 8, 9, 9).apply { shuffle() } 9 | println(arr.joinToString()) 10 | 11 | val counts = IntArray(size = 10) 12 | arr.forEach { ++counts[it] } 13 | var j = 0 14 | counts.forEachIndexed { i, count -> repeat(count) { arr[j++] = i } } 15 | println(arr.joinToString()) 16 | } 17 | -------------------------------------------------------------------------------- /src/sort/InsertionSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | /** 4 | * Time Complexity: O(n^2) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | val arr = (-5..5).toMutableList().apply { shuffle() } 9 | println(arr.joinToString()) 10 | 11 | for (i in 1 until arr.size) { 12 | val target = arr[i] 13 | var j = i - 1 14 | while (0 <= j && target < arr[j]) { 15 | arr[j + 1] = arr[j] 16 | --j 17 | } 18 | arr[j + 1] = target 19 | } 20 | println(arr.joinToString()) 21 | } 22 | -------------------------------------------------------------------------------- /src/sort/MergeSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | private val arr: MutableList = (-5..5).toMutableList().apply { shuffle() } 4 | private val tmp: IntArray = IntArray(arr.size) 5 | 6 | /** 7 | * Time Complexity: O(nlogn) 8 | * Space Complexity: O(n) 9 | */ 10 | fun main() { 11 | println(arr.joinToString()) 12 | mergeSort(start = 0, end = arr.size) 13 | println(arr.joinToString()) 14 | } 15 | 16 | private fun mergeSort(start: Int, end: Int) { 17 | if (start == end - 1) return 18 | 19 | val mid = start + end shr 1 20 | mergeSort(start, mid) 21 | mergeSort(mid, end) 22 | merge(start, end) 23 | } 24 | 25 | private fun merge(start: Int, end: Int) { 26 | val mid = start + end shr 1 27 | var left = start 28 | var right = mid 29 | for (i in start until end) { 30 | tmp[i] = when { 31 | right == end -> arr[left++] 32 | left == mid -> arr[right++] 33 | arr[left] <= arr[right] -> arr[left++] 34 | else -> arr[right++] 35 | } 36 | } 37 | for (i in start until end) arr[i] = tmp[i] 38 | } 39 | -------------------------------------------------------------------------------- /src/sort/QuickSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | private val arr: MutableList = (-5..5).toMutableList().apply { shuffle() } 4 | 5 | /** 6 | * Time Complexity 7 | * - Best: O(nlogn) 8 | * - Worst: O(n^2) 9 | * Space Complexity: O(n) 10 | */ 11 | fun main() { 12 | println(arr.joinToString()) 13 | quickSort(start = 0, end = arr.size) 14 | println(arr.joinToString()) 15 | } 16 | 17 | private fun quickSort(start: Int, end: Int) { 18 | if (end - 1 <= start) return 19 | 20 | val pivot = arr[start] 21 | var left = start + 1 22 | var right = end - 1 23 | while (true) { 24 | while (left <= right && arr[left] <= pivot) ++left 25 | while (left <= right && pivot <= arr[right]) --right 26 | if (right < left) break 27 | 28 | val tmp = arr[left] 29 | arr[left] = arr[right] 30 | arr[right] = tmp 31 | } 32 | arr[start] = arr[right] 33 | arr[right] = pivot 34 | 35 | quickSort(start, right) 36 | quickSort(right + 1, end) 37 | } 38 | -------------------------------------------------------------------------------- /src/sort/RadixSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * Time Complexity: O(dn) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | val arr = intArrayOf(0, 1, 2, 21, 22, 34, 41, 47, 5, 6, 77, 79, 80, 81, 999).apply { shuffle() } 11 | println(arr.joinToString()) 12 | 13 | val maxLen = 3 14 | var buckets = Array(size = 10) { LinkedList() } 15 | for (n in 1..maxLen) { 16 | arr.forEach { buckets[it.digitOf(n)].add(it) } 17 | var j = 0 18 | buckets.forEach { it.forEach { num -> arr[j++] = num } } 19 | buckets = Array(size = 10) { LinkedList() } 20 | } 21 | println(arr.joinToString()) 22 | } 23 | 24 | private fun Int.digitOf(n: Int): Int { 25 | require(1 <= n) 26 | val div = generateSequence(seed = 1) { it * 10 }.take(n).last() 27 | return this / div % 10 28 | } 29 | -------------------------------------------------------------------------------- /src/sort/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | /** 4 | * Time Complexity: O(n^2) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | val arr = (-5..5).toMutableList().apply { shuffle() } 9 | println(arr.joinToString()) 10 | 11 | for (i in 0 until arr.lastIndex) { 12 | for (j in i + 1 until arr.size) { 13 | if (arr[i] <= arr[j]) continue 14 | 15 | val tmp = arr[i] 16 | arr[i] = arr[j] 17 | arr[j] = tmp 18 | } 19 | } 20 | println(arr.joinToString()) 21 | } 22 | -------------------------------------------------------------------------------- /src/text/KmpAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | fun main() { 4 | val text = "abababadababacabad" 5 | val pattern = "ababacaba" 6 | 7 | val failOf = IntArray(text.length) 8 | var j = 0 9 | for (i in 1 until text.length) { 10 | while (0 < j && text[i] != text[j]) j = failOf[j - 1] 11 | if (text[i] == text[j]) failOf[i] = ++j 12 | } 13 | println("failOf=${failOf.joinToString()}") 14 | 15 | j = 0 16 | for (i in text.indices) { 17 | while (0 < j && text[i] != pattern[j]) j = failOf[j - 1] 18 | if (text[i] == pattern[j]) ++j 19 | if (j == pattern.length) return println("index=${j - 1}") 20 | } 21 | 22 | println("index=-1") 23 | } 24 | -------------------------------------------------------------------------------- /src/text/PatternMatchingAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | /** 4 | * Time Complexity: O(tk) (t = text, k = keyword) 5 | * Space Complexity: O(l) (l = text length) 6 | */ 7 | fun main() { 8 | val text = "aaaaaaaaaaaa" 9 | val pattern = "aaaaab" 10 | 11 | for (i in 0 until text.length - pattern.length) { 12 | var isMatched = true 13 | for (j in pattern.indices) { 14 | if (text[i + j] == pattern[j]) continue 15 | 16 | isMatched = false 17 | break 18 | } 19 | if (isMatched) return println("isMatched") 20 | } 21 | 22 | println("isUnmatched") 23 | } 24 | -------------------------------------------------------------------------------- /src/text/Trie.kt: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | /** 4 | * Time Complexity: O(l) (l = text length) 5 | * Space Complexity: O(nl) (n = text size, l = text length) 6 | */ 7 | fun main() { 8 | val result = buildString { 9 | with(Trie()) { 10 | appendLine(add(text = "apple")) 11 | appendLine(add(text = "apple")) 12 | appendLine(add(text = "application")) 13 | appendLine(add(text = "banana")) 14 | appendLine(add(text = "bananas")) 15 | appendLine(add(text = "car")) 16 | appendLine(size()) 17 | appendLine(has(text = "apple")) 18 | appendLine(has(text = "app")) 19 | appendLine(has(text = "applications")) 20 | appendLine(remove(text = "banana")) 21 | appendLine(remove(text = "banana")) 22 | appendLine(size()) 23 | appendLine(has(text = "banana")) 24 | appendLine(has(text = "bananas")) 25 | } 26 | } 27 | 28 | print(result) 29 | } 30 | 31 | private data class Trie(val c: Char = ' ') { 32 | private val children: HashMap = hashMapOf() 33 | private var isTerminal: Boolean = false 34 | private var size: Int = 0 35 | 36 | fun add(text: String): Boolean { 37 | var trie = this 38 | text.forEach { 39 | if (trie.children[it] == null) trie.children[it] = Trie(it) 40 | trie = requireNotNull(trie.children[it]) 41 | } 42 | if (trie.isTerminal) return false 43 | 44 | trie.isTerminal = true 45 | ++size 46 | 47 | return true 48 | } 49 | 50 | fun remove(text: String): Boolean { 51 | var trie = this 52 | text.forEach { 53 | if (trie.children[it] == null) return false 54 | trie = requireNotNull(trie.children[it]) 55 | } 56 | if (!trie.isTerminal) return false 57 | 58 | trie.isTerminal = false 59 | --size 60 | 61 | return true 62 | } 63 | 64 | fun has(text: String): Boolean { 65 | var trie = this 66 | text.forEach { 67 | if (trie.children[it] == null) return false 68 | trie = requireNotNull(trie.children[it]) 69 | } 70 | 71 | return trie.isTerminal 72 | } 73 | 74 | fun size(): Int = size 75 | } 76 | -------------------------------------------------------------------------------- /src/tree/BinaryTree.kt: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | /** 4 | * Time Complexity: 5 | * - Best: O(logn) (balanced tree) 6 | * - Worst: O(n) (unbalanced tree) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | val root = BinaryTree(value = 55).apply { 11 | left = BinaryTree(value = 30).apply { 12 | left = BinaryTree(value = 15) 13 | right = BinaryTree(value = 40) 14 | } 15 | right = BinaryTree(value = 70).apply { 16 | left = BinaryTree(value = 60) 17 | right = BinaryTree(value = 100) 18 | } 19 | } 20 | 21 | val result = buildString { 22 | appendLine(root.has(value = 70)) 23 | appendLine(root.has(value = 65)) 24 | appendLine(root.add(value = 65)) 25 | appendLine(root.has(value = 65)) 26 | appendLine(root.remove(value = 70)) 27 | appendLine(root.has(value = 70)) 28 | appendLine(root.has(value = 60)) 29 | } 30 | 31 | print(result) 32 | } 33 | 34 | private data class BinaryTree>(val value: VALUE) { 35 | var left: BinaryTree? = null 36 | var right: BinaryTree? = null 37 | 38 | fun add(value: VALUE): Boolean { 39 | var node: BinaryTree? = this 40 | while (node != null) { 41 | node = when { 42 | value < node.value -> { 43 | if (node.left == null) { 44 | node.left = BinaryTree(value) 45 | return true 46 | } 47 | node.left 48 | } 49 | 50 | node.value < value -> { 51 | if (node.right == null) { 52 | node.right = BinaryTree(value) 53 | return true 54 | } 55 | node.right 56 | } 57 | 58 | else -> return false 59 | } 60 | } 61 | 62 | return false 63 | } 64 | 65 | fun remove(value: VALUE): Boolean { 66 | var node: BinaryTree? = this 67 | while (node != null) { 68 | node = when { 69 | value < node.value -> { 70 | if (node.left?.value == value) { 71 | val right = node.left?.right 72 | node.left = node.findNewRootOrNull() 73 | node.left?.right = right 74 | return true 75 | } 76 | node.left 77 | } 78 | 79 | node.value < value -> { 80 | if (node.right?.value == value) { 81 | val left = node.right?.left 82 | node.right = node.right?.findNewRootOrNull() 83 | node.right?.left = left 84 | return true 85 | } 86 | node.right 87 | } 88 | 89 | else -> return false 90 | } 91 | } 92 | 93 | return false 94 | } 95 | 96 | private fun findNewRootOrNull(): BinaryTree? = 97 | when { 98 | left == null && right == null -> null 99 | left != null && right == null -> left 100 | left == null && right != null -> right 101 | else -> { 102 | var node = right 103 | while (node != null) { 104 | if (node.left == null) break 105 | 106 | node = node.left 107 | } 108 | node 109 | } 110 | } 111 | 112 | fun has(value: VALUE): Boolean { 113 | var node: BinaryTree? = this 114 | while (node != null) { 115 | node = when { 116 | value < node.value -> node.left 117 | node.value < value -> node.right 118 | else -> return true 119 | } 120 | } 121 | 122 | return false 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/tree/InOrderTraversal.kt: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | /** 4 | * Time Complexity: O(n) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | data class Node(val value: T) { 9 | var left: Node? = null 10 | var right: Node? = null 11 | } 12 | 13 | val root = Node(value = 1).apply { 14 | left = Node(value = 2).apply { 15 | left = Node(value = 4) 16 | right = Node(value = 5).apply { 17 | right = Node(value = 8) 18 | } 19 | } 20 | right = Node(value = 3).apply { 21 | left = Node(value = 6) 22 | right = Node(value = 7) 23 | } 24 | } 25 | 26 | val sb = StringBuilder() 27 | fun traverse(node: Node) { 28 | node.left?.let(::traverse) 29 | sb.append(node.value, ' ') 30 | node.right?.let(::traverse) 31 | } 32 | traverse(root) 33 | 34 | println(sb.toString()) 35 | } 36 | -------------------------------------------------------------------------------- /src/tree/LevelOrderTraversal.kt: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | import java.util.LinkedList 4 | 5 | /** 6 | * Time Complexity: O(n) 7 | * Space Complexity: O(n) 8 | */ 9 | fun main() { 10 | data class Node(val value: T) { 11 | var left: Node? = null 12 | var right: Node? = null 13 | } 14 | 15 | val root = Node(value = 1).apply { 16 | left = Node(value = 2).apply { 17 | left = Node(value = 4) 18 | right = Node(value = 5).apply { 19 | right = Node(value = 8) 20 | } 21 | } 22 | right = Node(value = 3).apply { 23 | left = Node(value = 6) 24 | right = Node(value = 7) 25 | } 26 | } 27 | 28 | val sb = StringBuilder() 29 | val nextNodes = LinkedList>().apply { offer(root) } 30 | while (nextNodes.isNotEmpty()) { 31 | val node = nextNodes.poll() 32 | sb.append(node.value, ' ') 33 | 34 | node.left?.let(nextNodes::offer) 35 | node.right?.let(nextNodes::offer) 36 | } 37 | 38 | println(sb.toString()) 39 | } 40 | -------------------------------------------------------------------------------- /src/tree/PostOrderTraversal.kt: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | /** 4 | * Time Complexity: O(n) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | data class Node(val value: T) { 9 | var left: Node? = null 10 | var right: Node? = null 11 | } 12 | 13 | val root = Node(value = 1).apply { 14 | left = Node(value = 2).apply { 15 | left = Node(value = 4) 16 | right = Node(value = 5).apply { 17 | right = Node(value = 8) 18 | } 19 | } 20 | right = Node(value = 3).apply { 21 | left = Node(value = 6) 22 | right = Node(value = 7) 23 | } 24 | } 25 | 26 | val sb = StringBuilder() 27 | fun traverse(node: Node) { 28 | node.left?.let(::traverse) 29 | node.right?.let(::traverse) 30 | sb.append(node.value, ' ') 31 | } 32 | traverse(root) 33 | 34 | println(sb.toString()) 35 | } 36 | -------------------------------------------------------------------------------- /src/tree/PreOrderTraversal.kt: -------------------------------------------------------------------------------- 1 | package tree 2 | 3 | /** 4 | * Time Complexity: O(n) 5 | * Space Complexity: O(n) 6 | */ 7 | fun main() { 8 | data class Node(val value: T) { 9 | var left: Node? = null 10 | var right: Node? = null 11 | } 12 | 13 | val root = Node(value = 1).apply { 14 | left = Node(value = 2).apply { 15 | left = Node(value = 4) 16 | right = Node(value = 5).apply { 17 | right = Node(value = 8) 18 | } 19 | } 20 | right = Node(value = 3).apply { 21 | left = Node(value = 6) 22 | right = Node(value = 7) 23 | } 24 | } 25 | 26 | val sb = StringBuilder() 27 | fun traverse(node: Node) { 28 | sb.append(node.value, ' ') 29 | node.left?.let(::traverse) 30 | node.right?.let(::traverse) 31 | } 32 | traverse(root) 33 | 34 | println(sb.toString()) 35 | } 36 | --------------------------------------------------------------------------------