├── MindfulGif.gif ├── src └── main │ └── kotlin │ ├── Main.kt │ ├── Algorithms │ ├── Dijkstras │ │ ├── Ref.kt │ │ ├── Vertex.kt │ │ ├── Edge.kt │ │ ├── Queue.kt │ │ ├── QueueStack.kt │ │ ├── PriorityQueue.kt │ │ ├── AdjacencyList.kt │ │ ├── PlayGround.kt │ │ ├── Stack.kt │ │ ├── AdjacencyMatrix.kt │ │ ├── Graph.kt │ │ ├── Heap.kt │ │ └── README.md │ ├── Prims │ │ ├── Vertex.kt │ │ ├── Edge.kt │ │ ├── Queue.kt │ │ ├── PriorityQueue.kt │ │ ├── Graph.kt │ │ ├── PlayGround.kt │ │ ├── AdjacencyList.kt │ │ ├── README.md │ │ └── Heap.kt │ ├── DepthFirstSearch │ │ ├── Ref.kt │ │ ├── Vertex.kt │ │ ├── Edge.kt │ │ ├── Queue.kt │ │ ├── PlayGround.kt │ │ ├── QueueStack.kt │ │ ├── AdjacencyList.kt │ │ ├── Stack.kt │ │ ├── AdjacencyMatrix.kt │ │ └── README.md │ ├── BreadthFirstSearch │ │ ├── Ref.kt │ │ ├── Vertex.kt │ │ ├── Edge.kt │ │ ├── Queue.kt │ │ ├── QueueStack.kt │ │ ├── PlayGround.kt │ │ ├── AdjacencyList.kt │ │ ├── AdjacencyMatrix.kt │ │ └── README.md │ ├── MergeSort │ │ ├── Utils.kt │ │ ├── PlayGround.kt │ │ ├── Challenge.kt │ │ ├── README.md │ │ └── MergeSort.kt │ ├── On2Sorting │ │ ├── Utils.kt │ │ ├── challenges │ │ │ ├── Challenge3.kt │ │ │ ├── Challenge2.kt │ │ │ └── Challenge1.kt │ │ ├── insertionsort │ │ │ └── InsertionSort.kt │ │ ├── selectionsort │ │ │ └── SelectionSort.kt │ │ ├── bubblesort │ │ │ └── BubbleSort.kt │ │ ├── PlayGround.kt │ │ └── README.md │ ├── QuickSort │ │ ├── Utils.kt │ │ ├── QuicksortHoare.kt │ │ ├── Stack.kt │ │ ├── QuicksortNaive.kt │ │ ├── QuicksortMedian.kt │ │ ├── Challenge.kt │ │ ├── QuicksortLomuto.kt │ │ ├── QuicksortDutchFlag.kt │ │ └── PlayGround.kt │ ├── HeapSort │ │ ├── PlayGround.kt │ │ ├── Utils.kt │ │ ├── Challenge.kt │ │ ├── README.md │ │ ├── HeapSort.kt │ │ └── Heap.kt │ ├── RadixSort │ │ ├── PlayGround.kt │ │ ├── radixSort.kt │ │ ├── README.md │ │ └── Challenge.kt │ └── BinarySearch │ │ ├── BinarySearchPlayGround.kt │ │ └── README.md │ └── DataStructure │ ├── Graphs │ ├── Ref.kt │ ├── Edge.kt │ ├── Vertex.kt │ ├── PlayGround.kt │ ├── AdjacencyList.kt │ ├── AdjacencyMatrix.kt │ └── README.md │ ├── PriorityQueues │ ├── Queue.kt │ ├── Person.kt │ ├── PlayGround.kt │ ├── README.md │ ├── PriorityQueue.kt │ ├── PriorityQueueArrayList.kt │ └── Heap.kt │ ├── Queues │ ├── DoublyLinkedList │ │ ├── Node.kt │ │ ├── DoublyLinkedList.kt │ │ └── LinkedListQueue.kt │ ├── Queue.kt │ ├── DoubleStack │ │ └── Stack.kt │ ├── Ringbuffer │ │ ├── RingBuffer.kt │ │ └── RingBufferQueue.kt │ └── ArrayListQueue │ │ └── ArrayListQueue.kt │ ├── Trees │ ├── Tries │ │ ├── TrieNode.kt │ │ └── Extensions.kt │ ├── AVL │ │ ├── TraversableBinaryNode.kt │ │ └── AVLNode.kt │ ├── BasicTree │ │ ├── queue │ │ │ └── Queue.kt │ │ └── list │ │ │ └── ArrayListQueue.kt │ └── BST │ │ └── BinaryNode.kt │ ├── LinkedList │ ├── Node.kt │ ├── LinkedListIterator.kt │ └── README.md │ ├── Heap │ ├── README.md │ └── HeapPlayGround.kt │ └── Stack │ ├── README.md │ └── Stack.kt └── .gitignore /MindfulGif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IbrahimEzzatSaad/MindfulData/HEAD/MindfulGif.gif -------------------------------------------------------------------------------- /src/main/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | 2 | fun main() { 3 | 4 | 5 | println("Hello, World") 6 | 7 | 8 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Ref.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | data class Ref(var value: T) -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/Ref.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Graphs 2 | 3 | data class Ref(var value: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/Vertex.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | class Vertex(val index: Int, val data: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/Ref.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | data class Ref(var value: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Vertex.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | class Vertex(val index: Int, val data: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/Ref.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | 4 | data class Ref(var value: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/Vertex.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | data class Vertex(val index: Int, val data: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/Vertex.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | 4 | data class Vertex(val index: Int, val data: T) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Edge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | 4 | class Edge(val source: Vertex, val destination: Vertex, val weight: Double? = null) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/Edge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | 3 | 4 | data class Edge(val source: Vertex, val destination: Vertex, val weight: Double? = null) 5 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/Edge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | data class Edge(val source: Vertex, val destination: Vertex, val weight: Double? = null) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/Edge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | data class Edge(val source: Vertex, val destination: Vertex, val weight: Double? = null) -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/MergeSort/Utils.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.MergeSort 2 | 3 | infix fun String.example(function: () -> Unit) { 4 | println("---Example of $this---") 5 | function() 6 | println() 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/Edge.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Graphs 2 | 3 | 4 | //An Edge connects two vertices and has an optional weight. 5 | data class Edge(val source: Vertex, val destination: Vertex, val weight: Double? = null) 6 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/Queue.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | interface Queue { 3 | 4 | fun enqueue(element: T): Boolean 5 | 6 | fun dequeue(): T? 7 | 8 | val count: Int 9 | 10 | val isEmpty: Boolean 11 | get() = count == 0 12 | 13 | fun peek(): T? 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Queue.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | interface Queue { 4 | 5 | fun enqueue(element: T): Boolean 6 | 7 | fun dequeue(): T? 8 | 9 | val count: Int 10 | 11 | val isEmpty: Boolean 12 | get() = count == 0 13 | 14 | fun peek(): T? 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/Queue.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | 4 | interface Queue { 5 | 6 | fun enqueue(element: T): Boolean 7 | 8 | fun dequeue(): T? 9 | 10 | val count: Int 11 | 12 | val isEmpty: Boolean 13 | get() = count == 0 14 | 15 | fun peek(): T? 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/Queue.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | 4 | interface Queue { 5 | 6 | fun enqueue(element: T): Boolean 7 | 8 | fun dequeue(): T? 9 | 10 | val count: Int 11 | get 12 | 13 | val isEmpty: Boolean 14 | get() = count == 0 15 | 16 | fun peek(): T? 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/Utils.kt: -------------------------------------------------------------------------------- 1 | 2 | 3 | infix fun String.example(function: () -> Unit) { 4 | println("---Example of $this---") 5 | function() 6 | println() 7 | } 8 | 9 | fun MutableList.swapAt(first: Int, second: Int) { 10 | val aux = this[first] 11 | this[first] = this[second] 12 | this[second] = aux 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/Queue.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.PriorityQueues 2 | 3 | interface Queue { 4 | 5 | fun enqueue(element: T): Boolean 6 | 7 | fun dequeue(): T? 8 | 9 | val count: Int 10 | get 11 | 12 | val isEmpty: Boolean 13 | get() = count == 0 14 | 15 | fun peek(): T? 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/Utils.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | infix fun String.example(function: () -> Unit) { 4 | println("---Example of $this---") 5 | function() 6 | println() 7 | } 8 | 9 | fun MutableList.swapAt(first: Int, second: Int) { 10 | val aux = this[first] 11 | this[first] = this[second] 12 | this[second] = aux 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/DoublyLinkedList/Node.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.DoublyLinkedList 2 | 3 | data class Node(var value: T, var next: Node? = null, var previous: Node? = null) { 4 | override fun toString(): String { 5 | return if (next != null) { 6 | "$value -> ${next.toString()}" 7 | } else { 8 | "$value" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/Vertex.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Graphs 2 | 3 | 4 | /*Here, you’ve defined a generic Vertex class. A vertex has a unique index within its 5 | graph and holds a piece of data. 6 | 7 | You defined Vertex as a data class because it will be used as a key in a Map later, and 8 | a data class gives you equals() and hashCode() implementations, without having to write them yourself.*/ 9 | data class Vertex(val index: Int, val data: T) 10 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/HeapSort/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.HeapSort 2 | 3 | fun main() { 4 | "Heap sort" example { 5 | val array = arrayOf(6, 12, 2, 26, 8, 18, 21, 9, 5) 6 | array.heapSort(ascending) 7 | print(array.joinToString()) 8 | } 9 | 10 | "Heap sort descending" example { 11 | val array = arrayOf(6, 12, 2, 26, 8, 18, 21, 9, 5) 12 | array.heapSort(descending) 13 | print(array.joinToString()) 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/HeapSort/Utils.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.HeapSort 2 | 3 | infix fun String.example(function: () -> Unit) { 4 | println("---Example of $this---") 5 | function() 6 | println() 7 | } 8 | 9 | fun Array.swapAt(first: Int, second: Int) { 10 | val aux = this[first] 11 | this[first] = this[second] 12 | this[second] = aux 13 | } 14 | 15 | val ascending = Comparator { first: Int, second: Int -> 16 | when { 17 | first < second -> -1 18 | first > second -> 1 19 | else -> 0 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/HeapSort/Challenge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.HeapSort 2 | 3 | /*---------------Challenge 2: Descending sort----------------- 4 | The current example of heap sort sorts the elements in **ascending** order. How would you sort in **descending** order? 5 | -----------------Solution----------------- 6 | You just need to swap the ascending comparator with a 7 | descending one when you create the SortingHeap. Looking at how ascending is 8 | defined, you can easily come up with a descending:*/ 9 | 10 | val descending = Comparator { first: Int, second: Int -> 11 | when { 12 | first < second -> 1 13 | first > second -> -1 14 | else -> 0 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | fun main() { 4 | val graph = AdjacencyList() 5 | val a = graph.createVertex("A") 6 | val b = graph.createVertex("B") 7 | val c = graph.createVertex("C") 8 | val d = graph.createVertex("D") 9 | 10 | graph.add(EdgeType.DIRECTED, a, b, null) 11 | graph.add(EdgeType.DIRECTED, a, c, null) 12 | graph.add(EdgeType.DIRECTED, b, c, null) 13 | graph.add(EdgeType.DIRECTED, c, d, null) 14 | 15 | val vertices = graph.depthFirstSearchRecursive(a) // 2 16 | vertices.forEach { 17 | println(it.data) 18 | } 19 | 20 | println(graph.hasCycle(a)) 21 | 22 | graph.add(EdgeType.DIRECTED, c, a, null) // make a cycle 23 | 24 | println(graph.hasCycle(a)) 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/QueueStack.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | class QueueStack : Queue { 4 | 5 | private var leftStack = arrayListOf() 6 | private var rightStack = arrayListOf() 7 | 8 | override fun enqueue(element: T): Boolean { 9 | rightStack.add(element) 10 | return true 11 | } 12 | 13 | override fun dequeue(): T? { 14 | if (rightStack.isEmpty()) return null 15 | 16 | if (leftStack.isEmpty()) { 17 | leftStack = ArrayList(rightStack.asReversed()) 18 | rightStack = arrayListOf() 19 | } 20 | 21 | return leftStack.removeAt(leftStack.size - 1) 22 | } 23 | 24 | override val count = leftStack.size + rightStack.size 25 | 26 | override fun peek(): T? { 27 | return if (!leftStack.isEmpty()) leftStack.lastOrNull() else rightStack.firstOrNull() 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | import java.util.* 4 | 5 | abstract class AbstractPriorityQueue : Queue { 6 | 7 | abstract val heap: Heap 8 | 9 | override val count: Int 10 | get() = heap.count 11 | 12 | override fun enqueue(element: T): Boolean { 13 | heap.insert(element) 14 | return true 15 | } 16 | 17 | override fun dequeue() = heap.remove() 18 | 19 | override fun peek() = heap.peek() 20 | } 21 | 22 | class ComparablePriorityQueueImpl> : 23 | AbstractPriorityQueue() { 24 | 25 | override val heap = ComparableHeapImpl() 26 | } 27 | 28 | class ComparatorPriorityQueueImpl( 29 | private val comparator: Comparator 30 | ) : AbstractPriorityQueue() { 31 | 32 | override val heap = ComparatorHeapImpl(comparator) 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | 3 | import java.util.* 4 | 5 | abstract class AbstractPriorityQueue : Queue { 6 | 7 | abstract val heap: Heap 8 | 9 | override val count: Int 10 | get() = heap.count 11 | 12 | override fun enqueue(element: T): Boolean { 13 | heap.insert(element) 14 | return true 15 | } 16 | 17 | override fun dequeue() = heap.remove() 18 | 19 | override fun peek() = heap.peek() 20 | } 21 | 22 | class ComparablePriorityQueueImpl> : 23 | AbstractPriorityQueue() { 24 | 25 | override val heap = ComparableHeapImpl() 26 | } 27 | 28 | class ComparatorPriorityQueueImpl( 29 | private val comparator: Comparator 30 | ) : AbstractPriorityQueue() { 31 | 32 | override val heap = ComparatorHeapImpl(comparator) 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/Tries/TrieNode.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Trees.Tries 2 | 3 | class TrieNode(var key: Key?, var parent: TrieNode?) { 4 | 5 | /* This interface is slightly different compared to the other nodes you’ve encountered: 6 | * 1. key holds the data for the node. This is optional because the root node of the trie has no key. 7 | * 2. A TrieNode holds a reference to its parent. This reference simplifies remove() later on. 8 | * 3. In binary search trees, nodes have a left and right child. In a trie, a node needs to 9 | hold multiple different elements. You’ve declared a children map to help with that. 10 | * 4. isTerminating acts as an indicator for the end of a collection.*/ 11 | val children: HashMap> = HashMap() 12 | 13 | var isTerminating = false 14 | 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/QueueStack.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | 4 | class QueueStack : Queue { 5 | 6 | private var leftStack = arrayListOf() 7 | private var rightStack = arrayListOf() 8 | 9 | override fun enqueue(element: T): Boolean { 10 | rightStack.add(element) 11 | return true 12 | } 13 | 14 | override fun dequeue(): T? { 15 | if (rightStack.isEmpty()) return null 16 | 17 | if (leftStack.isEmpty()) { 18 | leftStack = ArrayList(rightStack.asReversed()) 19 | rightStack = arrayListOf() 20 | } 21 | 22 | return leftStack.removeAt(leftStack.size - 1) 23 | } 24 | 25 | override val count = leftStack.size + rightStack.size 26 | 27 | override fun peek(): T? { 28 | return if (!leftStack.isEmpty()) leftStack.lastOrNull() else rightStack.firstOrNull() 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/QueueStack.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | 4 | class QueueStack : Queue { 5 | 6 | private var leftStack = arrayListOf() 7 | private var rightStack = arrayListOf() 8 | 9 | override fun enqueue(element: T): Boolean { 10 | rightStack.add(element) 11 | return true 12 | } 13 | 14 | override fun dequeue(): T? { 15 | if (rightStack.isEmpty()) return null 16 | 17 | if (leftStack.isEmpty()) { 18 | leftStack = ArrayList(rightStack.asReversed()) 19 | rightStack = arrayListOf() 20 | } 21 | 22 | return leftStack.removeAt(leftStack.size - 1) 23 | } 24 | 25 | override val count = leftStack.size + rightStack.size 26 | 27 | override fun peek(): T? { 28 | return if (!leftStack.isEmpty()) leftStack.lastOrNull() else rightStack.firstOrNull() 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/Tries/Extensions.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Trees.Tries 2 | 3 | 4 | /*These extension functions are only applicable to tries that store lists of characters. 5 | They hide the extra toList() calls you need to pass in a String, allowing you to 6 | simplify the code*/ 7 | fun Trie.insert(string: String) { 8 | insert(string.toList()) 9 | } 10 | 11 | fun Trie.contains(string: String): Boolean { 12 | return contains(string.toList()) 13 | } 14 | 15 | fun Trie.remove(string: String) { 16 | remove(string.toList()) 17 | } 18 | 19 | /*This extension maps the input string into a list of characters, and then maps the lists 20 | in the result of the collections() call back to strings.*/ 21 | fun Trie.collections(prefix: String): List { 22 | return collections(prefix.toList()).map { it.joinToString(separator = "") } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/Graph.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | 3 | 4 | interface Graph { 5 | 6 | fun createVertex(data: T): Vertex 7 | 8 | fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) 9 | 10 | fun addUndirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 11 | addDirectedEdge(source, destination, weight) 12 | addDirectedEdge(destination, source, weight) 13 | } 14 | 15 | fun add(edge: EdgeType, source: Vertex, destination: Vertex, weight: Double?) { 16 | when (edge) { 17 | EdgeType.DIRECTED -> addDirectedEdge(source, destination, weight) 18 | EdgeType.UNDIRECTED -> addUndirectedEdge(source, destination, weight) 19 | } 20 | } 21 | 22 | fun edges(source: Vertex): ArrayList> 23 | 24 | fun weight(source: Vertex, destination: Vertex): Double? 25 | 26 | } 27 | 28 | enum class EdgeType { 29 | DIRECTED, 30 | UNDIRECTED 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/challenges/Challenge3.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting.challenges 2 | 3 | import swapAt 4 | 5 | /*-----------------Challenge 3: Manual reverse----------------- 6 | * Reverse a list of elements by hand. Do not rely on reverse or reversed; you need to create your own. 7 | * --------------------Solution--------------------- 8 | * Reversing a collection is also quite straightforward. Using the double reference 9 | approach, you start swapping elements from the start and end of the collection, making your way to the middle. 10 | * Once you’ve hit the middle, you’re done swapping, and the collection is reversed. 11 | * For this solution, you need MutableList since you need to mutate the collection to reverse. 12 | * The time complexity of this solution is O(n).*/ 13 | fun > MutableList.rev() { 14 | var left = 0 15 | var right = this.lastIndex 16 | 17 | while (left < right) { 18 | swapAt(left, right) 19 | left++ 20 | right-- 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/MergeSort/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.MergeSort 2 | 3 | /*---------------------Key Points----------------------- 4 | * Merge sort is in the category of the divide and conquer algorithms. 5 | * There are many implementations of merge sort, and you can have different 6 | performance characteristics depending on the implementation. 7 | * To do a comparison, in this chapter you sorted objects implementing the 8 | Comparable interface but the same can be done providing a different implementation of Comparator.*/ 9 | 10 | fun main() { 11 | 12 | "merge sort" example { 13 | val list = listOf(7, 2, 6, 3, 9) 14 | println("Original: $list") 15 | 16 | val result = list.mergeSort() 17 | println("Merge sorted: $result") 18 | } 19 | 20 | "merge iterables" example { 21 | val list1 = listOf(1, 2, 3, 4, 5, 6, 7, 8) 22 | val list2 = listOf(1, 3, 4, 5, 5, 6, 7, 7) 23 | 24 | val result = merge(list1, list2) 25 | println("Merged: $result") 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/Queue.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues 2 | 3 | 4 | 5 | //This will be your starting point. From now on, everything you implement will obey the contract of this interface, which describes the core operations for a queue. 6 | interface Queue { 7 | 8 | //enqueue: Inserts an element at the back of the queue and returns true if the operation is successful. 9 | fun enqueue(element: T): Boolean 10 | 11 | //dequeue: Removes the element at the front of the queue and returns it. 12 | fun dequeue(): T? 13 | 14 | val count: Int 15 | get 16 | 17 | //isEmpty: Checks if the queue is empty using the count property 18 | val isEmpty: Boolean 19 | get() = count == 0 20 | 21 | //peek: Returns the element at the front of the queue without removing it. 22 | fun peek(): T? 23 | } 24 | 25 | //Notice that the queue only cares about removal from the front and insertion at the back. 26 | //You don’t need to know what the contents are in between. If you did, you’d presumably use an array instead of a Queue. -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | 3 | fun main() { 4 | val graph = AdjacencyList() 5 | val one = graph.createVertex(1) 6 | val two = graph.createVertex(2) 7 | val three = graph.createVertex(3) 8 | val four = graph.createVertex(4) 9 | val five = graph.createVertex(5) 10 | val six = graph.createVertex(6) 11 | 12 | graph.add(EdgeType.UNDIRECTED, one, two, 6.0) 13 | graph.add(EdgeType.UNDIRECTED, one, three, 1.0) 14 | graph.add(EdgeType.UNDIRECTED, one, four, 5.0) 15 | graph.add(EdgeType.UNDIRECTED, two, three, 5.0) 16 | graph.add(EdgeType.UNDIRECTED, two, five, 3.0) 17 | graph.add(EdgeType.UNDIRECTED, three, four, 5.0) 18 | graph.add(EdgeType.UNDIRECTED, three, five, 6.0) 19 | graph.add(EdgeType.UNDIRECTED, three, six, 4.0) 20 | graph.add(EdgeType.UNDIRECTED, four, six, 2.0) 21 | graph.add(EdgeType.UNDIRECTED, five, six, 6.0) 22 | 23 | val (cost, mst) = Prim.produceMinimumSpanningTree(graph) 24 | println("cost: $cost") 25 | println("mst:") 26 | println(mst) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/challenges/Challenge2.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting.challenges 2 | 3 | import Algorithms.On2Sorting.selectionsort.selectionSort 4 | 5 | /*-----------Challenge 2: Duplicate finder---------------- 6 | * Given a list of Comparable elements, return the largest element that’s a duplicate in the list. 7 | * --------------Solution-------------- 8 | * Finding the biggest duplicated element is rather straightforward. To make it even easier, 9 | * you can sort the list with one of the methods you’ve already implemented. 10 | * The time complexity of this solution is O(n²) because you’ve used sorting.*/ 11 | fun > MutableList.biggestDuplicate(): T? { 12 | //1-You first sort the list. 13 | this.selectionSort() 14 | //2-Start going through it from right to left since you know that the biggest elements are on the right, neatly sorted. 15 | for (i in this.lastIndex downTo 1) { 16 | //3-The first one that’s repeated is your solution 17 | if (this[i] == this[i - 1]) { 18 | return this[i] 19 | } 20 | } 21 | return null 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/LinkedList/Node.kt: -------------------------------------------------------------------------------- 1 | data class Node(var value: T, var next: Node ? = null) { 2 | 3 | override fun toString(): String { 4 | 5 | return if(next != null){ 6 | "$value ----> ${next.toString()}" 7 | } 8 | else "$value" 9 | 10 | } 11 | 12 | 13 | 14 | /*Challenge 1: Reverse a linked list 15 | This function forwards the call to the recursive function that traverses the list, node by node. To traverse the list, add this extension function for Node*/ 16 | fun Node.printInReverse() { 17 | this.next?.printInReverse() 18 | // 1-First, you check if you’ve reached the end of the list. That’s the beginning of the reverse printing, and you’ll not add an arrow there. 19 | // The arrows start with the second element of the reverse output. This is just for pretty formatting. 20 | if (this.next != null) { 21 | print(" -> ") 22 | } 23 | // 2-As the recursive statements unravel, the node data gets printed. 24 | print(this.value.toString()) 25 | } 26 | 27 | 28 | 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | 4 | fun main() { 5 | val graph = AdjacencyList() 6 | val a = graph.createVertex("A") 7 | val b = graph.createVertex("B") 8 | val c = graph.createVertex("C") 9 | val d = graph.createVertex("D") 10 | val e = graph.createVertex("E") 11 | val f = graph.createVertex("F") 12 | val g = graph.createVertex("G") 13 | val h = graph.createVertex("H") 14 | 15 | graph.add(EdgeType.UNDIRECTED, a, b, null) 16 | graph.add(EdgeType.UNDIRECTED, a, c, null) 17 | graph.add(EdgeType.UNDIRECTED, a, d, null) 18 | graph.add(EdgeType.UNDIRECTED, b, e, null) 19 | graph.add(EdgeType.UNDIRECTED, c, f, null) 20 | graph.add(EdgeType.UNDIRECTED, c, g, null) 21 | graph.add(EdgeType.UNDIRECTED, e, h, null) 22 | graph.add(EdgeType.UNDIRECTED, e, f, null) 23 | graph.add(EdgeType.UNDIRECTED, f, g, null) 24 | 25 | // 2 26 | val vertices = graph.bfs(a) 27 | vertices.forEach { 28 | println(it.data) 29 | } 30 | 31 | // 3 32 | println(graph.isDisconnected()) 33 | 34 | graph.createVertex("I") 35 | 36 | println(graph.isDisconnected()) 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/AdjacencyList.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | class AdjacencyList : Graph { 4 | 5 | private val adjacencies: HashMap, ArrayList>> = HashMap() 6 | 7 | override fun createVertex(data: T): Vertex { 8 | val vertex = Vertex(adjacencies.count(), data) 9 | adjacencies[vertex] = ArrayList() 10 | return vertex 11 | } 12 | 13 | override fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 14 | val edge = Edge(source, destination, weight) 15 | adjacencies[source]?.add(edge) 16 | } 17 | 18 | override fun edges(source: Vertex) = adjacencies[source] ?: arrayListOf() 19 | 20 | override fun weight(source: Vertex, destination: Vertex): Double? { 21 | return edges(source).firstOrNull { it.destination == destination }?.weight 22 | } 23 | 24 | override fun toString(): String { 25 | return buildString { 26 | adjacencies.forEach { (vertex, edges) -> 27 | val edgeString = edges.joinToString { it.destination.data.toString() } 28 | append("${vertex.data} ---> [ $edgeString ]\n") 29 | } 30 | 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/AdjacencyList.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | class AdjacencyList: Graph { 4 | 5 | override val allVertices: ArrayList> 6 | get() = ArrayList(adjacencies.keys) 7 | 8 | val adjacencies: HashMap, ArrayList>> = HashMap() 9 | 10 | override fun createVertex(data: T): Vertex { 11 | val vertex = Vertex(adjacencies.count(), data) 12 | adjacencies[vertex] = ArrayList() 13 | return vertex 14 | } 15 | 16 | override fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 17 | val edge = Edge(source, destination, weight) 18 | adjacencies[source]?.add(edge) 19 | } 20 | 21 | override fun edges(source: Vertex) = adjacencies[source] ?: arrayListOf() 22 | 23 | override fun weight(source: Vertex, destination: Vertex): Double? { 24 | return edges(source).firstOrNull { it.destination == destination }?.weight 25 | } 26 | 27 | override fun toString(): String { 28 | return buildString { 29 | adjacencies.forEach { (vertex, edges) -> 30 | val edgeString = edges.joinToString { it.destination.data.toString() } 31 | append("${vertex.data} ---> [ $edgeString ]\n") 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/AVL/TraversableBinaryNode.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Trees.AVL 2 | 3 | 4 | /*--------------Challenge 3: Some refactoring--------- 5 | * Since there are many variants of binary trees, it makes sense to group shared functionality in an abstract class. 6 | 7 | * The traversal methods are a good candidate. 8 | TraversableBinaryNode abstract class that provides a default implementation of the traversal methods so that concrete subclasses get these 9 | methods for free. Have AVLNode extend this class.*/ 10 | abstract class TraversableBinaryNode, T>(var value: T) { 11 | 12 | 13 | var leftChild: Self? = null 14 | var rightChild: Self? = null 15 | 16 | fun traverseInOrder(visit: Visitor) { 17 | leftChild?.traverseInOrder(visit) 18 | visit(value) 19 | rightChild?.traverseInOrder(visit) 20 | } 21 | 22 | fun traversePreOrder(visit: Visitor) { 23 | visit(value) 24 | leftChild?.traversePreOrder(visit) 25 | rightChild?.traversePreOrder(visit) 26 | } 27 | 28 | fun traversePostOrder(visit: Visitor) { 29 | leftChild?.traversePostOrder(visit) 30 | rightChild?.traversePostOrder(visit) 31 | visit(value) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/QuicksortHoare.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | import swapAt 4 | 5 | /*Hoare’s partitioning algorithm always chooses the first element as the pivot. So, how does this work in code?*/ 6 | fun > MutableList.quicksortHoare(low: Int, high: Int) { 7 | if (low < high) { 8 | val p = this.partitionHoare(low, high) 9 | this.quicksortHoare(low, p) 10 | this.quicksortHoare(p + 1, high) 11 | } 12 | } 13 | 14 | fun > MutableList.partitionHoare(low: Int, high: Int): Int { 15 | val pivot = this[low] // 1-Select the first element as the pivot. 16 | var i = low - 1 // 2-Indexes i and j define two regions. Every index before i will be less than or equal to the pivot. Every index after j will be greater than or equal to the pivot. 17 | var j = high + 1 18 | while (true) { 19 | do { // 3-Decrease j until it reaches an element that is not greater than the pivot. 20 | j -= 1 21 | } while (this[j] > pivot) 22 | do { // 4-Increase i until it reaches an element that is not less than the pivot. 23 | i += 1 24 | } while (this[i] < pivot) 25 | if (i < j) { // 5-If i and j have not overlapped, swap the elements. 26 | this.swapAt(i, j) 27 | } else { 28 | return j // 6-Return the index that separates both regions. 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/AdjacencyList.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | 3 | class AdjacencyList : Graph { 4 | 5 | private val adjacencies: HashMap, ArrayList>> = HashMap() 6 | 7 | val vertices: Set> 8 | get() = adjacencies.keys 9 | 10 | override fun createVertex(data: T): Vertex { 11 | val vertex = Vertex(adjacencies.count(), data) 12 | adjacencies[vertex] = ArrayList() 13 | return vertex 14 | } 15 | 16 | override fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 17 | val edge = Edge(source, destination, weight) 18 | adjacencies[source]?.add(edge) 19 | } 20 | 21 | override fun edges(source: Vertex) = adjacencies[source] ?: arrayListOf() 22 | 23 | override fun weight(source: Vertex, destination: Vertex): Double? { 24 | return edges(source).firstOrNull { it.destination == destination }?.weight 25 | } 26 | 27 | override fun toString(): String { 28 | return buildString { 29 | adjacencies.forEach { (vertex, edges) -> 30 | val edgeString = edges.joinToString { it.destination.data.toString() } 31 | append("${vertex.data} ---> [ $edgeString ]\n") 32 | } 33 | } 34 | } 35 | 36 | fun copyVertices(graph: AdjacencyList) { 37 | graph.vertices.forEach { 38 | adjacencies[it] = arrayListOf() 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/RadixSort/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.RadixSort 2 | 3 | import example 4 | 5 | /*-----------------Key Points----------------- 6 | * Radix sort is a non-comparative sort that doesn’t rely on comparing two values. 7 | Instead, it leverages bucket sort, which is like a sieve for filtering values. A helpful 8 | analogy is how some of the vending machines accept coins — the coins are distinguished by size. 9 | 10 | * This chapter covered the least significant digit radix sort. Another way to 11 | implement radix sort is the most significant digit form. This form sorts by 12 | prioritizing the most significant digits over the lesser ones and is best illustrated 13 | by the sorting behavior of the String type.*/ 14 | fun main() { 15 | "radix sort" example { 16 | val list = arrayListOf(88, 410, 1772, 20) 17 | println("Original: $list") 18 | list.radixSort() 19 | println("Radix sorted: $list") 20 | } 21 | 22 | "digits" example { 23 | val kb = 1024 24 | println("$kb has ${1024.digits()} digits") 25 | println("and the 3rd digit is ${1024.digit(3)}") 26 | } 27 | 28 | "MSD radix sort" example { 29 | val list = (0..10).map { (Math.random() * 10000).toInt() }.toMutableList() 30 | println("Original: $list") 31 | list.lexicographicalSort() 32 | println("Radix sorted: $list") 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/AdjacencyList.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | 4 | class AdjacencyList : Graph() { 5 | 6 | val adjacencies: HashMap, ArrayList>> = HashMap() 7 | val vertices 8 | get() = ArrayList(adjacencies.keys) 9 | 10 | override fun createVertex(data: T): Vertex { 11 | val vertex = Vertex(adjacencies.count(), data) 12 | adjacencies[vertex] = ArrayList() 13 | return vertex 14 | } 15 | 16 | override fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 17 | val edge = Edge(source, destination, weight) 18 | adjacencies[source]?.add(edge) 19 | } 20 | 21 | override fun edges(source: Vertex) = adjacencies[source] ?: arrayListOf() 22 | 23 | override fun weight(source: Vertex, destination: Vertex): Double? { 24 | return edges(source).firstOrNull { it.destination == destination }?.weight 25 | } 26 | 27 | override fun toString(): String { 28 | var result = "" 29 | 30 | adjacencies.forEach { vertex, edges -> 31 | var edgeString = "" 32 | edges.forEachIndexed { index, edge -> 33 | if (index != edges.count() - 1) { 34 | edgeString += "${edge.destination.data}, " 35 | } else { 36 | edgeString += edge.destination.data 37 | } 38 | } 39 | result += "${vertex.data} ---> [ $edgeString ]\n" 40 | } 41 | 42 | return result 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/Stack.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | interface Stack { 4 | fun push(element: T) 5 | 6 | fun pop(): T? 7 | 8 | fun peek(): T? 9 | 10 | val count: Int 11 | get 12 | 13 | val isEmpty: Boolean 14 | get() = count == 0 15 | } 16 | 17 | class StackImpl : Stack { 18 | private val storage = arrayListOf() 19 | 20 | override fun toString() = buildString { 21 | appendLine("----top----") 22 | storage.asReversed().forEach { 23 | appendLine("$it") 24 | } 25 | appendLine("-----------") 26 | } 27 | 28 | override fun push(element: T) { 29 | storage.add(element) 30 | } 31 | 32 | override fun pop(): T? { 33 | if (isEmpty) { 34 | return null 35 | } 36 | return storage.removeAt(count - 1) 37 | } 38 | 39 | override fun peek(): T? { 40 | return storage.lastOrNull() 41 | } 42 | 43 | override val count: Int 44 | get() = storage.size 45 | 46 | companion object { 47 | fun create(items: Iterable): Stack { 48 | val stack = StackImpl() 49 | for (item in items) { 50 | stack.push(item) 51 | } 52 | return stack 53 | } 54 | } 55 | } 56 | 57 | fun stackOf(vararg elements: T): Stack { 58 | return StackImpl.create(elements.asList()) 59 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/DoubleStack/Stack.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.DoubleStack 2 | 3 | interface Stack { 4 | fun push(element: T) 5 | 6 | fun pop(): T? 7 | 8 | fun peek(): T? 9 | 10 | val count: Int 11 | get 12 | 13 | val isEmpty: Boolean 14 | get() = count == 0 15 | } 16 | 17 | class StackImpl : Stack { 18 | private val storage = arrayListOf() 19 | 20 | override fun toString() = buildString { 21 | appendLine("----top----") 22 | storage.asReversed().forEach { 23 | appendLine("$it") 24 | } 25 | appendLine("-----------") 26 | } 27 | 28 | override fun push(element: T) { 29 | storage.add(element) 30 | } 31 | 32 | override fun pop(): T? { 33 | if (isEmpty) { 34 | return null 35 | } 36 | return storage.removeAt(count - 1) 37 | } 38 | 39 | override fun peek(): T? { 40 | return storage.lastOrNull() 41 | } 42 | 43 | override val count: Int 44 | get() = storage.size 45 | 46 | companion object { 47 | fun create(items: Iterable): Stack { 48 | val stack = StackImpl() 49 | for (item in items) { 50 | stack.push(item) 51 | } 52 | return stack 53 | } 54 | } 55 | } 56 | 57 | fun stackOf(vararg elements: T): Stack { 58 | return StackImpl.create(elements.asList()) 59 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | /*This playground comes with an 4 | adjacency list graph and a priority queue, which you'll use to implement Dijkstra’s algorithm. 5 | The priority queue is used to store vertices that have not been visited. 6 | It’s a min-priority queue so that, every time you dequeue a vertex, it gives you vertex with the 7 | current tentative shortest path.*/ 8 | fun main() { 9 | val graph = AdjacencyList() 10 | 11 | val a = graph.createVertex("A") 12 | val b = graph.createVertex("B") 13 | val c = graph.createVertex("C") 14 | val d = graph.createVertex("D") 15 | val e = graph.createVertex("E") 16 | 17 | graph.add(EdgeType.DIRECTED, a, b, 1.0) 18 | graph.add(EdgeType.DIRECTED, a, e, 21.0) 19 | graph.add(EdgeType.DIRECTED, a, c, 12.0) 20 | graph.add(EdgeType.DIRECTED, b, d, 9.0) 21 | graph.add(EdgeType.DIRECTED, b, c, 8.0) 22 | graph.add(EdgeType.DIRECTED, d, e, 2.0) 23 | graph.add(EdgeType.DIRECTED, c, e, 2.0) 24 | 25 | /*Here, you simply create an instance of Dijkstra by passing in the graph network*/ 26 | val dijkstra = Dijkstra(graph) 27 | val pathsFromA = dijkstra.getAllShortestPath(a) 28 | 29 | pathsFromA.forEach { (vertex, path) -> 30 | println("Path to ${vertex.data} from ${a.data}") 31 | path.forEach { 32 | println("${it.source.data} --|${it.weight ?: 0.0}|--> ${it.destination.data}") 33 | } 34 | println() 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/QuicksortNaive.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | /*This implementation recursively filters the list into three partitions. 4 | * https://assets.alexandria.raywenderlich.com/books/dsk/images/e407b66b0f19f8609ea8de72706aa3ffb34177dccda604756c0273391f20b7cd/original.png 5 | * Each level corresponds with a recursive call to quicksort. Once recursion stops, the 6 | leafs are combined again, resulting in a fully sorted list 7 | 8 | * While this naive implementation is easy to understand, it raises some issues and questions: 9 | • Calling filter three times on the same list is not efficient? 10 | • Creating a new list for every partition isn’t space-efficient. Could you possibly sort in place? 11 | • Is picking the middle element the best pivot strategy? What pivot strategy should you adopt?*/ 12 | fun > List.quicksortNaive(): List { 13 | if (this.size < 2) return this // 1-There must be more than one element in the list. If not, the list is considered sorted. 14 | 15 | val pivot = this[this.size / 2] // 2-Pick the middle element of the list as your pivot. 16 | val less = this.filter { it < pivot } // 3-Using the pivot, split the original list into three partitions. or greater than the pivot go into differElements less than,equal toent buckets. 17 | val equal = this.filter { it == pivot } 18 | val greater = this.filter { it > pivot } 19 | return less.quicksortNaive() + equal + greater.quicksortNaive() // 4-Recursively sort the partitions and then combine them. 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/QuicksortMedian.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | import swapAt 4 | 5 | /*An ideal pivot would split the elements evenly between the less than and greater 6 | than partitions. Choosing the first or last element of an already sorted list as a pivot 7 | makes quicksort perform much like insertion sort, which results in a worst-case performance of O(n²). 8 | 9 | One way to address this problem is by using the median of three pivot selection strategy. 10 | Here, you find the median of the first, middle and last element in the list, and use that as a pivot. 11 | 12 | This prevents you from picking the highest or lowest element in the list.*/ 13 | fun > MutableList.quickSortMedian(low: Int, high: Int) { 14 | //Here, you find the median of this[low], this[center] and this[high] by sorting them. The median will end up at index center, which is what the function returns. 15 | if (low < high) { 16 | val pivotIndex = medianOfThree(low, high) 17 | this.swapAt(pivotIndex, high) 18 | val pivot = partitionLomuto(low, high) 19 | this.quicksortLomuto(low, pivot - 1) 20 | this.quicksortLomuto(pivot + 1, high) 21 | } 22 | } 23 | 24 | fun > MutableList.medianOfThree(low: Int, high: Int): Int { 25 | val center = (low + high) / 2 26 | if (this[low] > this[center]) { 27 | this.swapAt(low, center) 28 | } 29 | if (this[low] > this[high]) { 30 | this.swapAt(low, high) 31 | } 32 | if (this[center] > this[high]) { 33 | this.swapAt(center, high) 34 | } 35 | return center 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/Person.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.PriorityQueues 2 | 3 | 4 | /*-------------Challenge 2: Sorting------------- 5 | * Your favorite concert was sold out. Fortunately, there’s a waitlist for people who still want to go. 6 | * However, the ticket sales will first prioritize someone with a military background, followed by seniority. 7 | * Write a sort function that returns the list of people on the waitlist by the appropriate priority. 8 | * 9 | * ------------------------Solution------------------- 10 | * Given a list of people on the waitlist, you would like to prioritize the people in the following order: 11 | 1. Military background. 12 | 2. Seniority, by age. 13 | The best solution for this problem is to put the previous logic into a Comparator implementation and then use the proper priority queue 14 | implementation. In this way, you can give Person objects different priority providing different Comparator implementations.*/ 15 | 16 | 17 | 18 | data class Person( 19 | val name: String, 20 | val age: Int, 21 | val isMilitary: Boolean) { 22 | override fun toString() = name 23 | } 24 | 25 | 26 | object MilitaryPersonComparator : Comparator { 27 | override fun compare(o1: Person, o2: Person): Int { 28 | if (o1.isMilitary && !o2.isMilitary) { 29 | return 1 30 | } else if (!o1.isMilitary && o2.isMilitary) { 31 | return -1 32 | } else if (o1.isMilitary && o2.isMilitary) { 33 | return o1.age.compareTo(o2.age) 34 | } 35 | return 0 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BinarySearch/BinarySearchPlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BinarySearch 2 | 3 | import example 4 | 5 | /* Binary search is a powerful algorithm to learn, and it comes up often in 6 | programming interviews. 7 | 8 | * Whenever you read something along the lines of “Given a 9 | sorted array...”, consider using the binary search algorithm. Also, if you’re given a 10 | problem that looks like it’s going to be O(n²) to search, consider doing some upfront 11 | sorting. With upfront sorting, you can use binary searching to reduce complexity to 12 | the cost of the sort at O(n log n). 13 | 14 | 15 | -----------------------Key Points-------------------- 16 | * 1-Binary search is only a valid algorithm on sorted collections. 17 | * 2-Sometimes, it may be beneficial to sort a collection just to leverage the binary search capability for looking up elements. 18 | * 3-The indexOf method of arrays uses linear search, which has an O(n) time 19 | complexity. Binary search has an O(log n) time complexity, which scales much better for large data sets.*/ 20 | fun main(){ 21 | 22 | "Searching for a number in the array" example{ 23 | val array = arrayListOf(1, 5, 15, 17, 19, 22, 24, 31, 105, 150) 24 | val search31 = array.indexOf(31) 25 | val binarySearch31 = array.binarySearch(31) 26 | println("indexOf(): $search31") 27 | println("binarySearch(): $binarySearch31") 28 | } 29 | 30 | 31 | 32 | "binary search for a range" example{ 33 | val array = arrayListOf(1, 2, 3, 3, 3, 4, 5, 5) 34 | val indices = array.findIndices(3) 35 | println(indices) 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Stack.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | /** 4 | * The Algorithms.Dijkstras.Stack interface. 5 | */ 6 | interface Stack { 7 | 8 | /** 9 | * Push of an T into the stack.Algorithms.Dijkstras.Stack 10 | */ 11 | fun push(element: T) 12 | 13 | /** 14 | * Pops an element from the stack.Algorithms.Dijkstras.Stack if any or returns null. 15 | */ 16 | fun pop(): T? 17 | 18 | val count: Int 19 | 20 | fun peek(): T? 21 | 22 | val isEmpty: Boolean 23 | get() = count == 0 24 | } 25 | 26 | 27 | /** 28 | * Simple stack.Algorithms.Dijkstras.Stack implementation using an ArrayList 29 | */ 30 | class StackImpl : Stack { 31 | 32 | private val storage = arrayListOf() 33 | 34 | companion object { 35 | fun create(items: Iterable): Stack { 36 | val stack = StackImpl() 37 | for (item in items) { 38 | stack.push(item) 39 | } 40 | return stack 41 | } 42 | } 43 | 44 | override fun push(element: T) { 45 | storage.add(element) 46 | } 47 | 48 | override fun pop(): T? { 49 | if (isEmpty) { 50 | return null 51 | } 52 | return storage.removeAt(count - 1) 53 | } 54 | 55 | override fun peek(): T? { 56 | return storage.lastOrNull() 57 | } 58 | 59 | override val count: Int 60 | get() = storage.size 61 | 62 | override fun toString() = buildString { 63 | appendln("----top----") 64 | storage.asReversed().forEach { 65 | appendln("$it") 66 | } 67 | appendln("-----------") 68 | } 69 | } 70 | 71 | fun stackOf(vararg elements: T): Stack { 72 | return StackImpl.create(elements.asList()) 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/Ringbuffer/RingBuffer.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.Ringbuffer 2 | 3 | class RingBuffer(private val size: Int) { 4 | 5 | private var array = ArrayList(size) 6 | private var readIndex = 0 7 | private var writeIndex = 0 8 | 9 | val count: Int 10 | get() = availableSpaceForReading 11 | 12 | private val availableSpaceForReading: Int 13 | get() = (writeIndex - readIndex) 14 | 15 | val first: T? 16 | get() = array.getOrNull(readIndex) 17 | 18 | val isEmpty: Boolean 19 | get() = (count == 0) 20 | 21 | private val availableSpaceForWriting: Int 22 | get() = (size - availableSpaceForReading) 23 | 24 | val isFull: Boolean 25 | get() = (availableSpaceForWriting == 0) 26 | 27 | fun write(element: T): Boolean { 28 | return if (!isFull) { 29 | if (array.size < size) { 30 | array.add(element) 31 | } else { 32 | array[writeIndex % size] = element 33 | } 34 | writeIndex += 1 35 | true 36 | } else { 37 | false 38 | } 39 | } 40 | 41 | fun read(): T? { 42 | return if (!isEmpty) { 43 | val element = array[readIndex % size] 44 | readIndex += 1 45 | element 46 | } else { 47 | null 48 | } 49 | } 50 | 51 | override fun toString(): String { 52 | val values = (0 until availableSpaceForReading).map { offset -> 53 | "${array[(readIndex + offset) % size]!!}" 54 | } 55 | return values.joinToString(prefix = "[", separator = ", ", postfix = "]") 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/insertionsort/InsertionSort.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting.insertionsort 2 | 3 | import swapAt 4 | 5 | /*Insertion sort is a more useful algorithm. Like bubble sort and selection sort, 6 | insertion sort has an average time complexity of O(n²), but the performance of 7 | insertion sort can vary. The more the data is already sorted, the less work it needs to 8 | do. Insertion sort has a best time complexity of O(n) if the data is already sorted. 9 | 10 | Insertion sort is one of the fastest sorting algorithms when some of the data is 11 | already sorted, but this isn’t true for all sorting algorithms. In practice, a lot of data 12 | collections will already be mostly — if not entirely — sorted, and an insertion sort 13 | will perform quite well in those scenarios.*/ 14 | fun > MutableList.insertionSort(showPasses: Boolean = false) { 15 | if (this.size < 2) return 16 | // 1-Insertion sort requires you to iterate from left to right, once. This loop does that. 17 | for (current in 1 until this.size) { 18 | // 2-Here, you run backward from the current index so you can shift left as needed. 19 | for (shifting in current downTo 1) { 20 | /*3-Keep shifting the element left as long as necessary. As soon as the element is in 21 | position, break the inner loop and start with the next element.*/ 22 | if (this[shifting] < this[shifting - 1]) { 23 | this.swapAt(shifting, shifting - 1) 24 | } else { 25 | break 26 | } 27 | } 28 | /*4-This is the same trick you used with the other sort algorithms; it shows you the 29 | passes. Remember that this is not part of the sorting algorithm.*/ 30 | if(showPasses) println(this) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/Stack.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | /** 4 | * The Algorithms.DepthFirstSearch.Stack interface. 5 | */ 6 | interface Stack { 7 | 8 | /** 9 | * Push of an T into the stack.Algorithms.DepthFirstSearch.Stack 10 | */ 11 | fun push(element: T) 12 | 13 | /** 14 | * Pops an element from the stack.Algorithms.DepthFirstSearch.Stack if any or returns null. 15 | */ 16 | fun pop(): T? 17 | 18 | val count: Int 19 | get 20 | 21 | fun peek(): T? 22 | 23 | val isEmpty: Boolean 24 | get() = count == 0 25 | } 26 | 27 | 28 | /** 29 | * Simple stack.Algorithms.DepthFirstSearch.Stack implementation using an ArrayList 30 | */ 31 | class StackImpl : Stack { 32 | 33 | private val storage = arrayListOf() 34 | 35 | companion object { 36 | fun create(items: Iterable): Stack { 37 | val stack = StackImpl() 38 | for (item in items) { 39 | stack.push(item) 40 | } 41 | return stack 42 | } 43 | } 44 | 45 | override fun push(element: T) { 46 | storage.add(element) 47 | } 48 | 49 | override fun pop(): T? { 50 | if (isEmpty) { 51 | return null 52 | } 53 | return storage.removeAt(count - 1) 54 | } 55 | 56 | override fun peek(): T? { 57 | return storage.lastOrNull() 58 | } 59 | 60 | override val count: Int 61 | get() = storage.size 62 | 63 | override fun toString() = buildString { 64 | appendLine("----top----") 65 | storage.asReversed().forEach { 66 | appendLine("$it") 67 | } 68 | appendLine("-----------") 69 | } 70 | } 71 | 72 | fun stackOf(vararg elements: T): Stack { 73 | return StackImpl.create(elements.asList()) 74 | } 75 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/DoublyLinkedList/DoublyLinkedList.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.DoublyLinkedList 2 | 3 | 4 | class DoublyLinkedList { 5 | 6 | fun isEmpty(): Boolean { 7 | return head == null 8 | } 9 | 10 | private var head: Node? = null 11 | private var tail: Node? = null 12 | 13 | 14 | override fun toString(): String { 15 | if (isEmpty()) { 16 | return "Empty list" 17 | } 18 | return head.toString() 19 | } 20 | 21 | 22 | fun append(value: T) { 23 | 24 | val newNode = Node(value = value, previous = tail) 25 | if (isEmpty()) { 26 | head = newNode 27 | tail = newNode 28 | return 29 | } 30 | 31 | tail?.next = newNode 32 | 33 | tail = newNode 34 | } 35 | 36 | fun node(index: Int): Node? { 37 | // 1 38 | var currentNode = head 39 | var currentIndex = 0 40 | 41 | // 2 42 | while (currentNode != null && currentIndex < index) { 43 | currentNode = currentNode.next 44 | currentIndex += 1 45 | } 46 | 47 | return currentNode 48 | } 49 | 50 | fun remove(node: Node): T { 51 | 52 | val prev = node.previous 53 | val next = node.next 54 | 55 | if (prev != null) { 56 | prev.next = node.previous 57 | } else { 58 | head = next 59 | } 60 | 61 | next?.previous = prev 62 | 63 | if (next == null) { 64 | tail = prev 65 | } 66 | 67 | node.previous = null 68 | node.next = null 69 | 70 | return node.value 71 | } 72 | 73 | val first: Node? 74 | get() = head 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/challenges/Challenge1.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting.challenges 2 | 3 | import swapAt 4 | 5 | /*---------Challenge 1: To the left, to the left--------- 6 | * Given a list of Comparable elements, bring all instances of a given value in the list to the right side of the array. 7 | * ---------------Solution----------------- 8 | * The trick to this problem is to find the elements that need to be moved and shift 9 | everything else to the left. Then, return to the position where the element was before, and continue searching from there. 10 | * The time complexity of this solution is O(n).*/ 11 | fun > MutableList.rightAlign(element: T) { 12 | // 1-If there are less than two elements in the list, there’s nothing to do. 13 | if (this.size < 2) return 14 | /* 2-You leave the last element alone and start from the previous one. Then, you go to 15 | the left (decreasing the index), until you reach the beginning of the list when the searchIndex is 0.*/ 16 | var searchIndex = this.size - 2 17 | while (searchIndex >= 0) { 18 | // 3-You’re looking for elements that are equal to the one in the function parameter. 19 | if (this[searchIndex] == element) { 20 | /* 4-Whenever you find one, you start shifting it to the right until you reach another 21 | element equal to it or the end of the list. Remember, you already implemented swapAt(); don’t forget to increment moveIndex.*/ 22 | var moveIndex = searchIndex 23 | while (moveIndex < this.size - 1 && this[moveIndex + 1] != element) { 24 | swapAt(moveIndex, moveIndex + 1) 25 | moveIndex++ 26 | } 27 | } 28 | // 5-After you’re done with that element, move searchIndex to the left again by decrementing it. 29 | searchIndex-- 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/selectionsort/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting.selectionsort 2 | 3 | import swapAt 4 | 5 | /*Selection sort follows the basic idea of bubble sort but improves upon this 6 | algorithm by reducing the number of swapAt operations. Selection sort only swaps at 7 | the end of each pass. You’ll see how that works in the following implementation. 8 | 9 | Like bubble sort, selection sort has a worst and average time complexity of O(n²), 10 | which is fairly dismal. Unlike the bubble sort, it also has the best time complexity of 11 | O(n²). Despite this, it performs better than bubble sort because it performs only O(n) 12 | swaps — and the best thing about it is that it’s a simple one to understand. 13 | https://thumbs.gfycat.com/SnappyMasculineAmericancicada-size_restricted.gif */ 14 | fun > MutableList.selectionSort(showPasses: Boolean = false) { 15 | if (this.size < 2) return 16 | /*1-You perform a pass for every element in the collection, except for the last one. 17 | There’s no need to include the last element because if all other elements are in their correct order, the last one will be as well.*/ 18 | for (current in 0 until this.lastIndex) { 19 | var lowest = current 20 | // 2-In every pass, you go through the remainder of the collection to find the element with the lowest value. 21 | for (other in (current + 1) until this.size) { 22 | if (this[other] < this[lowest]) { 23 | lowest = other 24 | } 25 | } 26 | // 3-If that element is not the current element, swap them. 27 | if (lowest != current) { 28 | this.swapAt(lowest, current) 29 | } 30 | /*4-This optional step shows you how the list looks after each step when you call the function with showPasses set to true. 31 | You can remove this and the parameter once you understand the algorithm.*/ 32 | if (showPasses) println(this) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/AdjacencyMatrix.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | 4 | class AdjacencyMatrix : Graph() { 5 | 6 | private val vertices = arrayListOf>() 7 | private val weights = arrayListOf>() 8 | 9 | override fun createVertex(data: T): Vertex { 10 | val vertex = Vertex(vertices.count(), data) 11 | vertices.add(vertex) 12 | weights.forEach { 13 | it.add(null) 14 | } 15 | weights.add(arrayListOf()) 16 | return vertex 17 | } 18 | 19 | override fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 20 | val row = weights[source.index] 21 | if (row.size <= destination.index) { 22 | (row.size..destination.index).forEach { 23 | row.add(it, null) 24 | } 25 | } 26 | row[destination.index] = weight 27 | } 28 | 29 | override fun edges(source: Vertex): ArrayList> { 30 | val edges = arrayListOf>() 31 | (0 until weights.size).forEach { 32 | val weight = weights[source.index][it] 33 | if (weight != null) edges.add(Edge(source, vertices[it], weight)) 34 | } 35 | return edges 36 | } 37 | 38 | override fun weight(source: Vertex, destination: Vertex): Double? { 39 | return weights[source.index][destination.index] 40 | } 41 | 42 | override fun toString(): String { 43 | val verticesDescription = vertices.joinToString("\n") { "${it.index}: ${it.data}" } 44 | 45 | val grid = arrayListOf() 46 | weights.forEach { 47 | var row = "" 48 | (0 until weights.size).forEach { columnIndex -> 49 | if (columnIndex >= it.size) { 50 | row += "ø\t\t" 51 | } else { 52 | row += it[columnIndex]?.let { "$it\t" } ?: "ø\t\t" 53 | } 54 | } 55 | grid.add(row) 56 | } 57 | val edgesDescription = grid.joinToString("\n") 58 | return "$verticesDescription\n\n$edgesDescription" 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Graphs 2 | 3 | /*This chart summarizes the cost of different operations for graphs represented by 4 | adjacency lists versus adjacency matrices. 5 | https://assets.alexandria.raywenderlich.com/books/dsk/images/208c7220466ea6d6dfaa531311cc371949c2f7849e0050652a88404847cf7dd7/original.png 6 | */ 7 | fun main() { 8 | val graph = AdjacencyMatrix() 9 | 10 | val singapore = graph.createVertex("Singapore") 11 | val tokyo = graph.createVertex("Tokyo") 12 | val hongKong = graph.createVertex("Hong Kong") 13 | val detroit = graph.createVertex("Detroit") 14 | val sanFrancisco = graph.createVertex("San Francisco") 15 | val washingtonDC = graph.createVertex("Washington, DC") 16 | val austinTexas = graph.createVertex("Austin, Texas") 17 | val seattle = graph.createVertex("Seattle") 18 | 19 | graph.add(EdgeType.UNDIRECTED, singapore, hongKong, 300.0) 20 | graph.add(EdgeType.UNDIRECTED, singapore, tokyo, 500.0) 21 | graph.add(EdgeType.UNDIRECTED, hongKong, tokyo, 250.0) 22 | graph.add(EdgeType.UNDIRECTED, tokyo, detroit, 450.0) 23 | graph.add(EdgeType.UNDIRECTED, tokyo, washingtonDC, 300.0) 24 | graph.add(EdgeType.UNDIRECTED, hongKong, sanFrancisco, 600.0) 25 | graph.add(EdgeType.UNDIRECTED, detroit, austinTexas, 50.0) 26 | graph.add(EdgeType.UNDIRECTED, austinTexas, washingtonDC, 292.0) 27 | graph.add(EdgeType.UNDIRECTED, sanFrancisco, washingtonDC, 337.0) 28 | graph.add(EdgeType.UNDIRECTED, washingtonDC, seattle, 277.0) 29 | graph.add(EdgeType.UNDIRECTED, sanFrancisco, seattle, 218.0) 30 | graph.add(EdgeType.UNDIRECTED, austinTexas, sanFrancisco, 297.0) 31 | 32 | println(graph) 33 | 34 | println("San Francisco Outgoing Flights:") 35 | println("--------------------------------") 36 | graph.edges(sanFrancisco).forEach { edge -> 37 | println("from: ${edge.source.data} to: ${edge.destination.data}") 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/BasicTree/queue/Queue.kt: -------------------------------------------------------------------------------- 1 | package queue 2 | /* 3 | * Copyright (c) 2021 Razeware LLC 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, 16 | * distribute, sublicense, create a derivative work, and/or sell copies of the 17 | * Software in any work that is designed, intended, or marketed for pedagogical or 18 | * instructional purposes related to programming, coding, application development, 19 | * or information technology. Permission for such use, copying, modification, 20 | * merger, publication, distribution, sublicensing, creation of derivative works, 21 | * or sale is expressly withheld. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | * THE SOFTWARE. 30 | */ 31 | 32 | interface Queue { 33 | 34 | fun enqueue(element: T): Boolean 35 | 36 | fun dequeue(): T? 37 | 38 | val count: Int 39 | 40 | val isEmpty: Boolean 41 | get() = count == 0 42 | 43 | fun peek(): T? 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/AdjacencyMatrix.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.DepthFirstSearch 2 | 3 | 4 | class AdjacencyMatrix : Graph { 5 | 6 | private val vertices = arrayListOf>() 7 | private val weights = arrayListOf>() 8 | 9 | override fun createVertex(data: T): Vertex { 10 | val vertex = Vertex(vertices.count(), data) 11 | vertices.add(vertex) 12 | weights.forEach { 13 | it.add(null) 14 | } 15 | val row = ArrayList(vertices.count()) 16 | repeat(vertices.count()) { 17 | row.add(null) 18 | } 19 | weights.add(row) 20 | 21 | return vertex 22 | } 23 | 24 | override fun addDirectedEdge( 25 | source: Vertex, 26 | destination: Vertex, 27 | weight: Double? 28 | ) { 29 | weights[source.index][destination.index] = weight 30 | } 31 | 32 | override fun edges(source: Vertex): ArrayList> { 33 | val edges = arrayListOf>() 34 | (0 until weights.size).forEach { column -> 35 | val weight = weights[source.index][column] 36 | if (weight != null) { 37 | edges.add(Edge(source, vertices[column], weight)) 38 | } 39 | } 40 | return edges 41 | } 42 | 43 | override fun weight( 44 | source: Vertex, 45 | destination: Vertex 46 | ): Double? { 47 | return weights[source.index][destination.index] 48 | } 49 | 50 | override fun toString(): String { 51 | val verticesDescription = vertices 52 | 53 | .joinToString(separator = "\n") { "${it.index}: ${it.data}" } 54 | val grid = weights.map { row -> 55 | buildString { 56 | (0 until weights.size).forEach { columnIndex -> 57 | val value = row[columnIndex] 58 | if (value != null) { 59 | append("$value\t") 60 | } else { 61 | append("ø\t\t") 62 | } 63 | } 64 | } 65 | } 66 | val edgesDescription = grid.joinToString("\n") 67 | return "$verticesDescription\n\n$edgesDescription" 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/bubblesort/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting.bubblesort 2 | 3 | import swapAt 4 | 5 | /*Bubble sort has a best time complexity of O(n) if it’s already sorted, and a worst and 6 | average time complexity of O(n²), making it one of the least appealing sorts. 7 | 8 | Example of Bubble sort : https://www.resultswebdev.com/wp-content/themes/results-website-design/uploads/bubble-sort-animation2.gif */ 9 | fun > MutableList.bubbleSort(showPasses: Boolean = false) { 10 | /*1-There’s no need to sort the collection when it has less than two elements. 11 | One element is sorted by itself; zero elements don’t require an order.*/ 12 | if (this.size < 2) return 13 | /* 2-A single-pass will bubble the largest value to the end of the collection. Every pass 14 | needs to compare one less value than in the previous pass, so you shorten the array by one with each pass.*/ 15 | for (end in this.lastIndex downTo 1) { 16 | var swapped = false 17 | /* 3-This loop performs a single pass starting from the first element and going up 18 | until the last element not already sorted. It compares every element with the adjacent value.*/ 19 | for (current in 0 until end) { 20 | if (this[current] > this[current + 1]) { 21 | /* 4-Next, the algorithm swaps the values if needed and marks this in swapped. This is 22 | important later because it’ll allow you to exit the sort as early as you can detect the list is sorted.*/ 23 | this.swapAt(current, current + 1) 24 | swapped = true 25 | } 26 | } 27 | /* 5-This prints out how the list looks after each pass. This step has nothing to do 28 | with the sorting algorithm, but it will help you visualize how it works. You can 29 | remove it (and the function parameter) after you understand the sorting algorithm.*/ 30 | if(showPasses) println(this) 31 | // 6-If no values were swapped this pass, the collection is assumed sorted, and you can exit early. 32 | if (!swapped) return 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/HeapSort/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Heap Sort??? 3 | ![enter image description here](https://miro.medium.com/max/400/1*bPHK3V9y1EZnr6z5ZiIncQ.jpeg)\ 4 | Heapsort is another comparison-based algorithm that sorts an array in ascending order using a heap. This chapter builds on the heap concepts. 5 | Heapsort takes advantage of a heap being, by definition, a partially sorted binary tree 6 | with the following qualities: 7 | 1. In a max heap, all parent nodes are larger than their children. 8 | 2. In a min heap, all parent nodes are smaller than their children. 9 | 10 | The diagram below shows a heap with parent node values underlined: 11 | ![enter image description here](https://assets.alexandria.raywenderlich.com/books/dsk/images/bb2f31cfaca0d50ec54e076e6c969af86fbf37d88bb9710cfd382cf638e014ee/original.png)\ 12 | [Click here to see it in video!](https://www.youtube.com/watch?v=MtQL_ll5KhQ) 13 | ## ⏲ Time Complexity 14 | Even though you get the benefit of in-memory sorting, the performance of heap sort is O(n log n) for its best, worse and average cases. 15 | 16 | This is because you have to traverse the whole array once and, every time you swap elements, you must perform a sift down, which is an O(log n) operation. 17 | 18 | Heap sort is also not a stable sort because it depends on how the elements are laid out and put into the heap. If you were heap sorting a deck of cards by their rank, for example, you might see their suite change order with respect to the original deck. 19 | ## Key points 20 | - Heap sort leverages the max-heap data structure to sort elements in an array. 21 | - Heap sort sorts its elements by following a simple pattern: swap the first and last element, perform a sift-down, decrease the array size by one; then repeat. 22 | - The performance of heap sort is O(n log n) for its best, worse and average cases. 23 | ## 📒 References 24 | [RayWenderLich - heapsort](https://www.raywenderlich.com/books/data-structures-algorithms-in-kotlin/v1.0/chapters/17-heap-sort)\ 25 | [GeeksForGeeks - Youtube heap sort](https://www.youtube.com/watch?v=MtQL_ll5KhQ) 26 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/Challenge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | 4 | /*------------------Challenge 1: Using recursion----------------- 5 | * Your challenge is to implement quicksort iteratively. 6 | * -----------------Solution------------------ 7 | * You implemented quicksort recursively, which means you know what quicksort is all 8 | about. So, how you might do it iteratively? This solution uses Lomuto’s partition strategy. 9 | 10 | * This function takes in a list and the range between low and high. You’re going to 11 | leverage the stack to store pairs of start and end values. 12 | * You’re simply using the stack to store a pair of start and end indices to perform the partitions.*/ 13 | fun > MutableList.quicksortIterativeLomuto(low: Int, high: Int) { 14 | val stack = stackOf() // 1-Create a stack that stores indices. 15 | stack.push(low) // 2-Push the starting low and high boundaries on the stack to initiate the algorithm. 16 | stack.push(high) 17 | 18 | while (!stack.isEmpty) { // 3-As long as the stack is not empty, quicksort is not complete. 19 | // 4-Get the pair of start and end indices from the stack. 20 | val end = stack.pop() ?: continue 21 | val start = stack.pop() ?: continue 22 | 23 | /*5-Perform Lomuto’s partitioning with the current start and end index. Recall that 24 | Lomuto picks the last element as the pivot, and splits the partitions into three 25 | parts: elements that are less than the pivot, the pivot, and finally elements that are greater than the pivot.*/ 26 | val p = this.partitionLomuto(start, end) 27 | if ((p - 1) > start) { // 6-Once the partitioning is complete, check and add the lower bound’s start and end indices to later partition the lower half. 28 | stack.push(start) 29 | stack.push(p - 1) 30 | } 31 | if ((p + 1) < end) { // 7-Similarly, check and add the upper bound’s start and end indices to later partition the upper half. 32 | stack.push(p + 1) 33 | stack.push(end) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/AdjacencyMatrix.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.BreadthFirstSearch 2 | 3 | 4 | class AdjacencyMatrix : Graph { 5 | 6 | override val allVertices: ArrayList> 7 | get() = vertices 8 | 9 | private val vertices = arrayListOf>() 10 | private val weights = arrayListOf>() 11 | 12 | override fun createVertex(data: T): Vertex { 13 | val vertex = Vertex(vertices.count(), data) 14 | vertices.add(vertex) 15 | weights.forEach { 16 | it.add(null) 17 | } 18 | val row = ArrayList(vertices.count()) 19 | repeat(vertices.count()) { 20 | row.add(null) 21 | } 22 | weights.add(row) 23 | 24 | return vertex 25 | } 26 | 27 | override fun addDirectedEdge( 28 | source: Vertex, 29 | destination: Vertex, 30 | weight: Double? 31 | ) { 32 | weights[source.index][destination.index] = weight 33 | } 34 | 35 | override fun edges(source: Vertex): ArrayList> { 36 | val edges = arrayListOf>() 37 | (0 until weights.size).forEach { column -> 38 | val weight = weights[source.index][column] 39 | if (weight != null) { 40 | edges.add(Edge(source, vertices[column], weight)) 41 | } 42 | } 43 | return edges 44 | } 45 | 46 | override fun weight( 47 | source: Vertex, 48 | destination: Vertex 49 | ): Double? { 50 | return weights[source.index][destination.index] 51 | } 52 | 53 | override fun toString(): String { 54 | val verticesDescription = vertices 55 | 56 | .joinToString(separator = "\n") { "${it.index}: ${it.data}" } 57 | val grid = weights.map { row -> 58 | buildString { 59 | (0 until weights.size).forEach { columnIndex -> 60 | val value = row[columnIndex] 61 | if (value != null) { 62 | append("$value\t") 63 | } else { 64 | append("ø\t\t") 65 | } 66 | } 67 | } 68 | } 69 | val edgesDescription = grid.joinToString("\n") 70 | return "$verticesDescription\n\n$edgesDescription" 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/AVL/AVLNode.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Trees.AVL 2 | 3 | typealias Visitor = (T) -> Unit 4 | 5 | class AVLNode>(value: T) : TraversableBinaryNode, T>(value) { 6 | 7 | val min: AVLNode? 8 | get() = leftChild?.min ?: this 9 | 10 | /*To keep a binary tree balanced, you need a way to measure the balance of the tree. 11 | The AVL tree achieves this with a height property in each node. 12 | In tree-speak, the height of a node is the longest distance from the current node to a leaf node: 13 | https://assets.alexandria.raywenderlich.com/books/dsk/images/287b29d061fc27a7bde786bfbd558072e36f92da73dd0a323b5ec27d92e6e9ab/original.png 14 | You’ll use the relative heights of a node’s children to determine whether a particular node is balanced. 15 | 16 | The height of the left and right children of each node must differ at most by 1. 17 | This is known as the balance factor.*/ 18 | var height = 0 19 | 20 | val leftHeight: Int 21 | get() = leftChild?.height ?: -1 22 | 23 | val rightHeight: Int 24 | get() = rightChild?.height ?: -1 25 | 26 | /*The balanceFactor computes the height difference of the left and right child. If a 27 | particular child is null, its height is considered to be -1. 28 | https://www.youtube.com/watch?v=zh27Tp8HV7E*/ 29 | val balanceFactor: Int 30 | get() = leftHeight - rightHeight 31 | 32 | override fun toString() = diagram(this) 33 | 34 | private fun diagram(node: AVLNode?, 35 | top: String = "", 36 | root: String = "", 37 | bottom: String = ""): String { 38 | return node?.let { 39 | if (node.leftChild == null && node.rightChild == null) { 40 | "$root${node.value}\n" 41 | } else { 42 | diagram(node.rightChild, "$top ", "$top┌──", "$top│ ") + 43 | root + "${node.value}\n" + diagram(node.leftChild, "$bottom│ ", "$bottom└──", "$bottom ") 44 | } 45 | } ?: "${root}null\n" 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/QuicksortLomuto.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | import swapAt 4 | 5 | /*----------Lomuto Algorithm---------- 6 | Lomuto’s partitioning algorithm always chooses the last element as the pivot. Time 7 | to look at how this works in code. 8 | 9 | This function takes three arguments: 10 | • list is the list you are partitioning. 11 | • low and high set the range within the list you’ll partition. This range will get 12 | smaller and smaller with every recursion. 13 | The function returns the index of the pivot. 14 | 15 | In the naive implementation of quicksort, you created three new lists and filtered the 16 | unsorted lists three times. Lomuto’s algorithm performs the partitioning in place. 17 | That’s much more efficient*/ 18 | 19 | fun > MutableList.quicksortLomuto(low: Int, high: Int) { 20 | /*Here, you apply Lomuto’s algorithm to partition the list into two regions. You then 21 | recursively sort these regions. Recursion ends once a region has less than two elements.*/ 22 | if (low < high) { 23 | val pivot = this.partitionLomuto(low, high) 24 | this.quicksortLomuto(low, pivot - 1) 25 | this.quicksortLomuto(pivot + 1, high) 26 | } 27 | } 28 | 29 | fun > MutableList.partitionLomuto( 30 | low: Int, 31 | high: Int 32 | ): Int { 33 | val pivot = this[high] // 1-Set the pivot. Lomuto always chooses the last element as the pivot. 34 | 35 | var i = low /* 2-The variable i indicates how many elements are less than the pivot. Whenever 36 | you encounter an element that is less than the pivot, you swap it with the element at index i and increase i.*/ 37 | for (j in low until high) { // 3-Loop through all the elements from low to high, but not including high since it’s the pivot. 38 | if (this[j] <= pivot) { // 4-Check to see if the current element is less than or equal to the pivot. 39 | this.swapAt(i, j) // 5-If it is, swap it with the element at index i and increase i. 40 | i += 1 41 | } 42 | } 43 | this.swapAt(i, high) // 6-Once done with the loop, swap the element at i with the pivot. The pivot always sits between the less and greater partitions. 44 | return i // 7-Return the index of the pivot. 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/LinkedList/LinkedListIterator.kt: -------------------------------------------------------------------------------- 1 | //Again, MutableIterator is a broader interface than Iterator, so you can remove Iterator from the list of implemented interfaces. 2 | class LinkedListIterator(private val list: LinkedList) : Iterator, MutableIterator { 3 | 4 | 5 | private var index = 0 6 | private var lastNode: Node? = null 7 | 8 | override fun next(): T { 9 | 10 | // 1- You check that there are still elements to return. If there aren’t, you throw an 11 | //exception. This should never be the case if clients use the Iterator correctly, 12 | //always checking with hasNext() before trying to read a value from it with next(). 13 | if(index >= list.size) throw IndexOutOfBoundsException() 14 | 15 | // 2-If this is the first iteration, there is no lastNode set, so you take the first node of the list. 16 | // After the first iteration, you can get the next property of the last node to 17 | // find the next one. 18 | lastNode = if(index == 0){ 19 | list.nodeAt(0) 20 | }else{ 21 | lastNode?.next 22 | } 23 | 24 | // 3-Since the lastNode property was updated, you need to update the index too. 25 | //You’ve now gone through one more iteration, so the index increments 26 | index++ 27 | return lastNode!!.value 28 | } 29 | 30 | override fun hasNext(): Boolean { 31 | return index < list.size 32 | } 33 | 34 | override fun remove() { 35 | 36 | // 1- The simplest case is when you’re at the beginning of the list. Using pop() will do the trick. 37 | if (index == 1) { 38 | list.pop() 39 | } else { 40 | // 2- If the iterator is somewhere inside the list, it needs to find the previous node. That’s the only way to remove items from a linked list. 41 | val prevNode = list.nodeAt(index - 2) ?: return 42 | // 3- The iterator needs to step back so that next() returns the correct method the next time. This means reassigning the lastNode and decreasing the index. 43 | list.removeAfter(prevNode) 44 | lastNode = prevNode 45 | } 46 | index-- 47 | 48 | } 49 | 50 | 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/AdjacencyList.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Graphs 2 | 3 | //Here, you’ve defined an AdjacencyList that uses a map to store the edges. 4 | class AdjacencyList : Graph { 5 | 6 | private val adjacencies: HashMap, ArrayList>> = HashMap() 7 | 8 | //Here, you create a new vertex and return it. In the adjacency list, you store an empty list of edges for this new vertex. 9 | override fun createVertex(data: T): Vertex { 10 | val vertex = Vertex(adjacencies.count(), data) 11 | adjacencies[vertex] = ArrayList() 12 | return vertex 13 | } 14 | 15 | //This method creates a new edge and stores it in the adjacency list. 16 | override fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 17 | val edge = Edge(source, destination, weight) 18 | adjacencies[source]?.add(edge) 19 | } 20 | 21 | /*Retrieving the outgoing edges from a vertex. 22 | This is a straightforward implementation: You either return the stored edges or an empty list if the source vertex is unknown.*/ 23 | override fun edges(source: Vertex) = adjacencies[source] ?: arrayListOf() 24 | 25 | /*Retrieving the weight of an edge 26 | * Here, you find the first edge from source to destination; if there is one, you return its weight.*/ 27 | override fun weight(source: Vertex, destination: Vertex): Double? { 28 | return edges(source).firstOrNull { it.destination == destination }?.weight 29 | } 30 | 31 | override fun toString(): String { 32 | return buildString {//1-You'll be assembling the result using buildString(), which places you inside the scope of a StringBuilder, and returns whatever you've built. 33 | adjacencies.forEach { (vertex, edges) -> //2-You loop through every key-value pair in adjacencies. 34 | val edgeString = edges.joinToString { it.destination.data.toString() } //3-For every vertex, you create a string representation of all its outgoing edges. joinToString() gives you a neat, comma-separated list of the items. 35 | append("${vertex.data} ---> [ $edgeString ]\n") //4-Finally, for every vertex, you append both the vertex itself and its outgoing edges to the StringBuilder that buildString() provides you with. 36 | } 37 | } 38 | } 39 | 40 | 41 | 42 | 43 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/BasicTree/list/ArrayListQueue.kt: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | /* 4 | * Copyright (c) 2021 Razeware LLC 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, 17 | * distribute, sublicense, create a derivative work, and/or sell copies of the 18 | * Software in any work that is designed, intended, or marketed for pedagogical or 19 | * instructional purposes related to programming, coding, application development, 20 | * or information technology. Permission for such use, copying, modification, 21 | * merger, publication, distribution, sublicensing, creation of derivative works, 22 | * or sale is expressly withheld. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | * THE SOFTWARE. 31 | */ 32 | import queue.Queue 33 | 34 | class ArrayListQueue : Queue { 35 | 36 | private val list = arrayListOf() 37 | 38 | override fun enqueue(element: T): Boolean { 39 | list.add(element) 40 | return true 41 | } 42 | 43 | override fun dequeue(): T? { 44 | return if (isEmpty) null else list.removeAt(0) 45 | } 46 | 47 | override val count: Int 48 | get() = list.size 49 | 50 | override fun peek(): T? = list.getOrNull(0) 51 | 52 | override fun toString(): String { 53 | return list.toString() 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.On2Sorting 2 | 3 | import Algorithms.On2Sorting.bubblesort.bubbleSort 4 | import Algorithms.On2Sorting.challenges.biggestDuplicate 5 | import Algorithms.On2Sorting.challenges.rev 6 | import Algorithms.On2Sorting.challenges.rightAlign 7 | import example 8 | import Algorithms.On2Sorting.insertionsort.insertionSort 9 | import Algorithms.On2Sorting.selectionsort.selectionSort 10 | 11 | 12 | /*---------------------Key Points-------------------- 13 | * n² algorithms often have a terrible reputation, but some of these algorithms 14 | usually have some redeeming points. insertionSort can sort in O(n) time if the 15 | collection is already in sorted order and gradually scales down to O(n²). 16 | * insertionSort is one of the best sorts in situations wherein you know ahead of time that your data is in sorted order. 17 | * Design your algorithms to be as generic as possible without hurting the performance.*/ 18 | fun main(){ 19 | 20 | 21 | "bubble sort" example { 22 | 23 | val list = arrayListOf(9, 4, 10, 3) 24 | println("Original: $list") 25 | list.bubbleSort(true) 26 | println("Bubble sorted: $list") 27 | } 28 | "selection sort" example { 29 | val list = arrayListOf(9, 4, 10, 3) 30 | println("Original: $list") 31 | list.selectionSort(true) 32 | println("Bubble sorted: $list") 33 | } 34 | "insertion sort" example { 35 | val list = arrayListOf(9, 4, 10, 3) 36 | println("Original: $list") 37 | list.insertionSort(true) 38 | println("Bubble sorted: $list") 39 | } 40 | "align right" example { 41 | val list = mutableListOf(3, 4, 134, 3, 3, 5, 6, 3, 5, 6, 1, 0) 42 | println("Original: $list") 43 | val element = 3 44 | list.rightAlign(element) 45 | println("Right align element $element: $list") 46 | } 47 | "biggest duplicate" example { 48 | val list = mutableListOf(3, 4, 14, 23, 134, 5, 36, 31, 15, 6, 1, 0) 49 | println("Original: $list") 50 | val element = list.biggestDuplicate() 51 | println("Biggest duplicate element: $element") 52 | } 53 | 54 | "reverse" example { 55 | val list = mutableListOf(3, 4, 14, 0, 23, 134, 5, 36, 31, 15, 6, 1, 0) 56 | println("Original: $list") 57 | list.rev() 58 | println("Reverse: $list") 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BreadthFirstSearch/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Breadth First Search??? 3 | ![MEME!](https://memegenerator.net/img/instances/18829555.jpg)\ 4 | Traversal means visiting all the nodes of a graph. Breadth First Traversal or Breadth First Search is a recursive algorithm for searching all the vertices of a graph or tree data structure. 5 | 6 | 7 | ### BFS algorithm 8 | 9 | A standard BFS implementation puts each vertex of the graph into one of two categories: 10 | 11 | 1. Visited 12 | 2. Not Visited 13 | 14 | The purpose of the algorithm is to mark each vertex as visited while avoiding cycles. 15 | 16 | The algorithm works as follows: 17 | 18 | 1. Start by putting any one of the graph's vertices at the back of a queue. 19 | 2. Take the front item of the queue and add it to the visited list. 20 | 3. Create a list of that vertex's adjacent nodes. Add the ones which aren't in the visited list to the back of the queue. 21 | 4. Keep repeating steps 2 and 3 until the queue is empty. 22 | 23 | The graph might have two different disconnected parts so to make sure that we cover every vertex, we can also run the BFS algorithm on every node 24 | 25 | ![enter image description here](https://upload.wikimedia.org/wikipedia/commons/5/5d/Breadth-First-Search-Algorithm.gif?20100504223639) 26 | 27 | ## ⏲ Time Complexity 28 | When traversing a graph using BFS, each vertex is enqueued once. This has a time complexity of O(V). 29 | 30 | During this traversal, you also visit all of the edges. 31 | 32 | The time it takes to visit all edges is O(E). This means that the overall time complexity for breadth-first search is O(V + E). 33 | 34 | The space complexity of BFS is O(V) since you have to store the vertices in three separate structures: queue, enqueued and visited. 35 | 36 | ## Key points 37 | - Breadth-first search (BFS) is an algorithm for traversing or searching a graph. 38 | - BFS explores all of the current vertex’s neighbors before traversing the next level of vertices. 39 | - It’s generally good to use this algorithm when your graph structure has a lot of neighboring vertices or when you need to find out every possible outcome. 40 | - The queue data structure is used to prioritize traversing a vertex’s neighboring edges before diving down a level deeper. 41 | 42 | ## 📒 References 43 | [Programiz - Breadth First Search](https://www.programiz.com/dsa/graph-bfs)\ 44 | [RayWenderLich - Breadth first search](https://www.raywenderlich.com/books/data-structures-algorithms-in-kotlin/v1.0/chapters/20-breadth-first-search) 45 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/RadixSort/radixSort.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.RadixSort 2 | 3 | /*Radix sort is a non-comparative algorithm for sorting integers in linear time. There 4 | are many implementations of radix sort that focus on different problems. To keep 5 | things simple, you’ll focus on sorting base 10 integers while investigating the least 6 | significant digit (LSD) variant of radix sort. 7 | 8 | https://www.youtube.com/watch?v=nu4gDuFabIM 9 | 10 | Radix sort is one of the fastest sorting algorithms. The average time complexity of 11 | radix sort is O(k ×n), where k is the number of significant digits of the largest 12 | number, and n is the number of integers in the list. 13 | 14 | Radix sort works best when k is constant, which occurs when all numbers in the list 15 | have the same count of significant digits. Its time complexity then becomes O(n). 16 | Radix sort also incurs an O(n) space complexity, as you need space to store each bucket.*/ 17 | 18 | //Here, you’ve added radixSort() to MutableList of integers via an extension function. 19 | fun MutableList.radixSort() { 20 | // 1-You’re sorting base 10 integers in this instance. Since you’ll use this value many times in the algorithm, you store it in a constant base. 21 | val base = 10 22 | /*2-You declare two variables to track your progress. Radix sort works in many 23 | passes, so done serves as a flag that determines whether the sort is complete. 24 | The digits variable keeps track of the current digit you’re looking at.*/ 25 | var done = false 26 | var digits = 1 27 | while (!done) { 28 | done = true 29 | //1-You instantiate the buckets using a two-dimensional list. Because you’re using base 10, you need ten buckets. 30 | val buckets = MutableList>(base) { mutableListOf() } 31 | //2-You place each number in the correct bucket. 32 | this.forEach { number -> 33 | val remainingPart = number / digits 34 | val digit = remainingPart % base 35 | buckets[digit].add(number) 36 | 37 | if (remainingPart > 0) { 38 | done = false 39 | } 40 | } 41 | /*3-You update digits to the next digit you want to inspect and update the list using 42 | the contents of buckets. flatten() flattens the two-dimensional list to a one- 43 | dimensional list, as if you’re emptying the buckets into the list.*/ 44 | digits *= base 45 | this.clear() 46 | this.addAll(buckets.flatten()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Heap/README.md: -------------------------------------------------------------------------------- 1 | # What is Heap???? 2 | 3 | ![enter image description here](https://miro.medium.com/max/1240/1*bwRWOhFtfK9xdVbpIGOm4A.png)\ 4 | Heap data structure is [a complete binary tree](https://www.programiz.com/dsa/complete-binary-tree) that satisfies **the heap property**, where any given node is 5 | 6 | - always greater than its child node/s and the key of the root node is the largest among all other nodes. This property is also called **max heap property**. 7 | - always smaller than the child node/s and the key of the root node is the smallest among all other nodes. This property is also called **min heap property**. 8 | ![Max Heap](https://cdn.programiz.com/cdn/farfuture/OTLuUbQZmYPjHkXgmCfzHr8nNCkoi2Je9y9ZzIl1vuI/mtime:1582112622/sites/tutorial2program/files/maxheap_1.png) 9 | 10 | > Max Heap 11 | 12 | ![Min Heap](https://cdn.programiz.com/cdn/farfuture/tVzREVaraXbOKPPJtMbcQ10N2QkxiAJcNOIfxPYlVR0/mtime:1582112622/sites/tutorial2program/files/minheap_0.png) 13 | > Min Heap 14 | 15 | 16 | 17 | ## Heap applications 18 | Some useful applications of a heap include: 19 | - Calculating the minimum or maximum element of a collection. 20 | - Heap sort. 21 | - Implementing a priority queue. 22 | - Supporting graph algorithms, like Prim’s or Dijkstra’s, with a priority queue. 23 | 24 | **Common heap operations** 25 | ![enter image description here](https://i.imgur.com/TFXOclU.png) 26 | 27 | Here you have a generic Collection interface with the basic property count which returns the number of elements and the boolean property isEmpty which just tests if the count is 0. 28 | It also contains the classical operations of inserting and deletion. 29 | 30 | 31 | ## Pros & Cons 32 | ![enter image description here](https://i.imgur.com/2TwCDpG.png) 33 | 34 | ## Key points 35 | - Here’s a summary of the algorithmic complexity of the heap operations implemented in this package: 36 | ![enter image description here](https://assets.alexandria.raywenderlich.com/books/dsk/images/60c605f35bc4d8eeb918e823645d47529e29ba5ca655516fbde640d3bd7d3c44/original.png) 37 | - The heap data structure is good for maintaining the highest or lowest priority element. 38 | - Every time you insert or remove items from the heap, you must check to see if it satisfies the rules of the priority. 39 | ## 📒 References 40 | [Stack Vs Heap - Advantages and disadvantages](https://drasticcode.com/stack-vs-heap-what-is-stack/#Advantages_and_disadvantages_of_heap)\ 41 | [GeeksForGeeks - Heap](https://www.geeksforgeeks.org/heap-data-structure/)\ 42 | [Programiz - Heap](https://www.programiz.com/dsa/heap-data-structure) 43 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Heap/HeapPlayGround.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Heap 2 | 3 | import example 4 | 5 | fun main(){ 6 | 7 | "Max-Heap" example{ 8 | //this code in order to create a max-heap of Comparable objects represented by Int values. 9 | val array = arrayListOf(1, 12, 3, 4, 1, 6, 8, 7) // 1-create an ArrayList of Ints 10 | val priorityQueue = ComparableHeapImpl.create(array) // 2-using the array in order to create a ComparableHeapImpl 11 | while (!priorityQueue.isEmpty) { // 3-remove and print the max value until the Heap is empty 12 | println(priorityQueue.remove()) 13 | } 14 | } 15 | 16 | 17 | 18 | 19 | 20 | "create a min-heap" example{ 21 | val array = arrayListOf(1, 12, 3, 4, 1, 6, 8, 7) // 1-create an ArrayList of Ints 22 | val inverseComparator = object : Comparator { // 2-create an implementation of the Comparator which implements the inverse order for Int 23 | override fun compare(o1: Int, o2: Int): Int = 24 | o2.compareTo(o1) 25 | } 26 | val minHeap = ComparatorHeapImpl.create(array, 27 | inverseComparator) // 3-using the array and the comparator in order to create a ComparatorHeapImpl 28 | while (!minHeap.isEmpty) { // 4-remove and print the value with highest priority (whose value this time is the lowest) until the Heap is empty 29 | println(minHeap.remove()) 30 | } 31 | } 32 | 33 | } 34 | 35 | 36 | 37 | 38 | 39 | /*----------Challenge 1: Find the nth smallest value--------- 40 | * Write a function to find the nth smallest integer in an unsorted array. 41 | * --------------Solution------------- 42 | * There are many ways to solve for the nth smallest integer in an unsorted array. For 43 | example, you could choose a sorting algorithm you learned in this chapter, sort the 44 | array, and grab the element at the nth index. 45 | 46 | * Building a heap takes O(n). Every element removal from the heap takes O(log n). 47 | Keep in mind that you’re also doing this n times. The overall time complexity is O(n 48 | log n).*/ 49 | fun getNthSmallestElement(n: Int, elements: ArrayList): Int? { 50 | if (n <= 0 || elements.isEmpty()) return null 51 | 52 | val heap = ComparableHeapImpl.create(arrayListOf()) 53 | 54 | elements.forEach { 55 | val maxElement = heap.peek() 56 | if (heap.count < n) { 57 | heap.insert(it) 58 | } else if (maxElement != null && maxElement > it) { 59 | heap.remove() 60 | heap.insert(it) 61 | } 62 | } 63 | 64 | return heap.peek() 65 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/RadixSort/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Radix Sort??? 3 | ![enter image description here](https://i.redd.it/hnnial6x6x411.jpg)\ 4 | Radix sort is [a sorting algorithm](https://www.programiz.com/dsa/sorting-algorithm) that sorts the elements by first grouping the individual digits of the same **place value**. Then, sort the elements according to their increasing/decreasing order. 5 | 6 | Suppose, we have an array of 8 elements. First, we will sort elements based on the value of the unit place. Then, we will sort elements based on the value of the tenth place. This process goes on until the last significant place. 7 | 8 | Let the initial array be `[121, 432, 564, 23, 1, 45, 788]`. It is sorted according to radix sort as shown in the figure below. 9 | 10 | ![Radix Sort Working](https://cdn.programiz.com/cdn/farfuture/GKQPB3dxbVfvYT3qiSZtTQDI5UOENnLr-oTPlCbYKaM/mtime:1582112622/sites/tutorial2program/files/Radix-sort-0_0.png "Working of Radix Sort") 11 | 12 | Working of Radix Sort 13 | 14 | ![enter image description here](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9mdW5nbm90bC1pbWcub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS8lRTYlQTElQjYlRTYlOEUlOTIlRTUlQkElOEYvQ291bnRTb3J0LmdpZg) 15 | ## ⏲ Time Complexity 16 | ![enter image description here](https://i.imgur.com/2d0Qj7t.png)\ 17 | Since radix sort is a non-comparative algorithm, it has advantages over comparative sorting algorithms. 18 | 19 | For the radix sort that uses counting sort as an intermediate stable sort, the time complexity is O(d(n+k)). 20 | 21 | Here, d is the number cycle and O(n+k) is the time complexity of counting sort. 22 | 23 | Thus, radix sort has linear time complexity which is better than O(nlog n) of comparative sorting algorithms. 24 | 25 | If we take very large digit numbers or the number of other bases like 32-bit and 64-bit numbers then it can perform in linear time however the intermediate sort takes large space. 26 | 27 | This makes radix sort space inefficient. This is the reason why this sort is not used in software libraries. 28 | ## Key points 29 | - Radix sort is a non-comparative sort that doesn’t rely on comparing two values. 30 | Instead, it leverages bucket sort, which is like a sieve for filtering values. A helpful analogy is how some of the vending machines accept coins — the coins are distinguished by size. 31 | - This chapter covered the least significant digit radix sort. Another way to implement radix sort is the most significant digit form. This form sorts by prioritizing the most significant digits over the lesser ones and is best illustrated by the sorting behavior of the String type. 32 | ## 📒 References 33 | [Programiz - Radix Sort](https://www.programiz.com/dsa/radix-sort) 34 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/Ringbuffer/RingBufferQueue.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.Ringbuffer 2 | 3 | import DataStructure.Queues.Queue 4 | 5 | 6 | /* A ring buffer, also known as a circular buffer, is a fixed-size array. 7 | * This data structure strategically wraps around to the beginning when there are no more items to remove at the end. 8 | * You’ll notice a RingBuffer class inside this package, which you can look at to understand its internal mechanics.*/ 9 | 10 | /* How does the ring-buffer implementation compare? Let’s look at a summary of the algorithmic and storage complexity. 11 | https://assets.alexandria.raywenderlich.com/books/dsk/images/a2f9ece55f108c9585d2a748dde0a89a5a8e318215a16378e0f29d5f46f56a02/original.png 12 | * The ring-buffer-based queue has the same time complexity for enqueue and dequeue as the linked-list implementation. 13 | * The only difference is the space complexity. The ring buffer has a fixed size, which means that enqueue can fail.*/ 14 | 15 | 16 | /* Here, you define a generic RingBufferQueue. Note that you must include a size parameter since the ring buffer has a fixed size. 17 | * To implement the Queue interface, you also need to implement peek() and the count property using the same from the RingBuffer class. 18 | * Once you provide the count property, the isEmpty property is already defined in the Queue interface. 19 | * Instead of exposing ringBuffer, you provide these helpers to access the front of the queue and to check if the queue is empty. Both of these are O(1) operations.*/ 20 | class RingBufferQueue(size: Int) : Queue { 21 | 22 | private val ringBuffer: RingBuffer = RingBuffer(size) 23 | 24 | override val count: Int 25 | get() = ringBuffer.count 26 | 27 | override fun peek(): T? = ringBuffer.first 28 | 29 | 30 | 31 | /* To append an element to the queue, you call write() on the ringBuffer. This increments the write pointer by one. 32 | * Since the queue has a fixed size, you must now return true or false to indicate whether the element has been successfully added. 33 | * enqueue() is still an O(1) operation.*/ 34 | override fun enqueue(element: T): Boolean = 35 | ringBuffer.write(element) 36 | 37 | 38 | 39 | /* To remove an item from the front of the queue. 40 | * This code checks if the queue is empty and, if so, returns null. 41 | * If not, it returns an item from the front of the buffer. 42 | * Behind the scenes, the ring buffer increments the read pointer by one.*/ 43 | override fun dequeue(): T? = 44 | if (isEmpty) null else ringBuffer.read() 45 | 46 | 47 | 48 | 49 | /*This code creates a string representation of the queue by delegating to the underlying ring buffer.*/ 50 | override fun toString(): String = ringBuffer.toString() 51 | 52 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/DoublyLinkedList/LinkedListQueue.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.DoublyLinkedList 2 | 3 | import DataStructure.Queues.Queue 4 | import LinkedList 5 | 6 | /* Doubly linked list implementation 7 | You should already be familiar with linked lists from Chapter 3, “Linked Lists”. 8 | A doubly linked list is simply a linked list in which nodes also contain a reference to the previous node.*/ 9 | 10 | 11 | /* One of the DataStructure.LinkedList.main problems with ArrayListQueue is that dequeuing an item takes linear time. 12 | * With the linked list implementation, you reduced it to a constant operation, O(1). 13 | * All you needed to do was update the node’s previous and next pointers. 14 | * The DataStructure.LinkedList.main weakness with LinkedListQueue is not apparent from the table. 15 | * Despite O(1) performance, it suffers from high overhead. 16 | * Each element has to have extra storage for the forward and back reference. 17 | * Moreover, every time you create a new element, it requires a relatively expensive dynamic allocation. 18 | * By contrast, ArrayListQueue does bulk allocation, which is faster. 19 | * https://assets.alexandria.raywenderlich.com/books/dsk/images/d509e4b840159746506a709f304c58a672a4bde827872861e1d629f85c6f8944/original.png*/ 20 | class LinkedListQueue : Queue { 21 | 22 | private val list = DoublyLinkedList() 23 | 24 | private var size = 0 25 | 26 | override val count: Int 27 | get() = size 28 | 29 | 30 | //Behind the scenes, the doubly linked list will update its tail node’s previous and next references to the new node. 31 | // You also increment the size. This is an O(1) operation. 32 | override fun enqueue(element: T): Boolean { 33 | list.append(element) 34 | size++ 35 | return true 36 | } 37 | 38 | 39 | 40 | 41 | //This code checks to see if the first element of the queue exists. If it doesn’t, it returns null. 42 | //Otherwise, it removes and returns the element at the front of the queue. In this case it also decrement the size. 43 | override fun dequeue(): T? { 44 | val firstNode = list.first ?: return null 45 | size-- 46 | return list.remove(firstNode) 47 | } 48 | /*Removing from the front of the list is also an O(1) operation. Compared to the ArrayList implementation, you didn’t have to shift elements one by one. 49 | https://assets.alexandria.raywenderlich.com/books/dsk/images/5ee3c7e50edd03a7effa7abd40d895e93ce5a26518809806dc4364276204eb5c/original.png 50 | Instead, in the diagram above, you simply update the next and previous pointers between the first two nodes of the linked list.*/ 51 | 52 | 53 | 54 | override fun toString(): String = list.toString() 55 | 56 | override fun peek(): T? = list.first?.value 57 | 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/AdjacencyMatrix.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Graphs 2 | 3 | 4 | 5 | /*Here, you’ve defined an AdjacencyMatrix that contains an array of vertices and an 6 | adjacency matrix to keep track of the edges and their weights.*/ 7 | class AdjacencyMatrix : Graph { 8 | 9 | private val vertices = arrayListOf>() 10 | private val weights = arrayListOf>() 11 | 12 | override fun createVertex(data: T): Vertex { 13 | val vertex = Vertex(vertices.count(), data) 14 | vertices.add(vertex) //1-Add a new vertex to the array. 15 | weights.forEach { 16 | it.add(null) //2-Append a null weight to every row in the matrix, as none of the current vertices have an edge to the new vertex. 17 | } 18 | val row = ArrayList(vertices.count()) 19 | repeat(vertices.count()) { 20 | row.add(null) 21 | } 22 | weights.add(row) //3-Add a new row to the matrix. This row holds the outgoing edges for the new vertex. You put a null value in this row for each vertex that your graph stores. 23 | return vertex 24 | } 25 | 26 | override fun addDirectedEdge( 27 | source: Vertex, 28 | destination: Vertex, 29 | weight: Double? 30 | ) { 31 | weights[source.index][destination.index] = weight 32 | } 33 | 34 | /*To retrieve the outgoing edges for a vertex, you search the row for this vertex in the 35 | matrix for weights that are not null. 36 | 37 | Every non-null weight corresponds with an outgoing edge. The destination is the 38 | vertex that corresponds with the column in which the weight was found.*/ 39 | override fun edges(source: Vertex): ArrayList> { 40 | val edges = arrayListOf>() 41 | (0 until weights.size).forEach { column -> 42 | val weight = weights[source.index][column] 43 | if (weight != null) { 44 | edges.add(Edge(source, vertices[column], weight)) 45 | } 46 | } 47 | return edges 48 | } 49 | 50 | override fun weight( 51 | source: Vertex, 52 | destination: Vertex 53 | ): Double? { 54 | return weights[source.index][destination.index] 55 | } 56 | 57 | override fun toString(): String { 58 | //1-You first create a list of the vertices. 59 | val verticesDescription = vertices 60 | .joinToString(separator = "\n") { "${it.index}: ${it.data}" } 61 | 62 | //2-Then, you build up a grid of weights, row by row. 63 | val grid = weights.map { row -> 64 | buildString { 65 | (0 until weights.size).forEach { columnIndex -> 66 | val value = row[columnIndex] 67 | if (value != null) { 68 | append("$value\t") 69 | } else { 70 | append("ø\t\t") 71 | } 72 | } 73 | } 74 | } 75 | 76 | val edgesDescription = grid.joinToString("\n") 77 | //3-Finally, you join both descriptions together and return them. 78 | return "$verticesDescription\n\n$edgesDescription" 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/QuicksortDutchFlag.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | /*A problem with Lomuto’s and Hoare’s algorithms is that they don’t handle duplicates 4 | really well. With Lomuto’s algorithm, duplicates end up in the less than partition and 5 | aren’t grouped together. With Hoare’s algorithm, the situation is even worse as duplicates can be all over the place. 6 | 7 | A solution to organize duplicate elements is using Dutch national flag partitioning. 8 | This technique is named after the Dutch flag, which has three bands of colors: red, white and blue. 9 | This is similar to how you create three partitions. 10 | 11 | Dutch national flag partitioning is a good technique to use if you have a lot of duplicate elements. 12 | 13 | You’ll adopt the same strategy as Lomuto’s partition by choosing the last element as the pivotIndex.*/ 14 | fun> MutableList.partitionDutchFlag(low: Int, high: Int, pivotIndex: Int): Pair { 15 | val pivot = this[pivotIndex] 16 | /*1- Whenever you encounter an element that is less than the pivot, move it to index smaller. 17 | This means that all elements that come before this index are less than the pivot.*/ 18 | var smaller = low 19 | /*2- Index equal points to the next element to compare. Elements that are equal to 20 | the pivot are skipped, which means that all elements between smaller and equal are equal to the pivot.*/ 21 | var equal = low 22 | /*3-Whenever you encounter an element that is greater than the pivot, move it to index larger. 23 | This means that all elements that come after this index are greater than the pivot.*/ 24 | var larger = high 25 | 26 | /*4- The main loop compares elements and swaps them if needed. 27 | This continues until index equal moves past index larger, meaning all elements have been moved to their correct partition.*/ 28 | while (equal <= larger) { 29 | if (this[equal] < pivot) { 30 | this.swapAt(smaller, equal) 31 | smaller += 1 32 | equal += 1 33 | } else if (this[equal] == pivot) { 34 | equal += 1 35 | } else { 36 | this.swapAt(equal, larger) 37 | larger -= 1 38 | } 39 | } 40 | /*5- The algorithm returns indices smaller and larger. These point to the first and last elements of the middle partition.*/ 41 | return Pair(smaller, larger) 42 | } 43 | 44 | 45 | /*Notice how recursion uses the middleFirst and middleLast indices to determine 46 | the partitions that need to be sorted recursively. Because the elements equal to the 47 | pivot are grouped together, they can be excluded from the recursion.*/ 48 | fun> MutableList.quicksortDutchFlag(low: 49 | Int, high: Int) { 50 | if (low < high) {val middle = partitionDutchFlag(low, high, high) 51 | this.quicksortDutchFlag(low, middle.first - 1) 52 | this.quicksortDutchFlag(middle.second + 1, high) 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/QuickSort/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.QuickSort 2 | 3 | /*Quicksort is another comparison-based sorting algorithm. Much like merge sort, it 4 | uses the same strategy of divide and conquer. One important feature of quicksort is 5 | choosing a pivot point. The pivot divides the list into three partitions 6 | 7 | ------------------Challenge 2: Provide an explanation--------------- 8 | Explain when and why you would use merge sort over quicksort. 9 | -----------------Solution---------------- 10 | • Merge sort is preferable over quicksort when you need stability. Merge sort is a 11 | stable sort and guarantees O(n log n). This is not the case with quicksort, which isn’t stable and can perform as bad as O(n²). 12 | 13 | • Merge sort works better for larger data structures or data structures where 14 | elements are scattered throughout memory. Quicksort works best when elements are stored in a contiguous block. 15 | 16 | 17 | ----------------------Key Points---------------------- 18 | * The naive partitioning creates a new list on every filter function; this is inefficient. All other strategies sort in place. 19 | * Lomuto’s partitioning chooses the last element as the pivot. 20 | * Hoare’s partitioning chooses the first element as its pivot. 21 | * An ideal pivot would split the elements evenly between partitions. 22 | * Choosing a bad pivot can cause quicksort to perform in O(n²). 23 | * Median of three finds the pivot by taking the median of the first, middle and last element. 24 | * Dutch national flag partitioning strategy helps to organize duplicate elements in a more efficient way.*/ 25 | fun main() { 26 | 27 | "Naive quicksort" example { 28 | val list = arrayListOf(12, 0, 3, 9, 2, 18, 8, 27, 1, 5, 8, -1, 21) 29 | println("Original: $list") 30 | val sorted = list.quicksortNaive() 31 | println("Sorted: $sorted") 32 | } 33 | "Lomuto quicksort" example { 34 | val list = arrayListOf(12, 0, 3, 9, 2, 21, 18, 27, 1, 5, 8, -1, 8) 35 | println("Original: $list") 36 | list.quicksortLomuto(0, list.size - 1) 37 | println("Sorted: $list") 38 | } 39 | "Hoare quicksort" example { 40 | val list = arrayListOf(12, 0, 3, 9, 2, 21, 18, 27, 1, 5, 8, -1, 8) 41 | println("Original: $list") 42 | list.quicksortHoare( 0, list.size - 1) 43 | println("Sorted: $list") 44 | } 45 | "Median of three quicksort" example { 46 | val list = arrayListOf(12, 0, 3, 9, 2, 21, 18, 27, 1, 5, 8, -1, 8) 47 | println("Original: $list") 48 | list.quickSortMedian( 0, list.size - 1) 49 | println("Sorted: $list") 50 | } 51 | "Dutch flag quicksort" example { 52 | val list = arrayListOf(12, 0, 3, 9, 2, 21, 18, 27, 1, 5, 8, -1, 8) 53 | println("Original: $list") 54 | list.quicksortDutchFlag( 0, list.size - 1) 55 | println("Sorted: $list") 56 | } 57 | "Iterative Lomuto quicksort" example { 58 | val list = arrayListOf(12, 0, 3, 9, 2, 21, 18, 27, 1, 5, 8, -1, 8) 59 | println("Original: $list") 60 | list.quicksortIterativeLomuto( 0, list.size - 1) 61 | println("Sorted: $list") 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Queues/ArrayListQueue/ArrayListQueue.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Queues.ArrayListQueue 2 | 3 | import DataStructure.Queues.Queue 4 | 5 | 6 | /* Here’s a summary of the algorithmic and storage complexity of the ArrayList-based queue implementation. 7 | Most of the operations are constant time except for dequeue(), which takes linear time. Storage space is also linear. 8 | https://assets.alexandria.raywenderlich.com/books/dsk/images/c15c2bc2e699d4edd2a0672a19138cedfbe2db27d8267796622e39a266e624a6/original.png 9 | There are some shortcomings to the implementation. Removing an item from the front of the queue can be inefficient, as removal causes all elements to shift up by one. This makes a difference for very large queues. 10 | Once the list gets full, it has to resize and may have unused space. This could increase your memory footprint over time. 11 | Is it possible to address these shortcomings? Let’s look at a linked list-based implementation and compare it to an ArrayListQueue.*/ 12 | 13 | class ArrayListQueue : Queue { 14 | 15 | 16 | /*After adding multiple elements, the internal array of the ArrayList will eventually be full. 17 | When you want to use more than the allocated space, the array must resize to make additional room. 18 | 19 | Resizing is an O(n) operation. Resizing requires the list to allocate new memory and copy all existing data over to the new list. 20 | Since this doesn’t happen very often (thanks to doubling the size each time), the complexity still works out to be an amortized O(1).*/ 21 | private val list = arrayListOf() 22 | 23 | 24 | 25 | /*Using the features of ArrayList, you get the following for free: 26 | 27 | * Get the size of the queue using the same property of the list. 28 | * Return the element at the front of the queue, if there is any. 29 | 30 | These operations are all O(1).*/ 31 | 32 | override val count: Int 33 | get() = list.size 34 | 35 | override fun peek(): T? = list.getOrNull(0) 36 | 37 | 38 | 39 | //Adding an element to the back of the queue is easy. You simply add the element to the end of the ArrayList 40 | //Regardless of the size of the list, enqueueing an element is an O(1) operation. This is because the list has empty space at the back. 41 | override fun enqueue(element: T): Boolean { 42 | list.add(element) 43 | return true 44 | } 45 | 46 | 47 | 48 | /*Removing an item from the front requires a bit more work. 49 | * If the queue is empty, dequeue() simply returns null. If not, it removes the element from the front of the list and returns it. 50 | * Removing an element from the front of the queue is an O(n) operation. To dequeue, you remove the element from the beginning of the list. 51 | * This is always a linear time operation because it requires all of the remaining elements in the list to be shifted in memory.*/ 52 | override fun dequeue(): T? = 53 | if (isEmpty) null else list.removeAt(0) 54 | 55 | 56 | 57 | 58 | //For debugging purposes, you’ll have your queue override toString(). 59 | override fun toString(): String = list.toString() 60 | 61 | 62 | 63 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/PlayGround.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.PriorityQueues 2 | 3 | import example 4 | 5 | fun main(){ 6 | 7 | 8 | "Max priority queue" example { 9 | // When you run the code, notice the elements are removed largest to smallest. 10 | 11 | // 1-Create a ComparablePriorityQueueImpl using Int as generic type value which is Comparable. 12 | val priorityQueue = ComparablePriorityQueueImpl() 13 | // 2-Enqueue the value from an unsorted array into the priority queue. 14 | arrayListOf(1, 12, 3, 4, 1, 6, 8, 7).forEach { 15 | priorityQueue.enqueue(it) 16 | } 17 | // 3-Dequeue all of the values from the priority queue. 18 | while (!priorityQueue.isEmpty) { 19 | println(priorityQueue.dequeue()) 20 | } 21 | } 22 | 23 | 24 | 25 | "Min priorityQueue" example{ 26 | // When you run the code, you’ll see this output where the String objects are sorted from the longest to the shortest. 27 | // 1-Create a Comparator implementation that compares String based on the length from the longest to the shortest. 28 | val stringLengthComparator = object : Comparator { 29 | override fun compare(o1: String?, o2: String?): Int { 30 | val length1 = o1?.length ?: -1 31 | val length2 = o2?.length ?: -1 32 | return length1 - length2 33 | } 34 | } 35 | // 2-Create a ComparatorPriorityQueueImpl using the previous comparator in the constructor. 36 | val priorityQueue = ComparatorPriorityQueueImpl(stringLengthComparator) 37 | // 3-Enqueue value from an unsorted array as String into the priority queue. 38 | arrayListOf("one", "two", "three", "forty", "five", "six", "seven", "eight", "nine").forEach { 39 | priorityQueue.enqueue(it) 40 | } 41 | // 4-Dequeue all the values from the priority queue. 42 | while (!priorityQueue.isEmpty) { 43 | println(priorityQueue.dequeue()) 44 | } 45 | } 46 | 47 | 48 | "Max priority array list based queue" example{ 49 | val priorityQueue = CustomPriorityQueueArrayList() 50 | arrayListOf(1, 12, 3, 4, 1, 6, 8, 7).forEach { 51 | priorityQueue.enqueue(it) 52 | } 53 | priorityQueue.enqueue(5) 54 | priorityQueue.enqueue(0) 55 | priorityQueue.enqueue(10) 56 | while (!priorityQueue.isEmpty) { 57 | println(priorityQueue.dequeue()) 58 | } 59 | } 60 | 61 | 62 | 63 | "Challenge 2: Sorting: Concert line" example{ 64 | val p1 = Person("Josh", 21, true) 65 | val p2 = Person("Jake", 22, true) 66 | val p3 = Person("Clay", 28, false) 67 | val p4 = Person("Cindy", 28, false) 68 | val p5 = Person("Sabrina", 30, false) 69 | val priorityQueue = 70 | ComparatorPriorityQueueImpl(MilitaryPersonComparator) 71 | arrayListOf(p1, p2, p3, p4, p5).forEach { 72 | priorityQueue.enqueue(it) 73 | } 74 | while (!priorityQueue.isEmpty) { 75 | println(priorityQueue.dequeue()) 76 | } 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Priority Queues?? 3 | ![enter image description here](https://www.memecreator.org/static/images/memes/3529510.jpg)\ 4 | A priority queue is a **special type of queue** in which each element is associated with a **priority value**. And, elements are served on the basis of their priority. That is, higher priority elements are served first. 5 | 6 | However, if elements with the same priority occur, they are served according to their order in the queue. 7 | 8 | **Assigning Priority Value** 9 | 10 | Generally, the value of the element itself is considered for assigning the priority. For example, 11 | 12 | The element with the highest value is considered the highest priority element. However, in other cases, we can assume the element with the lowest value as the highest priority element. 13 | 14 | We can also set priorities according to our needs. 15 | 16 | ![enter image description here](https://cdn.programiz.com/sites/tutorial2program/files/Introduction.png) 17 | > Removing Highest Priority Element 18 | 19 | ### Difference between Priority Queue and Normal Queue 20 | 21 | In a queue, the **first-in-first-out rule** is implemented whereas, in a priority queue, the values are removed **on the basis of priority**. The element with the highest priority is removed first. 22 | 23 | ## Implementation of Priority Queue 24 | 25 | Priority queue can be implemented using an array, a linked list, a heap data structure, or a binary search tree. Among these data structures, heap data structure provides an efficient implementation of priority queues. 26 | 27 | Hence, we will be using the heap data structure to implement the priority queue in this package. 28 | 29 | ## Applications 30 | Some useful applications of a priority queue include: 31 | - Dijkstra’s algorithm: Uses a priority queue to calculate the minimum cost. 32 | - A* pathfinding algorithm: Uses a priority queue to track the unexplored routes that will produce the path with the shortest length. 33 | - Heap sort: Many heap sorts use a priority queue. 34 | - Huffman coding: Useful for building a compression tree. A min-priority queue is used to repeatedly find two nodes with the smallest frequency that don’t yet have a parent node. 35 | Priority queues have many more applications and practical uses; the list above represents only a handful. 36 | 37 | ## ⏲ Priority Queue Time Complexity 38 | ![enter image description here](https://i.imgur.com/UwFSjY0.png) 39 | 40 | ## Key points 41 | - A priority queue is often used to find the element in priority order. 42 | - The AbstractPriorityQueue implementation creates a layer of abstraction by focusing on key operations of a queue and leaving out additional functionality provided by the heap data structure. 43 | - This makes the priority queue’s intent clear and concise. Its only job is to enqueue and dequeue elements, nothing else. 44 | - The AbstractPriorityQueue implementation is another good example of 45 | Composition over (implementation) inheritance. 46 | 47 | 48 | ## 📒 References 49 | [Programiz - Priority Queue](https://www.programiz.com/dsa/priority-queue)\ 50 | [GeeksForGeeks - Priority Queue](https://www.geeksforgeeks.org/priority-queue-set-1-introduction/) 51 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/BinarySearch/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is BinarySearch??? 3 | ![enter image description here](https://preview.redd.it/z8g8xg2dekk51.jpg?auto=webp&s=74eb5607d552bf67c19535a2b7c23ea5da415f13)\ 4 | As the photo suggest this binary search get the half of the list and it has to be stored cause he will see which half is bigger or smaller than the value its searching for. 5 | 6 | Binary search is one of the most efficient searching algorithms with a time 7 | complexity of O(log n). This is comparable with searching for an element inside a balanced binary search tree. 8 | Two conditions need to be met before you can use binary search: 9 | - The collection must be able to perform index manipulation in constant time. 10 | Kotlin collections that can do this include the Array and the ArrayList. 11 | - The collection must be sorted. 12 | 13 | ![enter image description here](https://c.tenor.com/Jl0YrqxnHmAAAAAd/binary-search-sequence-search.gif) 14 | 15 | 16 | ## Pros & Cons 17 | **Advantages**: 18 | 19 | - Compared to linear search (checking each element in the array starting from the first), binary search is much faster. Linear search takes, on average N/2 comparisons (where N is the number of elements in the array), and worst case N comparisons. Binary search takes an average and worst-case log2(N)log2(N) comparisons. So for a million elements, linear search would take an average of 500,000 comparisons, whereas binary search would take 20. 20 | - It’s a fairly simple algorithm, though people get it wrong all the time. 21 | - It’s well known and often implemented for you as a library routine. 22 | 23 | **Disadvantages**: 24 | 25 | - It’s more complicated than linear search, and is overkill for very small numbers of elements. 26 | - It works only on lists that are sorted and kept sorted. That is not always feasable, especially if elements are constantly being added to the list. 27 | - It works only on element types for which there exists a less-than relationship. Some types simply cannot be sorted (though this is rare). 28 | - There is a great lost of efficiency if the list does not support random-access. You need, for example, to immediately jump to the middle of the list. If your list is a plain array, that’s great. If it’s a linked-list, not so much. Depending on the cost of your comparison operation, the cost of traversing a non-random-access list could dwarf the cost of the comparisons. 29 | - There are even faster search methods available, such as hash lookups. However a hash lookup requires the elements to be organized in a much more complicated data structure (a hash table, not a list). 30 | 31 | ## Key points 32 | - Binary search is only a valid algorithm on sorted collections. 33 | - Sometimes, it may be beneficial to sort a collection just to leverage the binary search capability for looking up elements. 34 | - The indexOf method of arrays uses linear search, which has an O(n) time 35 | complexity. Binary search has an O(log n) time complexity, which scales much better for large data sets. 36 | 37 | ## 📒 References 38 | [GeeksForGeeks - BinarySearch](https://www.geeksforgeeks.org/binary-search/)\ 39 | [Quora - Advantages and disadvantages](https://www.quora.com/What-are-the-advantages-and-disadvantages-of-binary-search)\ 40 | [Programiz - BinarySearch](https://www.programiz.com/dsa/binary-search) 41 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.PriorityQueues 2 | 3 | import java.util.* 4 | 5 | /*Queues are lists that maintain the order of elements using first in, first out (FIFO) 6 | ordering. A priority queue is another version of a queue. However, instead of using 7 | FIFO ordering, elements are dequeued in priority order. 8 | A priority queue can have either a: 9 | • Max-priority: The element at the front is always the largest. 10 | • Min-priority: The element at the front is always the smallest. 11 | A priority queue is especially useful when you need to identify the maximum or 12 | minimum value within a list of elements. 13 | 14 | 15 | 16 | ---------------------Key Points------------------ 17 | * A priority queue is often used to find the element in priority order. 18 | 19 | * The AbstractPriorityQueue implementation creates a layer of abstraction by focusing on key operations of a queue 20 | and leaving out additional functionality provided by the heap data structure. 21 | 22 | * This makes the priority queue’s intent clear and concise. Its only job is to enqueue and dequeue elements, nothing else. 23 | 24 | * The AbstractPriorityQueue implementation is another good example of Composition over (implementation) inheritance.*/ 25 | 26 | 27 | 28 | /*1-AbstractPriorityQueue implements the Queue interface and is generic in the type T. 29 | It’s an abstract class because you want to manage comparison using either 30 | Comparable objects or an external Comparator implementation.*/ 31 | abstract class AbstractPriorityQueue : Queue { 32 | 33 | //2-You’re going to use a Heap, so you need an abstract property that the specific implementation will define. 34 | abstract val heap: Heap 35 | get 36 | 37 | //count uses the same property of the heap. 38 | override val count: Int 39 | get() = heap.count 40 | 41 | /*By calling enqueue(), you add the element into the heap using insert(), which 42 | guarantees to arrange data internally so that the one with the highest priority is 43 | ready to extract. The overall complexity of enqueue() is the same as insert(): O(log n).*/ 44 | override fun enqueue(element: T): Boolean { 45 | heap.insert(element) 46 | return true 47 | } 48 | 49 | /*By calling dequeue(), you remove the root element from the heap using 50 | remove(). The Heap guarantees to get the one with the highest priority. The 51 | overall complexity of dequeue() is the same as remove(): O(log n) .*/ 52 | override fun dequeue() = heap.remove() 53 | 54 | //peek() delegates to the same method of the heap. 55 | override fun peek() = heap.peek() 56 | } 57 | 58 | /*Here, you implement heap using a ComparableHeapImpl object. 59 | The ComparablePriorityQueueImpl needs an object that implements the Comparable interface.*/ 60 | class ComparablePriorityQueueImpl> : 61 | AbstractPriorityQueue() { 62 | 63 | override val heap = ComparableHeapImpl() 64 | } 65 | 66 | /*Here, the only difference is the value provided to heap, which is now a 67 | ComparatorHeapImpl and needs a Comparator that you provide as a constructor parameter.*/ 68 | class ComparatorPriorityQueueImpl( 69 | private val comparator: Comparator 70 | ) : AbstractPriorityQueue() { 71 | 72 | override val heap = ComparatorHeapImpl(comparator) 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Graph.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | 4 | abstract class Graph { 5 | 6 | abstract fun createVertex(data: T): Vertex 7 | abstract fun addDirectedEdge(source: Vertex, destination: Vertex, weight: Double?) 8 | fun addUndirectedEdge(source: Vertex, destination: Vertex, weight: Double?) { 9 | addDirectedEdge(source, destination, weight) 10 | addDirectedEdge(destination, source, weight) 11 | } 12 | 13 | fun add(edge: EdgeType, source: Vertex, destination: Vertex, weight: Double?) { 14 | when (edge) { 15 | EdgeType.DIRECTED -> addDirectedEdge(source, destination, weight) 16 | EdgeType.UNDIRECTED -> addUndirectedEdge(source, destination, weight) 17 | } 18 | } 19 | 20 | abstract fun edges(source: Vertex): ArrayList> 21 | abstract fun weight(source: Vertex, destination: Vertex): Double? 22 | 23 | fun numberOfPaths(source: Vertex, destination: Vertex): Int { 24 | val numberOfPaths = Ref(0) // 1 25 | val visited: ArrayList> = arrayListOf() // 2 26 | paths(source, destination, visited, numberOfPaths) // 3 27 | return numberOfPaths.value 28 | } 29 | 30 | fun paths(source: Vertex, destination: Vertex, visited: ArrayList>, pathCount: Ref) { 31 | visited.add(source) // 1 32 | if (source == destination) { // 2 33 | pathCount.value += 1 34 | } else { 35 | val neighbors = edges(source) // 3 36 | neighbors.forEach { edge -> 37 | // 4 38 | if (!visited.contains(edge.destination)) { 39 | paths(edge.destination, destination, visited, pathCount) 40 | } 41 | } 42 | } 43 | // 5 44 | visited.remove(source) 45 | } 46 | 47 | fun breadthFirstSearch(source: Vertex): ArrayList> { 48 | val queue = QueueStack>() 49 | val enqueued = ArrayList>() 50 | val visited = ArrayList>() 51 | 52 | queue.enqueue(source) 53 | enqueued.add(source) 54 | 55 | while (true) { 56 | val vertex = queue.dequeue() ?: break 57 | 58 | visited.add(vertex) 59 | 60 | val neighborEdges = edges(vertex) 61 | neighborEdges.forEach { 62 | if (!enqueued.contains(it.destination)) { 63 | queue.enqueue(it.destination) 64 | enqueued.add(it.destination) 65 | } 66 | } 67 | } 68 | 69 | return visited 70 | } 71 | 72 | fun depthFirstSearch(source: Vertex): ArrayList> { 73 | val stack = StackImpl>() 74 | val visited = arrayListOf>() 75 | val pushed = arrayListOf>() 76 | 77 | stack.push(source) 78 | pushed.add(source) 79 | visited.add(source) 80 | 81 | outer@ while (true) { 82 | if (stack.isEmpty) break 83 | 84 | val vertex = stack.peek() 85 | val neighbors = edges(vertex!!) 86 | 87 | if (neighbors.isEmpty()) { 88 | stack.pop() 89 | continue 90 | } 91 | 92 | for (i in 0 until neighbors.size) { 93 | val destination = neighbors[i].destination 94 | if (!pushed.contains(destination)) { 95 | stack.push(destination) 96 | pushed.add(destination) 97 | visited.add(destination) 98 | continue@outer 99 | } 100 | } 101 | stack.pop() 102 | } 103 | 104 | return visited 105 | } 106 | 107 | } 108 | 109 | enum class EdgeType { 110 | DIRECTED, 111 | UNDIRECTED 112 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Stack/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Stack?? 3 | Stacks are everywhere. Some common examples of things you might stack:\ 4 | • Pancakes.\ 5 | • Books.\ 6 | • Paper.\ 7 | • Cash, especially cash. :]\ 8 | The stack data structure is identical, in concept, to a physical stack of objects. 9 | 10 | When you add an item to a stack, you place it on top of the stack. When you remove an item from a stack, you always remove the top-most item. 11 | 12 | ![Cattie!](https://media1.giphy.com/media/aOqVDcqUQt1BK/200.gif)\ 13 | A stack is a linear data structure that follows the principle of **Last In First Out (LIFO)**. This means the last element inserted inside the stack is removed first. 14 | 15 | You can think of the stack data structure as the pile of plates on top of another. 16 | 17 | Here, you can: 18 | - Put a new plate on top 19 | - Remove the top plate 20 | 21 | And, if you want the plate at the bottom, you must first remove all the plates on top. This is exactly how the stack data structure works. 22 | 23 | 24 | 25 | ## LIFO Principle of Stack 26 | In programming terms, putting an item on top of the stack is called **push** and removing an item is called **pop**. 27 | ![Stack operations](https://cdn.programiz.com/sites/tutorial2program/files/stack.png)\ 28 | In the above image, although item **3** was kept last, it was removed first. This is exactly how the **LIFO (Last In First Out) Principle** works. 29 | 30 | ## Basic Operations of Stack 31 | 32 | There are some basic operations that allow us to perform different actions on a stack. 33 | 34 | - **Push**: Add an element to the top of a stack 35 | - **Pop**: Remove an element from the top of a stack 36 | - **IsEmpty**: Check if the stack is empty 37 | - **IsFull**: Check if the stack is full 38 | - **Peek**: Get the value of the top element without removing it 39 | 40 | ## Stack Time Complexity 41 | 42 | For the array-based implementation of a stack, the push and pop operations take constant time, i.e. `O(1)`. 43 | 44 | ## Pros & Cons 45 | ### Advantages of Stack 46 | 47 | 1. **Efficient data management:** Stack helps you manage the data in a LIFO (last in, first out) method, which is not possible with a Linked list and array. 48 | 2. **Efficient management of functions:** When a function is called, the local variables are stored in a stack, and it is automatically destroyed once returned. 49 | 3. **Control over memory:** Stack allows you to control how memory is allocated and deallocated. 50 | 4. **Smart memory management:** Stack automatically cleans up the object. 51 | 5. **Not easily corrupted:** Stack does not get corrupted easily; hence it is more secure and reliable. 52 | 6. **Does not allow resizing of variables:** Variables cannot be resized. 53 | 54 | ### Disadvantages of Stack 55 | 56 | 1. **Limited memory size:** Stack memory is very limited. 57 | 2. **Chances of stack overflow:** Creating too many objects on the stack can increase the risk of stack overflow. 58 | 3. **Random access is not possible:** In a stack, random accessing the data is not possible. 59 | 4. **Unreliable:** When variable storage will get overwritten, it will sometimes leads to undefined behaviour of the function or program. 60 | 5. **Undesired termination:** The stack will fall outside of the memory area, which might lead to an abnormal termination. 61 | 62 | ## 📒 References 63 | 64 | [Programiz - Stack](https://www.programiz.com/dsa/stack)\ 65 | [GeeksForGeeks - Stack](https://www.geeksforgeeks.org/stack-data-structure/)\ 66 | [LearningMadesimple360 - Stack](https://learningmadesimple360.blogspot.com/2021/08/advantages-and-disadvantages-of-stack.html) 67 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/PriorityQueueArrayList.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.PriorityQueues 2 | 3 | import java.util.* 4 | import kotlin.Comparator 5 | 6 | 7 | /*----------Challenge 1: Construc6ng ArrayList priority queues----------- 8 | * To make an array-based priority queue, you need to implement the Queue interface. Instead of using a heap, you can use an array list.*/ 9 | 10 | //1-Define the AbstractPriorityQueueArrayList abstract class implementing the Queue interface. 11 | abstract class AbstractPriorityQueueArrayList : Queue { 12 | 13 | //2-Define the elements property of type ArrayList as protected so it can be accessed by the classes extending this. 14 | protected val elements = ArrayList() 15 | 16 | //3-The sort abstract function is the one you’re going to implement in different ways depending on the usage of Comparable objects or a Comparator. 17 | abstract fun sort() 18 | 19 | 20 | override val count: Int 21 | get() = elements.size 22 | 23 | /*The overall time complexity here is the complexity of the sort implementation, because the add operation of the ArrayList is O(1).*/ 24 | override fun enqueue(element: T): Boolean { 25 | //1-Appends the element in the ArrayList. 26 | elements.add(element) 27 | //2-Sorts the elements into the ArrayList using the sort function. 28 | sort() 29 | //3-Returns true because the element was inserted with success. 30 | return true 31 | } 32 | 33 | /*Here, you’re assuming that the ArrayList is always sorted, and if it’s not empty, 34 | it always contains the element with the highest priority in position 0. 35 | 36 | It’s important to know how the dequeue operation is O(n) because the removal of an 37 | item in position 0 requires the shift of all of the other elements. 38 | 39 | A possible optimization, which you can try as exercise, is to put the element with the highest 40 | priority in the last position so that you don’t have to shift any elements but instead reduce the size by 1.*/ 41 | override fun dequeue() = 42 | if (isEmpty) null else elements.removeAt(0) 43 | 44 | override fun peek() = elements.firstOrNull() 45 | 46 | override fun toString() = elements.toString() 47 | } 48 | 49 | 50 | /*Here, you implement sort() using the same method of the Collections class. The 51 | complexity, in this case, is O(n log n); it’s the same if you want to use a Comparator*/ 52 | class ComparablePriorityQueueArrayList> : AbstractPriorityQueueArrayList() { 53 | override fun sort() { 54 | Collections.sort(elements) 55 | } 56 | } 57 | 58 | class ComparatorPriorityQueueArrayList( 59 | private val comparator: Comparator 60 | ) : AbstractPriorityQueueArrayList() { 61 | override fun sort() { 62 | Collections.sort(elements, comparator) 63 | } 64 | } 65 | 66 | 67 | /* If you always insert the new item in the right position, you have to shift all of the other elements — 68 | and this can be done in O(n). You can now write this implementation for Comparable objects 69 | 70 | This is an O(n) operation since you have to shift the existing elements to the left by 71 | one until you find the right position.*/ 72 | class CustomPriorityQueueArrayList> : AbstractPriorityQueueArrayList() { 73 | override fun sort() { 74 | var index = count - 2 75 | while (index >= 0 && 76 | elements[index + 1].compareTo(elements[index]) > 0) { 77 | swap(index, index + 1) 78 | index--; 79 | } 80 | } 81 | 82 | private fun swap(i: Int, j: Int) { 83 | val tmp = elements[i] 84 | elements[i] = elements[j] 85 | elements[j] = tmp 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/HeapSort/HeapSort.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.HeapSort 2 | 3 | 4 | /*Heapsort is another comparison-based algorithm that sorts an array in ascending 5 | order using a heap. 6 | Heapsort takes advantage of a heap being, by definition, a partially sorted binary tree 7 | with the following qualities: 8 | 1. In a max heap, all parent nodes are larger than their children. 9 | 2. In a min heap, all parent nodes are smaller than their children. 10 | 11 | Ex: https://www.youtube.com/watch?v=MtQL_ll5KhQ 12 | 13 | You'll be performing the sort on an Array and reusing the algorithms already 14 | implemented in Heap in the form of extension functions. You'll reorder the elements 15 | using heapify(). After that, the heavy lifting will be done by a siftDown() method. 16 | 17 | 18 | Even though you get the benefit of in-memory sorting, the performance of heap sort 19 | is O(n log n) for its best, worse and average cases. This is because you have to 20 | traverse the whole array once and, every time you swap elements, you must perform 21 | a sift down, which is an O(log n) operation. 22 | 23 | Heap sort is also not a stable sort because it depends on how the elements are laid 24 | out and put into the heap. If you were heap sorting a deck of cards by their rank, for 25 | example, you might see their suite change order with respect to the original deck. 26 | 27 | ----------------Key Points------------------ 28 | * Heap sort leverages the max-heap data structure to sort elements in an array. 29 | * Heap sort sorts its elements by following a simple pattern: swap the first and last 30 | element, perform a sift-down, decrease the array size by one; then repeat. 31 | * The performance of heap sort is O(n log n) for its best, worse and average cases.*/ 32 | 33 | 34 | private fun leftChildIndex(index: Int) = (2 * index) + 1 35 | 36 | private fun rightChildIndex(index: Int) = (2 * index) + 2 37 | 38 | 39 | /*The differences between this function and the method that the Heap has is that you 40 | operate on this array instead of elements, and that your compare() calls are 41 | addressed to the comparator you get as a parameter. The algorithm itself remains 42 | the same, so if you can't wrap your head around this, you can take a look at the Heap Data Structure again*/ 43 | fun Array.siftDown( 44 | index: Int, 45 | upTo: Int, 46 | comparator: Comparator, 47 | ) { 48 | var parent = index 49 | while (true) { 50 | val left = leftChildIndex(parent) 51 | val right = rightChildIndex(parent) 52 | var candidate = parent 53 | if (left < upTo && 54 | comparator.compare(this[left], this[candidate]) > 0 55 | ) { 56 | candidate = left 57 | } 58 | if (right < upTo && 59 | comparator.compare(this[right], this[candidate]) > 0 60 | ) { 61 | candidate = right 62 | } 63 | if (candidate == parent) { 64 | return 65 | } 66 | this.swapAt(parent, candidate) 67 | parent = candidate 68 | } 69 | } 70 | 71 | fun Array.heapify(comparator: Comparator) { 72 | if (this.isNotEmpty()) { 73 | (this.size / 2 downTo 0).forEach { 74 | this.siftDown(it, this.size, comparator) 75 | } 76 | } 77 | } 78 | 79 | fun Array.heapSort(comparator: Comparator) { 80 | this.heapify(comparator) // 1-You reorder the elements so that the array looks like a Heap. 81 | for (index in this.indices.reversed()) { // 2-You loop through the array, starting from the last element. 82 | this.swapAt(0, index) // 3-You swap the first element and the last element. This moves the largest unsorted element to its correct spot. 83 | siftDown(0, index, comparator) // 4-Because the heap is now invalid, you must sift down the new root node. As a result, the next largest element will become the new root. 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/MergeSort/Challenge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.MergeSort 2 | 3 | /*--------------------Challenge 1: Iterables merge-------------------- 4 | * Write a function that takes two sorted iterables and merges them into a single iterable. 5 | * -----------------------Solution----------------------------- 6 | * The tricky part of this challenge is the limited capabilities of Iterable. Traditional 7 | implementations of this algorithm rely on the abilities of List types to keep track of indices. 8 | Since Iterable types have no notion of indices, you’ll make use of their iterator. 9 | * The Iterator in Kotlin has a slight inconvenience that you need to fix first. If there are 10 | no more elements in the iterable and you try to get the next one using next(), you’ll 11 | get a NoSuchElementException. */ 12 | fun > merge( 13 | first: Iterable, 14 | second: Iterable 15 | ): Iterable { 16 | 17 | /*1-Create a new container to store the merged iterable. It could be any class that 18 | implements Iterable but a MutableList is an easy to use choice, so go with 19 | that one. Then, grab the iterators of the first and second iterable. Iterators 20 | sequentially dispense values of the iterable via next(), but you’ll use your own extension nextOrNull().*/ 21 | val result = mutableListOf() 22 | 23 | //2-Create two variables that are initialized as the first and second iterator’s first value. 24 | val firstIterator = first.iterator() 25 | val secondIterator = second.iterator() 26 | 27 | //3-If one of the iterators didn’t have a first value, it means the iterable it came from was empty, and you’re done sorting. Simply return the other iterable. 28 | if (!firstIterator.hasNext()) return second 29 | if (!secondIterator.hasNext()) return first 30 | 31 | var firstEl = firstIterator.nextOrNull() 32 | var secondEl = secondIterator.nextOrNull() 33 | 34 | //4-This first while loop is where all of the comparisons are made to get the resulting iterable ordered. It only works while you still have values in both iterables. 35 | while (firstEl != null && secondEl != null) { 36 | //Using the iterators, you’ll decide which element should be appended into the result list by comparing the first and second next values. 37 | when { 38 | /*If the first value is less than the second, you’ll append the first value in result 39 | and seed the next value to be compared with by invoking nextOrNull() on the first iterator.*/ 40 | firstEl < secondEl -> { 41 | result.add(firstEl) 42 | firstEl = firstIterator.nextOrNull() 43 | } 44 | /*If the second value is less than the first, you’ll do the opposite. You seed the next 45 | value to be compared by invoking nextOrNull() on the second iterator.*/ 46 | secondEl < firstEl -> { 47 | result.add(secondEl) 48 | secondEl = secondIterator.nextOrNull() 49 | } 50 | else -> { 51 | /*If they’re equal, you append both the first and second values and seed both next values.*/ 52 | result.add(firstEl) 53 | result.add(secondEl) 54 | firstEl = firstIterator.nextOrNull() 55 | secondEl = secondIterator.nextOrNull() 56 | } 57 | } 58 | } 59 | 60 | while (firstEl != null) { 61 | result.add(firstEl) 62 | firstEl = firstIterator.nextOrNull() 63 | } 64 | while (secondEl != null) { 65 | result.add(secondEl) 66 | secondEl = secondIterator.nextOrNull() 67 | } 68 | 69 | return result 70 | } 71 | 72 | //In order to avoid the null on next for the Iterator we write this extension function 73 | private fun Iterator.nextOrNull(): T? { 74 | return if (this.hasNext()) this.next() else null 75 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Prims Algorithm??? 3 | ![enter image description here](https://pbs.twimg.com/media/E4BU33rVgAEn1oz.jpg)\ 4 | Prim's algorithm is a [minimum spanning tree](https://www.programiz.com/dsa/spanning-tree-and-minimum-spanning-tree#minimum-spanning) algorithm that takes a graph as input and finds the subset of the edges of that graph which 5 | 6 | - form a tree that includes every vertex 7 | - has the minimum sum of weights among all the trees that can be formed from the graph 8 | 9 | 10 | ### How Prim's algorithm works 11 | 12 | It falls under a class of algorithms called [greedy algorithms](https://www.programiz.com/dsa/greedy-algorithm) that find the local optimum in the hopes of finding a global optimum. 13 | 14 | We start from one vertex and keep adding edges with the lowest weight until we reach our goal. 15 | 16 | The steps for implementing Prim's algorithm are as follows: 17 | 18 | 1. Initialize the minimum spanning tree with a vertex chosen at random. 19 | 2. Find all the edges that connect the tree to new vertices, find the minimum and add it to the tree 20 | 3. Keep repeating step 2 until we get a minimum spanning tree 21 | 22 | ![enter image description here](https://inotgo.com/imagesLocal/202109/25/20210925212505140x_2.gif) 23 | ### Example of Prim's algorithm 24 | 25 | ![Start with a weighted graph](https://cdn.programiz.com/sites/tutorial2program/files/pa_1.png "Prim's Algorithm Steps") 26 | 27 | Start with a weighted graph 28 | 29 | ![Choose a vertex](https://cdn.programiz.com/sites/tutorial2program/files/pa_2.png "Prim's Algorithm Steps") 30 | 31 | Choose a vertex 32 | 33 | ![Choose the shortest edge from this vertex and add it](https://cdn.programiz.com/sites/tutorial2program/files/pa_3.png "Prim's Algorithm Steps") 34 | 35 | Choose the shortest edge from this vertex and add it 36 | 37 | ![Choose the nearest vertex not yet in the solution](https://cdn.programiz.com/sites/tutorial2program/files/pa_4.png "Prim's Algorithm Steps") 38 | 39 | Choose the nearest vertex not yet in the solution 40 | 41 | ![Choose the nearest edge not yet in the solution, if there are multiple choices, choose one at random](https://cdn.programiz.com/sites/tutorial2program/files/pa_5.png "Prim's Algorithm Steps") 42 | 43 | Choose the nearest edge not yet in the solution, if there are multiple choices, choose one at random 44 | 45 | ![Repeat until you have a spanning tree](https://cdn.programiz.com/sites/tutorial2program/files/pa_6.png "Prim's Algorithm Steps") 46 | 47 | Repeat until you have a spanning tree 48 | ## ⏲ Time Complexity 49 | In the algorithm above, you maintain three data structures: 50 | 1. An adjacency list graph to build a minimum spanning tree. Adding vertices and edges to an adjacency list is O(1) . 51 | 2. A Set to store all vertices you have visited. Adding a vertex to the set and checking if the set contains a vertex also have a time complexity of O(1). 52 | 3. A min-priority queue to store edges as you explore more vertices. The priority queue is built on top of a heap and insertion takes O(log E). 53 | 54 | The worst-case time complexity of Prim’s algorithm is O(E log E). This is because, each time you dequeue the smallest edge from the priority queue, you have to traverse all the edges of the destination vertex ( O(E) ) and insert the edge into the priority queue ( O(logE) ). 55 | 56 | ## Key points 57 | - You can leverage three different data structures: Priority queue, set, and adjacency lists to construct Prim's algorithm. 58 | - Prim's algorithm is a greedy algorithm that constructs a minimum spanning tree. 59 | - A spanning tree is a subgraph of an undirected graph that contains all the vertices with the fewest number of edges. 60 | 61 | ## 📒 References 62 | [Programiz - Prim Algorithms](https://www.programiz.com/dsa/prim-algorithm)\ 63 | [Raywenderlich - Prims Algorithms](https://www.raywenderlich.com/books/data-structures-algorithms-in-kotlin/v1.0/chapters/23-prim-s-algorithm) 64 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/MergeSort/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Merge Sort??? 3 | ![enter image description here](https://pics.me.me/thumb_0-bubble-sort-you-guys-always-act-like-youre-better-45912067.png)\ 4 | Merge Sort is one of the most popular [sorting algorithms](https://www.programiz.com/dsa/sorting-algorithm) that is based on the principle of [Divide and Conquer Algorithm](https://www.programiz.com/dsa/divide-and-conquer). 5 | 6 | Here, a problem is divided into multiple sub-problems. Each sub-problem is solved individually. Finally, sub-problems are combined to form the final solution.\ 7 | ![enter image description here](https://www.ejneer.com/post/mergesort_files/figure-html/plot-mergesort-1.gif) 8 | 9 | ## Divide and Conquer Strategy 10 | 11 | Using the **Divide and Conquer** technique, we divide a problem into subproblems. When the solution to each subproblem is ready, we 'combine' the results from the subproblems to solve the main problem. 12 | 13 | Suppose we had to sort an array A. A subproblem would be to sort a sub-section of this array starting at index p and ending at index r, denoted as A[p..r]. 14 | 15 | **Divide** 16 | 17 | If q is the half-way point between p and r, then we can split the subarray A[p..r] into two arrays A[p..q] and A[q+1, r]. 18 | 19 | **Conquer** 20 | 21 | In the conquer step, we try to sort both the subarrays A[p..q] and A[q+1, r]. If we haven't yet reached the base case, we again divide both these subarrays and try to sort them. 22 | 23 | **Combine** 24 | 25 | When the conquer step reaches the base step and we get two sorted subarrays A[p..q] and A[q+1, r] for array A[p..r], we combine the results by creating a sorted array A[p..r] from two sorted subarrays A[p..q] and A[q+1, r]. 26 | ## ⏲ Time Complexity 27 | The best, worst and average time complexity of merge sort is O(n log n), which isn’t too bad. If you’re struggling to understand where n log n comes from, think about 28 | how the recursion works: 29 | - As you recurse, you split a single list into two smaller lists. This means a list of size 2 will need one level of recursion, a list of size 4 will need two levels, a list of size 8 will need three levels, and so on. If you had a list of 1,024 elements, it would take 10 levels of recursively splitting in two to get down to 1024 single element lists. In general, if you have a list of size n, the number of levels is log2(n). 30 | - A single recursion level will merge n elements. It doesn’t matter if there are many small merges or one large one; the number of elements merged will still be n at each level. This means the cost of a single recursion is O(n). 31 | 32 | This brings the total cost to O(log n) ×O(n) = O(n log n). 33 | 34 | The previous chapter’s sort algorithms were in-place and used swapAt to move elements around. Merge sort, by contrast, allocates additional memory to do its work. How much? There are log2(n) levels of recursion, and at each level, n elements are used. That makes the total O(n log n) in space complexity. Merge sort is one of 35 | the hallmark sorting algorithms. It’s relatively simple to understand, and serves as a great introduction to how to divide and conquer algorithms work. Merge sort is O(n log n), and this implementation requires O(n log n) of space. If you’re clever with your bookkeeping, you can reduce the memory required to O(n) by discarding the 36 | memory that is not actively being used. 37 | ## Key points 38 | - Merge sort is in the category of the divide and conquer algorithms. 39 | - There are many implementations of merge sort, and you can have different performance characteristics depending on the implementation. 40 | - To do a comparison, in this chapter you sorted objects implementing the Comparable interface but the same can be done providing a different implementation of Comparator. 41 | 42 | ## 📒 References 43 | [Programiz - merge sort](https://www.programiz.com/dsa/merge-sort)\ 44 | [GeeksForGeeks - Merge Sort](https://www.geeksforgeeks.org/merge-sort/) 45 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/RadixSort/Challenge.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.RadixSort 2 | 3 | import java.lang.Math.pow 4 | 5 | /*----------------Challenge 1: Most significant sort------------ 6 | * Your task is to implement a most significant digit (MSD) radix sort. 7 | This sorting behavior is called lexicographical sorting and is also used for String sorting. 8 | * -----------------------Solution-------------------- 9 | * MSD radix sort is closely related to LSD radix sort, in that both use bucket sort. 10 | * The difference is that MSD radix sort needs to curate subsequent passes of the bucket sort carefully. 11 | * In LSD radix sort, bucket sort ran repeatedly using the whole list for every pass. 12 | * In MSD radix sort, you run bucket sort with the entire list only once. Subsequent passes will sort each bucket recursively.*/ 13 | 14 | /*digits is a computed property that returns the number of digits that the Int has. 15 | For example, the value of 1024 has four digits.*/ 16 | fun Int.digits(): Int { 17 | var count = 0 18 | var num = this 19 | while (num != 0) { 20 | count += 1 21 | num /= 10 22 | } 23 | return count 24 | } 25 | 26 | /*digit(atPosition:) returns the digit at a given position. Like lists, the leftmost 27 | position is zero. Thus, the digit for position zero of the value 1024 is 1. The digit for 28 | position three is 4. Since there are only four digits, the digit for position five will return null.*/ 29 | fun Int.digit(atPosition: Int): Int? { 30 | val correctedPosition = (atPosition + 1).toDouble() 31 | if (correctedPosition > digits()) return null 32 | 33 | var num = this 34 | while (num / (pow(10.0, correctedPosition).toInt()) != 0) { 35 | num /= 10 36 | } 37 | return num % 10 38 | } 39 | 40 | 41 | /*lexicographicalSort() is the user-facing API for MSD radix sort. 42 | msdRadixSorted() is the meat of the algorithm and will be used to recursively apply MSD radix sort to the list.*/ 43 | fun MutableList.lexicographicalSort() { 44 | val newList = msdRadixSorted(this, 0) 45 | this.clear() 46 | this.addAll(newList) 47 | } 48 | 49 | private fun msdRadixSorted(list: MutableList, position: Int): MutableList { 50 | //This ensures that if the position is equal or greater than the list’s maxDigits, you’ll terminate the recursion. 51 | if (position >= list.maxDigits()) return list 52 | 53 | // 1-Similar to LSD radix sort, you instantiate a two-dimensional list for the buckets. 54 | val buckets = MutableList>(10) { mutableListOf() } 55 | // 2-The priorityBucket is a special bucket that stores values with fewer digits than the current position. Values that go in the priorityBucket are sorted first. 56 | val priorityBucket = arrayListOf() 57 | // 3-For every number in the list, you find the digit of the current position and place the number in the appropriate bucket. 58 | list.forEach { number -> 59 | val digit = number.digit(position) 60 | if (digit == null) { 61 | priorityBucket.add(number) 62 | return@forEach 63 | } 64 | buckets[digit].add(number) 65 | } 66 | 67 | /*you need to recursively apply MSD radix sort for each of the individual buckets. 68 | * This statement calls reduce(into:) to collect the results of the recursive sorts and 69 | appends them to the priorityBucket. That way, the elements in the priorityBucket always go first. */ 70 | val newValues = buckets.reduce { result, bucket -> 71 | if (bucket.isEmpty()) return@reduce result 72 | result.addAll(msdRadixSorted(bucket, position + 1)) 73 | result 74 | } 75 | priorityBucket.addAll(newValues) 76 | 77 | return priorityBucket 78 | } 79 | 80 | /*As with all recursive operations, you need to set a terminating condition that stops 81 | the recursion. Recursion should halt if the current position you’re inspecting is 82 | greater than the number of significant digits of the largest value inside the list.*/ 83 | private fun List.maxDigits(): Int { 84 | return this.maxOrNull()?.digits() ?: 0 85 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/On2Sorting/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is O(n2) Algorithms?? 3 | ![enter image description here](https://miro.medium.com/max/552/0*DPKL62mAZbrS5qyt.jpg)\ 4 | O(n²) time complexity is not great performance, but the sorting algorithms in this category are easy to understand and useful in some scenarios. These algorithms are space efficient, and they only require constant O(1) additional memory space. For small data sets, these sorts compare favorably against more complex sorts. It’s usually not recommended to use O(n²) in production code, but you’ll need to start somewhere, and these algorithms are a great place to start. 5 | In this chapter, you’ll look at the following sorting algorithms: 6 | - Bubble sort. 7 | - Selection sort. 8 | - Insertion sort. 9 | 10 | All of these are comparison-based sorting methods. In other words, they rely on a comparison method, such as the less than operator, to order elements. You measure a sorting technique’s general performance by counting the number of times this comparison gets called. 11 | ![enter image description here](https://qph.fs.quoracdn.net/main-qimg-58dc29bc4966efe3ac20c6ae66088a12) 12 | 13 | You will see the merge sort and heap and others in the next packages. 14 | 15 | ## Bubble Sort 16 | One of the simplest sorts is the bubble sort. The bubble sort repeatedly compares adjacent values and swaps them, if needed, to perform the sort. The larger values in the set will, therefore, bubble up to the end of the collection. 17 | 18 | ### Working of Bubble Sort 19 | Suppose we are trying to sort the elements in **ascending order**. 20 | 21 | **1. First Iteration (Compare and Swap)** 22 | 23 | 1. Starting from the first index, compare the first and the second elements. 24 | 2. If the first element is greater than the second element, they are swapped. 25 | 3. Now, compare the second and the third elements. Swap them if they are not in order. 26 | 4. The above process goes on until the last element. 27 | 28 | **2. Remaining Iteration** 29 | 30 | The same process goes on for the remaining iterations. 31 | 32 | After each iteration, the largest element among the unsorted elements is placed at the end. 33 | ![enter image description here](https://www.resultswebdev.com/wp-content/themes/results-website-design/uploads/bubble-sort-animation2.gif) 34 | 35 | 36 | ## Selection sort 37 | Selection sort follows the basic idea of bubble sort but improves upon this 38 | algorithm by reducing the number of swapAt operations. Selection sort only swaps at the end of each pass. 39 | ![enter image description here](https://user-images.githubusercontent.com/56377217/145599472-aac4bd2f-d841-4ab1-945b-33b42c447dd5.gif) 40 | 41 | ## Insertion sort 42 | Insertion sort is a more useful algorithm. Like bubble sort and selection sort, 43 | insertion sort has an average time complexity of O(n²), but the performance of 44 | insertion sort can vary. 45 | 46 | The more the data is already sorted, the less work it needs to do. Insertion sort has a best time complexity of O(n) if the data is already sorted. 47 | 48 | ![enter image description here](https://www.arabicprogrammer.com/images/548/4d4a4ee96b360815ba654de710a7c7a4.gif) 49 | 50 | ## ⏲ Time Complexity 51 | ### Bubble Sort Complexity 52 | ![enter image description here](https://i.imgur.com/WI7LML4.png) 53 | 54 | ### Selection Sort Complexity 55 | ![enter image description here](https://i.imgur.com/WO7F8ag.png) 56 | 57 | ### Insertion Sort Complexity 58 | ![enter image description here](https://i.imgur.com/a5N6NIw.png) 59 | ## Key points 60 | - n² algorithms often have a terrible reputation, but some of these algorithms usually have some redeeming points. insertionSort can sort in O(n) time if the collection is already in sorted order and gradually scales down to O(n²). 61 | - insertionSort is one of the best sorts in situations wherein you know ahead of time that your data is in sorted order. 62 | - Design your algorithms to be as generic as possible without hurting the 63 | performance. 64 | 65 | 66 | ## 📒 References 67 | [Programiz - Bubble sort](https://www.programiz.com/dsa/bubble-sort)\ 68 | [Programiz - Selection sort](https://www.programiz.com/dsa/selection-sort)\ 69 | [Programiz - Insertion sort](https://www.programiz.com/dsa/insertion-sort) 70 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Stack/Stack.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Stack 2 | 3 | import LinkedList 4 | 5 | /*--------------Stack operations--------------*/ 6 | /*Stacks are useful, and also exceedingly simple. The DataStructure.LinkedList.main goal of building a stack is to enforce how you access your data. 7 | 8 | There are only two essential operations for a stack: 9 | 10 | * push: Adding an element to the top of the stack. 11 | * pop: Removing the top element of the stack. 12 | 13 | This means that you can only add or remove elements from one side of the data structure. 14 | In computer science, a stack is known as the LIFO (last-in first-out) data structure. 15 | Elements that are pushed in last are the first ones to be popped out.*/ 16 | interface StackInterface { 17 | 18 | fun peek(): Element? 19 | 20 | val count: Int 21 | get 22 | 23 | val isEmpty: Boolean 24 | get() = count == 0 25 | 26 | 27 | 28 | 29 | fun push(element: Element) 30 | 31 | fun pop(): Element? 32 | } 33 | 34 | //Note: The previous Stack interface is different from the Stack class provided by Kotlin (or Java) which extends the Vector class and provides methods we don’t need here. 35 | 36 | 37 | 38 | 39 | /*-----------------------------------------------------------------------------------------------------*/ 40 | 41 | 42 | 43 | /*You can implement your Stack interface in different ways and choosing the right storage type is important. 44 | The ArrayList is an obvious choice since it offers constant time insertions and deletions at one end via add and removeAt with the last index as a parameter. 45 | Usage of these two operations will facilitate the LIFO nature of stacks.*/ 46 | class Stack() : StackInterface { 47 | private val storage = arrayListOf() 48 | 49 | override fun toString() = buildString { 50 | appendLine("----top----") 51 | storage.asReversed().forEach { 52 | appendLine("$it") 53 | } 54 | appendLine("-----------") 55 | } 56 | 57 | 58 | // In the push method you just append the value passed as parameter to the end of the ArrayList using it’s add method. 59 | override fun push(element: T) { 60 | storage.add(element) 61 | } 62 | 63 | // In the pop method you simply return null if the storage is empty, or you remove and return the last element you’have inserter. 64 | override fun pop(): T? { 65 | if (isEmpty) { 66 | return null 67 | } 68 | return storage.removeAt(count - 1) 69 | } 70 | 71 | 72 | 73 | 74 | /*----------------------------Non-essential operations----------------------------*/ 75 | 76 | override fun peek(): T? { 77 | return storage.lastOrNull() 78 | } 79 | 80 | override val count: Int 81 | get() = storage.size 82 | 83 | 84 | 85 | 86 | 87 | 88 | /*----------------------------Less is more----------------------------*/ 89 | 90 | /*You may have wondered if you could adopt the Kotlin collection interfaces for the stack. 91 | A stack’s purpose is to limit the number of ways to access your data, 92 | and adopting interfaces such as Iterable would go against this goal by exposing all of the elements via iterators. In this case, less is more! 93 | You might want to take an existing list and convert it to a stack so that the access order is guaranteed. Of course, it would be possible to loop through the array elements and push each element. 94 | However, you can write a static factory method that directly adds these elements to the Stack implementation.*/ 95 | 96 | companion object { 97 | fun create(items: Iterable): StackInterface { 98 | val stack = Stack() 99 | for (item in items) { 100 | stack.push(item) 101 | } 102 | return stack 103 | } 104 | } 105 | 106 | } 107 | 108 | /*Here we made a step further and made our stack initializable by listing its elements, similar to listOf() and other standard library collection factory functions.*/ 109 | fun stackOf(vararg elements: Element): StackInterface { 110 | return Stack.create(elements.asList()) 111 | } 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/LinkedList/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is a LinkedList?? 3 | I have thought about explaning it in real life example but I couldn't find better than this. 4 | ![enter image description here](https://mir-s3-cdn-cf.behance.net/project_modules/disp/0d8f8410905773.560ed8d7374db.gif)\ 5 | Did you ever saw how trains car connected? Each car holding the next one and there a Head of the train and there's a tail as well right? so when we add more cars to it we add from the tail and the new one becomes the new tail (last car) 6 | 7 | 8 | A linked list is a linear data structure that includes a series of connected nodes. Here, each node stores the **data** and the **address** of the next node. 9 | ![enter image description here](https://cdn.programiz.com/sites/tutorial2program/files/linked-list-concept.png) 10 | Linked lists can be of multiple types: **singly**, **doubly**, and **circular linked list**. In this article, we will focus on the **singly linked list**. 11 | 12 | > **Note:** You might have played the game Treasure Hunt, where each clue includes the information about the next clue. That is how the linked list operates. 13 | 14 | LinkedList have operations like append, push, remove(removelast, removeat, pop), insert. 15 | 16 | The Time Complexity for LinkedList 17 | 18 | - Search: O(n) 19 | - Insert: O(1) 20 | - Deletion: O(1) 21 | - Space Complexity: O(n) 22 | 23 | If you don't know what Time Complexity is, I suggest you read [this](https://www.geeksforgeeks.org/understanding-time-complexity-simple-examples/). 24 | 25 | The implementation for this data strcuture you will find in this package and I suggest reading the comments on each code to understand the code more and be able to imagine how things work. 26 | 27 | 28 | ## Pros & Cons 29 | **Advantages Of Linked List:** 30 | 31 | - **Dynamic data structure:** A linked list is a dynamic arrangement so it can grow and shrink at runtime by allocating and [deallocating memory](https://www.geeksforgeeks.org/how-to-deallocate-memory-without-using-free-in-c/). So there is no need to give the initial size of the linked list. 32 | - **No memory wastage:** In the Linked list, efficient memory utilization can be achieved since the size of the linked list increase or decrease at run time so there is no memory wastage and there is no need to pre-allocate the memory. 33 | - **Implementation:** Linear data structures like stack and queues are often easily implemented using a linked list. 34 | - **Insertion and Deletion Operations:** Insertion and deletion operations are quite easier in the linked list. There is no need to shift elements after the insertion or deletion of an element only the address present in the next pointer needs to be updated. 35 | 36 | **Disadvantages Of Linked List:** 37 | 38 | - **Memory usage:** More memory is required in the linked list as compared to an array. Because in a linked list, a [pointer](https://www.geeksforgeeks.org/pointers-in-c-and-c-set-1-introduction-arithmetic-and-array/) is also required to store the address of the next element and it requires extra memory for itself. 39 | - **Traversal:** In a [Linked list traversal](https://www.geeksforgeeks.org/recursive-insertion-and-traversal-linked-list/) is more time-consuming as compared to an array. Direct access to an element is not possible in a linked list as in an array by index. For example, for accessing a node at position n, one has to traverse all the nodes before it. 40 | - **Reverse Traversing:** In a singly linked list reverse traversing is not possible, but in the case of a [doubly-linked list](https://www.geeksforgeeks.org/doubly-linked-list/), it can be possible as it contains a pointer to the previously connected nodes with each node. For performing this extra memory is required for the back pointer hence, there is a wastage of memory. 41 | - **Random Access:** Random access is not possible in a linked list due to its [dynamic memory allocation](https://www.geeksforgeeks.org/what-is-dynamic-memory-allocation/). 42 | 43 | ## 📒 References 44 | 45 | [Programiz - LinkedList](https://www.programiz.com/dsa/linked-list)\ 46 | [GeeksForGeeks - LinkedList](https://www.geeksforgeeks.org/data-structures/linked-list/)\ 47 | [Stackabuse- Interview Questions LinkedList](https://stackabuse.com/linked-list-programming-interview-questions/)\ 48 | [GeeksForGeeks - Advantages and Disadvantages of LinkedList](https://www.geeksforgeeks.org/advantages-and-disadvantages-of-linked-list/) 49 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Prims/Heap.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Prims 2 | 3 | import java.util.Collections 4 | 5 | interface Collection { 6 | val count: Int 7 | 8 | val isEmpty: Boolean 9 | get() = count == 0 10 | 11 | fun insert(element: T) 12 | 13 | fun remove(): T? 14 | 15 | fun remove(index: Int): T? 16 | } 17 | 18 | 19 | interface Heap : Collection { 20 | 21 | fun peek(): T? 22 | } 23 | 24 | abstract class AbstractHeap : Heap { 25 | var elements: ArrayList = ArrayList() 26 | 27 | override val count: Int 28 | get() = elements.size 29 | 30 | override fun peek(): T? = elements.first() 31 | 32 | override fun insert(element: T) { 33 | elements.add(element) 34 | siftUp(count - 1) 35 | } 36 | 37 | private fun siftUp(index: Int) { 38 | var child = index 39 | var parent = parentIndex(child) 40 | while (child > 0 && compare(elements[child], elements[parent]) > 0) { 41 | Collections.swap(elements, child, parent) 42 | child = parent 43 | parent = parentIndex(child) 44 | } 45 | } 46 | 47 | override fun remove(): T? { 48 | if (isEmpty) return null 49 | 50 | Collections.swap(elements, 0, count - 1) 51 | val item = elements.removeAt(count - 1) 52 | siftDown(0) 53 | return item 54 | } 55 | 56 | private fun siftDown(index: Int) { 57 | var parent = index 58 | while (true) { 59 | val left = leftChildIndex(parent) 60 | val right = rightChildIndex(parent) 61 | var candidate = parent 62 | if (left < count && 63 | compare(elements[left], elements[candidate]) > 0 64 | ) { 65 | candidate = left 66 | } 67 | if (right < count && 68 | compare(elements[right], elements[candidate]) > 0 69 | ) { 70 | candidate = right 71 | } 72 | if (candidate == parent) { 73 | return 74 | } 75 | Collections.swap(elements, parent, candidate) 76 | parent = candidate 77 | } 78 | } 79 | 80 | override fun remove(index: Int): T? { 81 | if (index >= count) return null 82 | 83 | return if (index == count - 1) { 84 | elements.removeAt(count - 1) 85 | } else { 86 | Collections.swap(elements, index, count - 1) 87 | val item = elements.removeAt(count - 1) 88 | siftDown(index) 89 | siftUp(index) 90 | item 91 | } 92 | } 93 | 94 | private fun index(element: T, i: Int): Int? { 95 | if (i >= count) { 96 | return null 97 | } 98 | if (compare(element, elements[i]) > 0) { 99 | return null 100 | } 101 | if (element == elements[i]) { 102 | return i 103 | } 104 | val leftChildIndex = index(element, leftChildIndex(i)) 105 | if (leftChildIndex != null) return leftChildIndex 106 | val rightChildIndex = index(element, rightChildIndex(i)) 107 | if (rightChildIndex != null) return rightChildIndex 108 | return null 109 | } 110 | 111 | protected fun heapify(values: ArrayList) { 112 | elements = values 113 | if (!elements.isEmpty()) { 114 | (count / 2 downTo 0).forEach { 115 | siftDown(it) 116 | } 117 | } 118 | } 119 | 120 | private fun leftChildIndex(index: Int) = (2 * index) + 1 121 | 122 | private fun rightChildIndex(index: Int) = (2 * index) + 2 123 | 124 | private fun parentIndex(index: Int) = (index - 1) / 2 125 | 126 | abstract fun compare(a: T, b: T): Int 127 | } 128 | 129 | class ComparableHeapImpl> : 130 | AbstractHeap() { 131 | 132 | companion object { 133 | fun > create( 134 | elements: ArrayList 135 | ): Heap { 136 | val heap = ComparableHeapImpl() 137 | heap.heapify(elements) 138 | return heap 139 | } 140 | } 141 | 142 | override fun compare(a: T, b: T): Int = a.compareTo(b) 143 | } 144 | 145 | class ComparatorHeapImpl( 146 | private val comparator: Comparator 147 | ) : AbstractHeap() { 148 | 149 | companion object { 150 | fun create( 151 | elements: ArrayList, 152 | comparator: Comparator 153 | ): Heap { 154 | val heap = ComparatorHeapImpl(comparator) 155 | heap.heapify(elements) 156 | return heap 157 | } 158 | } 159 | 160 | override fun compare(a: T, b: T): Int = 161 | comparator.compare(a, b) 162 | } 163 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/MergeSort/MergeSort.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.MergeSort 2 | 3 | /*Merge sort is one of the most efficient sorting algorithms. With a time complexity of 4 | O(n log n), it’s one of the fastest of all general-purpose sorting algorithms. The idea 5 | behind merge sort is divide and conquer — to break a big problem into several 6 | smaller, easier-to-solve problems, and then combine those solutions into a final 7 | result. The merge sort mantra is to split first and merge after. 8 | 9 | Example: https://www.youtube.com/watch?v=JSceec-wEyw 10 | 11 | Here’s a summary of the key procedures of merge sort: 12 | 1.The strategy of merge sort is to divide and conquer so that you solve many small problems instead of one big problem. 13 | 2.It has two core responsibilities: a method to divide the initial list recursively, as well as a method to merge two lists. 14 | 3.The merging function should take two sorted lists and produce a single sorted list. 15 | 16 | ----------Performance---------- 17 | The best, worst and average time complexity of merge sort is O(n log n), which isn’t 18 | too bad. If you’re struggling to understand where n log n comes from, think about how the recursion works: 19 | * As you recurse, you split a single list into two smaller lists. This means a list of size 20 | 2 will need one level of recursion, a list of size 4 will need two levels, a list of size 8 21 | will need three levels, and so on. If you had a list of 1,024 elements, it would take 22 | 10 levels of recursively splitting in two to get down to 1024 single element lists. In 23 | general, if you have a list of size n, the number of levels is log2(n). 24 | * A single recursion level will merge n elements. It doesn’t matter if there are many 25 | small merges or one large one; the number of elements merged will still be n at 26 | each level. This means the cost of a single recursion is O(n). 27 | This brings the total cost to O(log n) ×O(n) = O(n log n).*/ 28 | fun > List.mergeSort(): List { 29 | /*1-Recursion needs a base case, which you can also think of as an “exit condition”. 30 | In this case, the base case is when the list only has one element. Your previous 31 | quick win is now the cornerstone of the algorithm.*/ 32 | if (this.size < 2) return this 33 | val middle = this.size / 2 34 | /*2-You’re calling mergeSort on each of the sub-lists. This recursion continues to try 35 | to split the lists into smaller lists until the “exit condition” is fulfilled. 36 | In your case, it will split until the lists contain only one element.*/ 37 | val left = this.subList(0, middle).mergeSort() 38 | val right = this.subList(middle, this.size).mergeSort() 39 | 40 | return merge(left, right) 41 | } 42 | 43 | private fun > merge(left: List, right: List): List { 44 | // 1-The leftIndex and rightIndex variables track your progress as you parse through the two lists. 45 | var leftIndex = 0 46 | var rightIndex = 0 47 | // 2-The result list will house the combined lists. 48 | val result = mutableListOf() 49 | /* 3-Starting from the beginning, you compare the elements in the left and right lists sequentially. 50 | When you reach the end of either list, there’s nothing else to compare.*/ 51 | while (leftIndex < left.size && rightIndex < right.size) { 52 | val leftElement = left[leftIndex] 53 | val rightElement = right[rightIndex] 54 | // 4-The smaller of the two elements goes into the result list. If the elements are equal, they can both be added. 55 | if (leftElement < rightElement) { 56 | result.add(leftElement) 57 | leftIndex += 1 58 | } else if (leftElement > rightElement) { 59 | result.add(rightElement) 60 | rightIndex += 1 61 | } else { 62 | result.add(leftElement) 63 | leftIndex += 1 64 | result.add(rightElement) 65 | rightIndex += 1 66 | } 67 | } 68 | /*5-The first loop guarantees that either left or right is empty. Since both lists are 69 | sorted, this ensures that the leftover elements are greater than or equal to the 70 | ones currently in result. In this scenario, you can add the rest of the elements without comparison.*/ 71 | if (leftIndex < left.size) { 72 | result.addAll(left.subList(leftIndex, left.size)) 73 | } 74 | if (rightIndex < right.size) { 75 | result.addAll(right.subList(rightIndex, right.size)) 76 | } 77 | return result 78 | } -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/Heap.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.Dijkstras 2 | 3 | import java.util.* 4 | import kotlin.Comparator 5 | import kotlin.collections.ArrayList 6 | 7 | interface Collection { 8 | val count: Int 9 | 10 | val isEmpty: Boolean 11 | get() = count == 0 12 | 13 | fun insert(element: T) 14 | 15 | fun remove(): T? 16 | 17 | fun remove(index: Int): T? 18 | } 19 | 20 | 21 | interface Heap : Collection { 22 | 23 | fun peek(): T? 24 | } 25 | 26 | abstract class AbstractHeap() : Heap { 27 | var elements: ArrayList = ArrayList() 28 | 29 | override val count: Int 30 | get() = elements.size 31 | 32 | override fun peek(): T? = elements.first() 33 | 34 | override fun insert(element: T) { 35 | elements.add(element) // 1 36 | siftUp(count - 1) // 2 37 | } 38 | 39 | private fun siftUp(index: Int) { 40 | var child = index 41 | var parent = parentIndex(child) 42 | while (child > 0 && compare(elements[child], elements[parent]) > 0) { 43 | Collections.swap(elements, child, parent) 44 | child = parent 45 | parent = parentIndex(child) 46 | } 47 | } 48 | 49 | override fun remove(): T? { 50 | if (isEmpty) return null // 1 51 | 52 | Collections.swap(elements, 0, count - 1) // 2 53 | val item = elements.removeAt(count - 1) // 3 54 | siftDown(0) // 4 55 | return item 56 | } 57 | 58 | private fun siftDown(index: Int) { 59 | var parent = index // 1 60 | while (true) { // 2 61 | val left = leftChildIndex(parent) //3 62 | val right = rightChildIndex(parent) 63 | var candidate = parent // 4 64 | if (left < count && 65 | compare(elements[left], elements[candidate]) > 0 66 | ) { 67 | candidate = left //5 68 | } 69 | if (right < count && 70 | compare(elements[right], elements[candidate]) > 0 71 | ) { 72 | candidate = right // 6 73 | } 74 | if (candidate == parent) { 75 | return // 7 76 | } 77 | Collections.swap(elements, parent, candidate) // 8 78 | parent = candidate 79 | } 80 | } 81 | 82 | override fun remove(index: Int): T? { 83 | if (index >= count) return null // 1 84 | 85 | return if (index == count - 1) { 86 | elements.removeAt(count - 1) // 2 87 | } else { 88 | Collections.swap(elements, index, count - 1) // 3 89 | val item = elements.removeAt(count - 1) // 4 90 | siftDown(index) // 5 91 | siftUp(index) 92 | item 93 | } 94 | } 95 | 96 | private fun index(element: T, i: Int): Int? { 97 | if (i >= count) { 98 | return null // 1 99 | } 100 | if (compare(element, elements[i]) > 0) { 101 | return null // 2 102 | } 103 | if (element == elements[i]) { 104 | return i // 3 105 | } 106 | val leftChildIndex = index(element, leftChildIndex(i)) 107 | if (leftChildIndex != null) return leftChildIndex // 4 108 | val rightChildIndex = index(element, rightChildIndex(i)) 109 | if (rightChildIndex != null) return rightChildIndex // 5 110 | return null // 6 111 | } 112 | 113 | protected fun heapify(values: ArrayList) { 114 | elements = values 115 | if (!elements.isEmpty()) { 116 | (count / 2 downTo 0).forEach { 117 | siftDown(it) 118 | } 119 | } 120 | } 121 | 122 | private fun leftChildIndex(index: Int) = (2 * index) + 1 123 | 124 | private fun rightChildIndex(index: Int) = (2 * index) + 2 125 | 126 | private fun parentIndex(index: Int) = (index - 1) / 2 127 | 128 | abstract fun compare(a: T, b: T): Int 129 | } 130 | 131 | class ComparableHeapImpl> : 132 | AbstractHeap() { 133 | 134 | companion object { 135 | fun > create( 136 | elements: ArrayList 137 | ): Heap { 138 | val heap = ComparableHeapImpl() 139 | heap.heapify(elements) 140 | return heap 141 | } 142 | } 143 | 144 | override fun compare(a: T, b: T): Int = a.compareTo(b) 145 | } 146 | 147 | class ComparatorHeapImpl( 148 | private val comparator: Comparator 149 | ) : AbstractHeap() { 150 | 151 | companion object { 152 | fun create( 153 | elements: ArrayList, 154 | comparator: Comparator 155 | ): Heap { 156 | val heap = ComparatorHeapImpl(comparator) 157 | heap.heapify(elements) 158 | return heap 159 | } 160 | } 161 | 162 | override fun compare(a: T, b: T): Int = 163 | comparator.compare(a, b) 164 | } 165 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/kotlin,intellij,gradle,maven 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=kotlin,intellij,gradle,maven 3 | 4 | ### Intellij ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # AWS User-specific 16 | .idea/**/aws.xml 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | 21 | # Sensitive or high-churn files 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.local.xml 25 | .idea/**/sqlDataSources.xml 26 | .idea/**/dynamic.xml 27 | .idea/**/uiDesigner.xml 28 | .idea/**/dbnavigator.xml 29 | 30 | # Gradle 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Gradle and Maven with auto-import 35 | # When using Gradle or Maven with auto-import, you should exclude module files, 36 | # since they will be recreated, and may cause churn. Uncomment if using 37 | # auto-import. 38 | # .idea/artifacts 39 | # .idea/compiler.xml 40 | # .idea/jarRepositories.xml 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | # *.iml 45 | # *.ipr 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # SonarLint plugin 69 | .idea/sonarlint/ 70 | 71 | # Crashlytics plugin (for Android Studio and IntelliJ) 72 | com_crashlytics_export_strings.xml 73 | crashlytics.properties 74 | crashlytics-build.properties 75 | fabric.properties 76 | 77 | # Editor-based Rest Client 78 | .idea/httpRequests 79 | 80 | # Android studio 3.1+ serialized cache file 81 | .idea/caches/build_file_checksums.ser 82 | 83 | ### Intellij Patch ### 84 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 85 | 86 | # *.iml 87 | # modules.xml 88 | # .idea/misc.xml 89 | # *.ipr 90 | 91 | # Sonarlint plugin 92 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 93 | .idea/**/sonarlint/ 94 | 95 | # SonarQube Plugin 96 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 97 | .idea/**/sonarIssues.xml 98 | 99 | # Markdown Navigator plugin 100 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 101 | .idea/**/markdown-navigator.xml 102 | .idea/**/markdown-navigator-enh.xml 103 | .idea/**/markdown-navigator/ 104 | 105 | # Cache file creation bug 106 | # See https://youtrack.jetbrains.com/issue/JBR-2257 107 | .idea/$CACHE_FILE$ 108 | 109 | # CodeStream plugin 110 | # https://plugins.jetbrains.com/plugin/12206-codestream 111 | .idea/codestream.xml 112 | 113 | ### Kotlin ### 114 | # Compiled class file 115 | *.class 116 | 117 | # Log file 118 | *.log 119 | 120 | # BlueJ files 121 | *.ctxt 122 | 123 | # Mobile Tools for Java (J2ME) 124 | .mtj.tmp/ 125 | 126 | # Package Files # 127 | *.jar 128 | *.war 129 | *.nar 130 | *.ear 131 | *.zip 132 | *.tar.gz 133 | *.rar 134 | 135 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 136 | hs_err_pid* 137 | replay_pid* 138 | 139 | ### Maven ### 140 | target/ 141 | pom.xml.tag 142 | pom.xml.releaseBackup 143 | pom.xml.versionsBackup 144 | pom.xml.next 145 | release.properties 146 | dependency-reduced-pom.xml 147 | buildNumber.properties 148 | .mvn/timing.properties 149 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 150 | .mvn/wrapper/maven-wrapper.jar 151 | 152 | # Eclipse m2e generated files 153 | # Eclipse Core 154 | .project 155 | # JDT-specific (Eclipse Java Development Tools) 156 | .classpath 157 | 158 | ### Gradle ### 159 | .gradle 160 | **/build/ 161 | !src/**/build/ 162 | 163 | # Ignore Gradle GUI config 164 | gradle-app.setting 165 | 166 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 167 | !gradle-wrapper.jar 168 | 169 | # Avoid ignore Gradle wrappper properties 170 | !gradle-wrapper.properties 171 | 172 | # Cache of project 173 | .gradletasknamecache 174 | 175 | # Eclipse Gradle plugin generated files 176 | # Eclipse Core 177 | # JDT-specific (Eclipse Java Development Tools) 178 | 179 | # End of https://www.toptal.com/developers/gitignore/api/kotlin,intellij,gradle,maven -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Graphs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Grahps??? 3 | ![enter image description here](https://media.makeameme.org/created/data-structure-data.jpg)\ 4 | A graph data structure is a collection of nodes that have data and are connected to other nodes. 5 | 6 | Let's try to understand this through an example. On facebook, everything is a node. That includes User, Photo, Album, Event, Group, Page, Comment, Story, Video, Link, Note...anything that has data is a node. 7 | 8 | Every relationship is an edge from one node to another. Whether you post a photo, join a group, like a page, etc., a new edge is created for that relationship. 9 | 10 | ![graph data structure explained using facebook's example. Users, groups, pages, events, etc. are represented as nodes and their relationships - friend, joining a group, liking a page are represented as links between nodes](https://cdn.programiz.com/sites/tutorial2program/files/facebook-graph.png "Example of graph data structure") 11 | 12 | Example of graph data structure 13 | 14 | All of facebook is then a collection of these nodes and edges. This is because facebook uses a graph data structure to store its data. 15 | 16 | More precisely, a graph is a data structure (V, E) that consists of 17 | 18 | - A collection of vertices V 19 | - A collection of edges E, represented as ordered pairs of vertices (u,v) 20 | 21 | You should read more about the graphs to fully understand the code and the rest of this file so I suggest reading from [here](https://www.programiz.com/dsa/graph) Just read the whole section of the Graphs or you can read it from the book I mentioned before 22 | ## ⏲ Time Complexity 23 | This chart summarizes the cost of different operations for graphs represented by adjacency lists versus adjacency matrices. 24 | ![enter image description here](https://assets.alexandria.raywenderlich.com/books/dsk/images/208c7220466ea6d6dfaa531311cc371949c2f7849e0050652a88404847cf7dd7/original.png) 25 | 26 | V represents vertices, and E represents edges. 27 | An adjacency list takes less storage space than an adjacency matrix. An adjacency list simply stores the number of vertices and edges needed. 28 | 29 | As for an adjacency matrix, recall that the number of rows and columns is equal to the number of vertices. 30 | This explains the quadratic space complexity of O(V²). 31 | 32 | Adding a vertex is efficient in an adjacency list: Simply create a vertex and set its key-value pair in the map. 33 | 34 | It’s amortized as O(1). When adding a vertex to an adjacency matrix, you’re required to add a column to every row and create a new row for the new vertex. This is at least O(V), and if you choose to represent your matrix with a contiguous block of memory, it can be O(V²). 35 | 36 | Adding an edge is efficient in both data structures, as they are both constant time. 37 | 38 | The adjacency list appends to the array of outgoing edges. The adjacency matrix sets the value in the two-dimensional array. Adjacency list loses out when trying to find a particular edge or weight. 39 | 40 | To find an edge in an adjacency list, you must obtain the list of outgoing edges and loop through every edge to find a matching destination. This happens in O(V) time. With an adjacency matrix, finding an edge or weight is a constant time access to retrieve the value from the two-dimensional array. 41 | 42 | Which data structure should you choose to construct your graph? 43 | If there are few edges in your graph, it’s considered a sparse graph, and an adjacency list would be a good fit. An adjacency matrix would be a bad choice for a sparse graph, because a lot of memory will be wasted since there aren’t many edges. 44 | 45 | If your graph has lots of edges, it’s considered a dense graph, and an adjacency matrix would be a better fit as you’d be able to access your weights and edges far more quickly. 46 | - Adjacency matrix uses a square matrix to represent a graph. 47 | - Adjacency list is generally good for sparse graphs, when your graph has the least amount of edges. 48 | - Adjacency matrix is generally suitable for dense graphs, when your graph has lots of edges. 49 | ## Key points 50 | - You can represent real-world relationships through vertices and edges. 51 | - Think of vertices as objects and edges as the relationship between the objects. 52 | - Weighted graphs associate a weight with every edge. 53 | - Directed graphs have edges that traverse in one direction. 54 | - Undirected graphs have edges that point both ways. 55 | - Adjacency list stores a list of outgoing edges for every vertex. 56 | ## 📒 References 57 | [Programiz - graph](https://www.programiz.com/dsa/graph)\ 58 | [RayWenderLich - Graphs](https://www.raywenderlich.com/books/data-structures-algorithms-in-kotlin/v1.0/chapters/19-graphs#toc-chapter-022-anchor-009) 59 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/DepthFirstSearch/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is DepthFirstSearch??? 3 | ![enter image description here](https://www.banterly.net/content/images/2020/02/meme3.jpg)\ 4 | Depth first Search or Depth first traversal is a recursive algorithm for searching all the vertices of a graph or tree data structure. Traversal means visiting all the nodes of a [graph](https://www.programiz.com/dsa/graph). 5 | 6 | A standard DFS implementation puts each vertex of the graph into one of two categories: 7 | 8 | 1. Visited 9 | 2. Not Visited 10 | 11 | The purpose of the algorithm is to mark each vertex as visited while avoiding cycles. 12 | 13 | The DFS algorithm works as follows: 14 | 15 | 1. Start by putting any one of the graph's vertices on top of a stack. 16 | 2. Take the top item of the stack and add it to the visited list. 17 | 3. Create a list of that vertex's adjacent nodes. Add the ones which aren't in the visited list to the top of the stack. 18 | 4. Keep repeating steps 2 and 3 until the stack is empty. 19 | 20 | ![enter image description here](https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif) 21 | 22 | ## Depth First Search Example 23 | 24 | Let's see how the Depth First Search algorithm works with an example. We use an undirected graph with 5 vertices. 25 | 26 | ![We start from vertex 0, the DFS algorithm starts by putting it in the Visited list and putting all its adjacent vertices in the stack.](https://cdn.programiz.com/sites/tutorial2program/files/graph-dfs-step-0.png "A DFS example") 27 | 28 | Undirected graph with 5 vertices 29 | 30 | We start from vertex 0, the DFS algorithm starts by putting it in the Visited list and putting all its adjacent vertices in the stack. 31 | 32 | ![Start by putting it in the Visited list and putting all its adjacent vertices in the stack.](https://cdn.programiz.com/sites/tutorial2program/files/graph-dfs-step-1.png "A DFS example") 33 | 34 | Visit the element and put it in the visited list 35 | 36 | Next, we visit the element at the top of stack i.e. 1 and go to its adjacent nodes. Since 0 has already been visited, we visit 2 instead. 37 | 38 | ![Next, we visit the element at the top of stack i.e. 1 and go to its adjacent nodes. Since 0 has already been visited, we visit 2 instead.](https://cdn.programiz.com/sites/tutorial2program/files/graph-dfs-step-2.png "A DFS example") 39 | 40 | Visit the element at the top of stack 41 | 42 | Vertex 2 has an unvisited adjacent vertex in 4, so we add that to the top of the stack and visit it. 43 | 44 | ![Vertex 2 has an unvisited adjacent vertex in 4, so we add that to the top of the stack and visit it.](https://cdn.programiz.com/sites/tutorial2program/files/graph-dfs-step-3.png "A DFS example") 45 | 46 | Vertex 2 has an unvisited adjacent vertex in 4, so we add that to the top of the stack and visit it. 47 | 48 | ![Vertex 2 has an unvisited adjacent vertex in 4, so we add that to the top of the stack and visit it.](https://cdn.programiz.com/sites/tutorial2program/files/graph-dfs-step-4.png "A DFS example") 49 | 50 | Vertex 2 has an unvisited adjacent vertex in 4, so we add that to the top of the stack and visit it. 51 | 52 | After we visit the last element 3, it doesn't have any unvisited adjacent nodes, so we have completed the Depth First Traversal of the graph. 53 | 54 | ![After we visit the last element 3, it doesn't have any unvisited adjacent nodes, so we have completed the Depth First Traversal of the graph.](https://cdn.programiz.com/sites/tutorial2program/files/graph-dfs-step-5.png "A DFS example") 55 | 56 | After we visit the last element 3, it doesn't have any unvisited adjacent nodes, so we have completed the Depth First Traversal of the graph. 57 | ## ⏲ Time Complexity 58 | DFS visits every vertex at least once. This has a time complexity of O(V). 59 | 60 | When traversing a graph in DFS, you have to check all neighboring vertices to find one that’s available to visit. 61 | 62 | The time complexity of this is O(E) because in the worst case, you have to visit every edge in the graph. 63 | 64 | Overall, the time complexity for depth-first search is O(V + E). 65 | 66 | The space complexity of depth-first search is O(V) because you have to store vertices in three separate data structures: stack, pushed and visited. 67 | 68 | ## Key points 69 | - Depth-first search (DFS) is another algorithm to traverse or search a graph. 70 | - DFS explores a branch as far as possible until it reaches the end. 71 | - Leverage a stack data structure to keep track of how deep you are in the graph. Only pop off the stack when you reach a dead end. 72 | ## 📒 References 73 | [Programiz - Graphs DFS](https://www.programiz.com/dsa/graph-dfs)\ 74 | [RayWenderLich - Depth First Search](https://www.raywenderlich.com/books/data-structures-algorithms-in-kotlin/v1.0/chapters/21-depth-first-search) 75 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/Dijkstras/README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is Dijkstra's Algorithm??? 3 | ![enter image description here](https://memegenerator.net/img/instances/66924889.jpg)\ 4 | It differs from the minimum spanning tree because the shortest distance between two vertices might not include all the vertices of the graph. 5 | 6 | ### How Dijkstra's Algorithm works 7 | 8 | Dijkstra's Algorithm works on the basis that any subpath `B -> D` of the shortest path `A -> D` between vertices A and D is also the shortest path between vertices B and D. 9 | 10 | ![shortest subpath property is used by dijkstra's algorithm](https://cdn.programiz.com/sites/tutorial2program/files/shortest-subpath.png "Subpaths - Dijkstra's Algorithm") 11 | 12 | Each subpath is the shortest path 13 | 14 | Djikstra used this property in the opposite direction i.e we overestimate the distance of each vertex from the starting vertex. Then we visit each node and its neighbors to find the shortest subpath to those neighbors. 15 | 16 | The algorithm uses a greedy approach in the sense that we find the next best solution hoping that the end result is the best solution for the whole problem. 17 | 18 | ### Example of Dijkstra's algorithm 19 | 20 | It is easier to start with an example and then think about the algorithm. 21 | 22 | ![Start with a weighted graph](https://cdn.programiz.com/sites/tutorial2program/files/dj-1.png "Dijkstra's algorithm steps") 23 | 24 | Start with a weighted graph 25 | 26 | ![Choose a starting vertex and assign infinity path values to all other devices](https://cdn.programiz.com/sites/tutorial2program/files/dj-2.png "Dijkstra's algorithm steps") 27 | 28 | Choose a starting vertex and assign infinity path values to all other devices 29 | 30 | ![Go to each vertex and update its path length](https://cdn.programiz.com/sites/tutorial2program/files/dj-3.png "Dijkstra's algorithm steps") 31 | 32 | Go to each vertex and update its path length 33 | 34 | ![If the path length of the adjacent vertex is lesser than new path length, don't update it](https://cdn.programiz.com/sites/tutorial2program/files/dj-4.png "Dijkstra's algorithm steps") 35 | 36 | If the path length of the adjacent vertex is lesser than new path length, don't update it 37 | 38 | ![Avoid updating path lengths of already visited vertices](https://cdn.programiz.com/sites/tutorial2program/files/dj-5.png "Dijkstra's algorithm steps") 39 | 40 | Avoid updating path lengths of already visited vertices 41 | 42 | ![After each iteration, we pick the unvisited vertex with the least path length. So we choose 5 before 7](https://cdn.programiz.com/sites/tutorial2program/files/dj-6.png "Dijkstra's algorithm steps") 43 | 44 | After each iteration, we pick the unvisited vertex with the least path length. So we choose 5 before 7 45 | 46 | ![Notice how the rightmost vertex has its path length updated twice](https://cdn.programiz.com/sites/tutorial2program/files/dj-7.png "Dijkstra's algorithm steps") 47 | 48 | Notice how the rightmost vertex has its path length updated twice 49 | 50 | ![Repeat until all the vertices have been visited](https://cdn.programiz.com/sites/tutorial2program/files/dj-8.png "Dijkstra's algorithm steps") 51 | 52 | Repeat until all the vertices have been visited 53 | 54 | ![Dijkstra](https://blog.finxter.com/wp-content/uploads/2021/12/Python-blog-Dijkstras-Algorithm.gif) 55 | 56 | ## ⏲ Time Complexity 57 | In Dijkstra’s algorithm, you constructed your graph using an adjacency list. You used a min-priority queue to store vertices and extract the vertex with the minimum path. 58 | 59 | This has an overall performance of O(log V). This's because the heap operations of extracting the minimum element or inserting an element both take O(log V). 60 | 61 | If you recall from the breadth-first search chapter, it takes O(V + E) to traverse all the vertices and edges. Dijkstra’s algorithm is somewhat similar to breadth-first search, because you have to explore all neighboring edges. 62 | 63 | This time, instead of going down to the next level, you use a min-priority queue to select a single vertex with the shortest distance to traverse down. That means it is O(1 + E) or simply O(E). 64 | 65 | So, combining the traversal with operations on the min-priority queue, it takes O(E log V) to perform Dijkstra’s algorithm. 66 | ## Key points 67 | - Dijkstra's algorithm finds a path to the rest of the nodes given a starting vertex. 68 | - This algorithm is useful for finding the shortest paths between different endpoints. 69 | - Visit state is used to track the edges back to the start vertex. 70 | - The priority queue data structure helps to always return the vertex with the shortest path. 71 | - Hence, it is a greedy algorithm! 72 | ## 📒 References 73 | [Programiz - Dijkstra](https://www.programiz.com/dsa/dijkstra-algorithm)\ 74 | [RayWenderLich - Dijkstra](https://www.raywenderlich.com/books/data-structures-algorithms-in-kotlin/v1.0/chapters/22-dijkstra-s-algorithm) 75 | -------------------------------------------------------------------------------- /src/main/kotlin/Algorithms/HeapSort/Heap.kt: -------------------------------------------------------------------------------- 1 | package Algorithms.HeapSort 2 | 3 | import java.util.* 4 | import kotlin.Comparator 5 | import kotlin.collections.ArrayList 6 | 7 | interface Collection { 8 | val count: Int 9 | 10 | val isEmpty: Boolean 11 | get() = count == 0 12 | 13 | fun insert(element: T) 14 | 15 | fun remove(): T? 16 | 17 | fun remove(index: Int): T? 18 | } 19 | 20 | 21 | interface Heap : Collection { 22 | 23 | fun peek(): T? 24 | } 25 | 26 | abstract class AbstractHeap() : Heap { 27 | var elements: ArrayList = ArrayList() 28 | 29 | override val count: Int 30 | get() = elements.size 31 | 32 | override fun peek(): T? = elements.firstOrNull() 33 | 34 | override fun insert(element: T) { 35 | elements.add(element) // 1 36 | siftUp(count - 1) // 2 37 | } 38 | 39 | private fun siftUp(index: Int) { 40 | var child = index 41 | var parent = parentIndex(child) 42 | while (child > 0 && compare(elements[child], elements[parent]) > 0) { 43 | Collections.swap(elements, child, parent) 44 | child = parent 45 | parent = parentIndex(child) 46 | } 47 | } 48 | 49 | override fun remove(): T? { 50 | if (isEmpty) return null // 1 51 | 52 | Collections.swap(elements, 0, count - 1) // 2 53 | val item = elements.removeAt(count - 1) // 3 54 | siftDown(0) // 4 55 | return item 56 | } 57 | 58 | private fun siftDown(index: Int) { 59 | var parent = index // 1 60 | while (true) { // 2 61 | val left = leftChildIndex(parent) //3 62 | val right = rightChildIndex(parent) 63 | var candidate = parent // 4 64 | if (left < count && 65 | compare(elements[left], elements[candidate]) > 0 66 | ) { 67 | candidate = left //5 68 | } 69 | if (right < count && 70 | compare(elements[right], elements[candidate]) > 0 71 | ) { 72 | candidate = right // 6 73 | } 74 | if (candidate == parent) { 75 | return // 7 76 | } 77 | Collections.swap(elements, parent, candidate) // 8 78 | parent = candidate 79 | } 80 | } 81 | 82 | override fun remove(index: Int): T? { 83 | if (index >= count) return null // 1 84 | 85 | return if (index == count - 1) { 86 | elements.removeAt(count - 1) // 2 87 | } else { 88 | Collections.swap(elements, index, count - 1) // 3 89 | val item = elements.removeAt(count - 1) // 4 90 | siftDown(index) // 5 91 | siftUp(index) 92 | item 93 | } 94 | } 95 | 96 | private fun index(element: T, i: Int): Int? { 97 | if (i >= count) { 98 | return null // 1 99 | } 100 | if (compare(element, elements[i]) > 0) { 101 | return null // 2 102 | } 103 | if (element == elements[i]) { 104 | return i // 3 105 | } 106 | val leftChildIndex = index(element, leftChildIndex(i)) 107 | if (leftChildIndex != null) return leftChildIndex // 4 108 | val rightChildIndex = index(element, rightChildIndex(i)) 109 | if (rightChildIndex != null) return rightChildIndex // 5 110 | return null // 6 111 | } 112 | 113 | protected fun heapify(values: ArrayList) { 114 | elements = values 115 | if (!elements.isEmpty()) { 116 | (count / 2 downTo 0).forEach { 117 | siftDown(it) 118 | } 119 | } 120 | } 121 | 122 | private fun leftChildIndex(index: Int) = (2 * index) + 1 123 | 124 | private fun rightChildIndex(index: Int) = (2 * index) + 2 125 | 126 | private fun parentIndex(index: Int) = (index - 1) / 2 127 | 128 | abstract fun compare(a: T, b: T): Int 129 | } 130 | 131 | class ComparableHeapImpl> : 132 | AbstractHeap() { 133 | 134 | companion object { 135 | fun > create( 136 | elements: ArrayList 137 | ): Heap { 138 | val heap = ComparableHeapImpl() 139 | heap.heapify(elements) 140 | return heap 141 | } 142 | } 143 | 144 | override fun compare(a: T, b: T): Int = a.compareTo(b) 145 | } 146 | 147 | class ComparatorHeapImpl( 148 | private val comparator: Comparator 149 | ) : AbstractHeap() { 150 | 151 | companion object { 152 | fun create( 153 | elements: ArrayList, 154 | comparator: Comparator 155 | ): Heap { 156 | val heap = ComparatorHeapImpl(comparator) 157 | heap.heapify(elements) 158 | return heap 159 | } 160 | } 161 | 162 | override fun compare(a: T, b: T): Int = 163 | comparator.compare(a, b) 164 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/PriorityQueues/Heap.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.PriorityQueues 2 | 3 | import java.util.* 4 | import kotlin.collections.ArrayList 5 | 6 | interface Collection { 7 | val count: Int 8 | get 9 | 10 | val isEmpty: Boolean 11 | get() = count == 0 12 | 13 | fun insert(element: T) 14 | 15 | fun remove(): T? 16 | 17 | fun remove(index: Int): T? 18 | } 19 | 20 | 21 | interface Heap : Collection { 22 | 23 | fun peek(): T? 24 | } 25 | 26 | abstract class AbstractHeap() : Heap { 27 | protected var elements: ArrayList = ArrayList() 28 | 29 | override val count: Int 30 | get() = elements.size 31 | 32 | override fun peek(): T? = elements.first() 33 | 34 | override fun insert(element: T) { 35 | elements.add(element) // 1 36 | siftUp(count - 1) // 2 37 | } 38 | 39 | private fun siftUp(index: Int) { 40 | var child = index 41 | var parent = parentIndex(child) 42 | while (child > 0 && compare(elements[child], elements[parent]) > 0) { 43 | Collections.swap(elements, child, parent) 44 | child = parent 45 | parent = parentIndex(child) 46 | } 47 | } 48 | 49 | override fun remove(): T? { 50 | if (isEmpty) return null // 1 51 | 52 | Collections.swap(elements, 0, count - 1) // 2 53 | val item = elements.removeAt(count - 1) // 3 54 | siftDown(0) // 4 55 | return item 56 | } 57 | 58 | private fun siftDown(index: Int) { 59 | var parent = index // 1 60 | while (true) { // 2 61 | val left = leftChildIndex(parent) //3 62 | val right = rightChildIndex(parent) 63 | var candidate = parent // 4 64 | if (left < count && 65 | compare(elements[left], elements[candidate]) > 0 66 | ) { 67 | candidate = left //5 68 | } 69 | if (right < count && 70 | compare(elements[right], elements[candidate]) > 0 71 | ) { 72 | candidate = right // 6 73 | } 74 | if (candidate == parent) { 75 | return // 7 76 | } 77 | Collections.swap(elements, parent, candidate) // 8 78 | parent = candidate 79 | } 80 | } 81 | 82 | override fun remove(index: Int): T? { 83 | if (index >= count) return null // 1 84 | 85 | return if (index == count - 1) { 86 | elements.removeAt(count - 1) // 2 87 | } else { 88 | Collections.swap(elements, index, count - 1) // 3 89 | val item = elements.removeAt(count - 1) // 4 90 | siftDown(index) // 5 91 | siftUp(index) 92 | item 93 | } 94 | } 95 | 96 | private fun index(element: T, i: Int): Int? { 97 | if (i >= count) { 98 | return null // 1 99 | } 100 | if (compare(element, elements[i]) > 0) { 101 | return null // 2 102 | } 103 | if (element == elements[i]) { 104 | return i // 3 105 | } 106 | val leftChildIndex = index(element, leftChildIndex(i)) 107 | if (leftChildIndex != null) return leftChildIndex // 4 108 | val rightChildIndex = index(element, rightChildIndex(i)) 109 | if (rightChildIndex != null) return rightChildIndex // 5 110 | return null // 6 111 | } 112 | 113 | protected fun heapify(values: ArrayList) { 114 | elements = values 115 | if (!elements.isEmpty()) { 116 | (count / 2 downTo 0).forEach { 117 | siftDown(it) 118 | } 119 | } 120 | } 121 | 122 | private fun leftChildIndex(index: Int) = (2 * index) + 1 123 | 124 | private fun rightChildIndex(index: Int) = (2 * index) + 2 125 | 126 | private fun parentIndex(index: Int) = (index - 1) / 2 127 | 128 | abstract fun compare(a: T, b: T): Int 129 | } 130 | 131 | class ComparableHeapImpl> : 132 | AbstractHeap() { 133 | 134 | companion object { 135 | fun > create( 136 | elements: ArrayList 137 | ): Heap { 138 | val heap = ComparableHeapImpl() 139 | heap.heapify(elements) 140 | return heap 141 | } 142 | } 143 | 144 | override fun compare(a: T, b: T): Int = a.compareTo(b) 145 | } 146 | 147 | class ComparatorHeapImpl( 148 | private val comparator: Comparator 149 | ) : AbstractHeap() { 150 | 151 | companion object { 152 | fun create( 153 | elements: ArrayList, 154 | comparator: Comparator 155 | ): Heap { 156 | val heap = ComparatorHeapImpl(comparator) 157 | heap.heapify(elements) 158 | return heap 159 | } 160 | } 161 | 162 | override fun compare(a: T, b: T): Int = 163 | comparator.compare(a, b) 164 | } -------------------------------------------------------------------------------- /src/main/kotlin/DataStructure/Trees/BST/BinaryNode.kt: -------------------------------------------------------------------------------- 1 | package DataStructure.Trees.BST 2 | 3 | typealias Visitor = (T) -> Unit 4 | 5 | class BinaryNode>(var value: T) { 6 | 7 | var leftChild: BinaryNode? = null 8 | var rightChild: BinaryNode? = null 9 | 10 | //This recursive min property in BinaryNode will help you find the minimum node in a subtree. 11 | val min: BinaryNode? 12 | get() = leftChild?.min ?: this 13 | 14 | val isBinarySearchTree: Boolean 15 | get() = isBST(this, min = null, max = null) 16 | 17 | override fun toString() = diagram(this) 18 | 19 | private fun diagram(node: BinaryNode?, 20 | top: String = "", 21 | root: String = "", 22 | bottom: String = ""): String { 23 | return node?.let { 24 | if (node.leftChild == null && node.rightChild == null) { 25 | "$root${node.value}\n" 26 | } else { 27 | diagram(node.rightChild, "$top ", "$top┌──", "$top│ ") + 28 | root + "${node.value}\n" + diagram(node.leftChild, "$bottom│ ", "$bottom└──", "$bottom ") 29 | } 30 | } ?: "${root}null\n" 31 | } 32 | 33 | fun traverseInOrder(visit: Visitor) { 34 | leftChild?.traverseInOrder(visit) 35 | visit(value) 36 | rightChild?.traverseInOrder(visit) 37 | } 38 | 39 | fun traversePreOrder(visit: Visitor) { 40 | visit(value) 41 | leftChild?.traversePreOrder(visit) 42 | rightChild?.traversePreOrder(visit) 43 | } 44 | 45 | fun traversePostOrder(visit: Visitor) { 46 | leftChild?.traversePostOrder(visit) 47 | rightChild?.traversePostOrder(visit) 48 | visit(value) 49 | } 50 | 51 | 52 | /*------Challenge 1 : Is it a BST?----- 53 | function that checks if a binary tree is a binary search tree. 54 | 55 | * A binary search tree is a tree where every left child is less than or equal to its parent, 56 | and every right child is greater than its parent. An algorithm that verifies whether a 57 | tree is a binary search tree involves going through all the nodes and checking for this 58 | property. 59 | 60 | The time complexity of this solution is O(n) since you need to traverse through the 61 | entire tree once. There is also a O(n) space cost since you’re making n recursive calls.*/ 62 | 63 | /* 1-isBST is responsible for recursively traversing through the tree and checking for 64 | the BST property. It needs to keep track of progress via a reference to a 65 | BinaryNode and also keep track of the min and max values to verify the BST property.*/ 66 | private fun isBST(tree: BinaryNode?, min: T?, max: T?): Boolean { 67 | /*2-This is the base case. If tree is null, then there are no nodes to inspect. 68 | A null node is a binary search tree, so you’ll return true in that case.*/ 69 | tree ?: return true 70 | 71 | /*3-This is essentially a bounds check. If the current value exceeds the bounds of the 72 | min and max values, the current node does not respect the binary search tree rules.*/ 73 | if (min != null && tree.value <= min) { 74 | return false 75 | } else if (max != null && tree.value > max) { 76 | return false 77 | } 78 | /*4-This line contains the recursive calls. When traversing through the left children, 79 | the current value is passed in as the max value. This is because nodes in the left 80 | side cannot be greater than the parent. Vice versa, when traversing to the right, 81 | the min value is updated to the current value. Nodes in the right side must be 82 | greater than the parent. If any of the recursive calls evaluate false, the false 83 | value will propagate to the top.*/ 84 | return isBST(tree.leftChild, min, tree.value) && isBST(tree.rightChild, tree.value, max) 85 | } 86 | 87 | 88 | /*---------Challenge 2 : Equality between BSTs---------- 89 | * Override equals() to check whether two binary search trees are equal. 90 | * Overriding equals() is relatively straightforward. For two binary trees to be equal, 91 | both trees must have the same elements in the same order. 92 | * The time complexity of this function is O(n). The space complexity of this function is O(n). 93 | 94 | 1-equals recursively checks two nodes and their descendants for equality.*/ 95 | override fun equals(other: Any?): Boolean { 96 | /*2-Here, you check the value of the left and right nodes for equality. 97 | You also recursively check the left children and the right children for equality.*/ 98 | return if (other != null && other is BinaryNode<*>) { 99 | this.value == other.value && 100 | this.leftChild == other.leftChild && 101 | this.rightChild == other.rightChild 102 | } else { 103 | false 104 | } 105 | } 106 | 107 | } --------------------------------------------------------------------------------