├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── kotlinc.xml ├── libraries │ └── KotlinJavaRuntime.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── Data Structures and Algorithms.iml ├── LICENSE.md ├── README.md ├── dsa-kotlin-banner.png └── src ├── algorithms ├── README.md ├── recursion │ ├── README.md │ ├── factorial │ │ └── Factorial.kt │ ├── fibonacci │ │ └── Fibonacci.kt │ └── reverse_string │ │ └── ReverseString.kt ├── searching │ ├── bfs │ │ └── README.md │ ├── binary_search │ │ ├── BinarySearch.kt │ │ └── README.md │ ├── dfs │ │ └── README.md │ └── linear_search │ │ ├── LinearSearch.kt │ │ └── README.md └── sorting │ ├── bubble_sort │ ├── BubbleSort.kt │ └── README.md │ ├── insertion_sort │ ├── InsertionSort.kt │ └── README.md │ ├── merge_sort │ ├── MergeSort.kt │ └── README.md │ ├── quick_sort │ ├── QuickSort.kt │ └── README.md │ └── selection_sort │ ├── README.md │ └── SelectionSort.kt ├── data_structures ├── README.md ├── arrays │ ├── README.md │ ├── implementation │ │ └── DynamicArray.kt │ ├── merge_sorted_arrays │ │ └── MergeSortedArrays.kt │ └── reverse_string │ │ └── ReverseString.kt ├── graphs │ ├── Graph.kt │ └── README.md ├── hash_tables │ ├── README.md │ ├── first_recurring_character │ │ └── FirstRecurring.kt │ └── implementation │ │ ├── HashTable.kt │ │ └── KeyValue.kt ├── linked_lists │ ├── doubly_linked_list │ │ ├── DoublyLinkedList.kt │ │ └── Node.kt │ └── singly_linked_list │ │ ├── Node.kt │ │ └── SinglyLinkedList.kt ├── queues │ ├── queue_using_linked_list │ │ ├── Node.kt │ │ └── Queue.kt │ └── queue_using_stacks │ │ └── Queue.kt ├── stacks │ ├── stack_using_array │ │ └── Stack.kt │ └── stack_using_linked_list │ │ ├── Node.kt │ │ └── Stack.kt └── trees │ ├── binary_search_tree │ ├── BinarySearchTree.kt │ ├── BinarySearchTreeNoComments.kt │ └── Node.kt │ ├── priority_queue │ ├── Node.kt │ └── PriorityQueue.kt │ └── trie │ ├── Node.kt │ └── Trie.kt └── dynamic_programming ├── README.md ├── dynamic_fibonacci └── DynamicFibonacci.kt └── memoization └── Memoization.kt /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /out/ -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/libraries/KotlinJavaRuntime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Data Structures and Algorithms.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Asror Abdurakhmanov 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 all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![DSA in Kotlin](dsa-kotlin-banner.png) 2 | ## Data Structures and Algorithms in Kotlin 3 | This is a GitHub repository containing implementations and explanations of various data structures[⌝](https://en.wikipedia.org/wiki/Data_structure) and algorithms[⌝](https://en.wikipedia.org/wiki/Algorithm) in Kotlin programming language. The repository covers a wide range of topics including arrays, linked lists, stacks, queues, trees, sorting algorithms, searching algorithms, and dynamic programming. Each implementation is accompanied by clear and concise code comments explaining the purpose and functionality of the code. The repository provides a great resource for anyone looking to learn or improve their understanding of data structures and algorithms using Kotlin. 4 | 5 | ### Overview 6 | - [Data Structures](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures) 7 | - Arrays[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/arrays) 8 | - Hash Tables[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/hash_tables) 9 | - Linked Lists[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/linked_lists) 10 | - Stacks[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/stacks) 11 | - Queues[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/queues) 12 | - Trees[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/trees) 13 | - Graphs[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/data_structures/graphs) 14 | - [Algorithms](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/algorithms) 15 | - Recursion[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/algorithms/recursion) 16 | - Sorting[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/algorithms/sorting) 17 | - Bubble Sort 18 | - Selection Sort 19 | - Insertion Sort 20 | - Merge Sort 21 | - Quick Sort 22 | - Searching[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/algorithms/searching) 23 | - Linear Search 24 | - Binary Search 25 | - BFS 26 | - DFS 27 | - [Dynamic Programming](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/tree/master/src/dynamic_programming) 28 | - Memoization[⌝](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/blob/2daef674620f7deb2155de5a03d3e861fc2739fa/src/dynamic_programming/memoization) 29 | 30 | ### Get started 31 | 1. Clone or download the repository and open it on your favorite IDE. (Recommended IDE: [IntelliJ IDEA](https://www.jetbrains.com/idea/download/)) 32 | 2. Checkout the implementation and explanation of various data structures and algorithms and try them out. 33 | -------------------------------------------------------------------------------- /dsa-kotlin-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/cd9746d02b34b54dd0cf8c8dacc2fb91d2a3cc07/dsa-kotlin-banner.png -------------------------------------------------------------------------------- /src/algorithms/README.md: -------------------------------------------------------------------------------- 1 | ### Algorithms overview 2 | 3 | - Recursion 4 | - Factorial (problem) 5 | - Fibonacci (problem) 6 | - Reverse string (problem) 7 | - Sorting 8 | - Bubble Sort 9 | - Selection Sort 10 | - Insertion Sort 11 | - Merge Sort 12 | - Quick Sort 13 | - Searching 14 | - Linear Search 15 | - Binary Search 16 | - BFS 17 | - DFS 18 | 19 | ### Algorithms definition 20 | 21 | Algorithms are a set of instructions or a step-by-step procedure for solving a problem or performing a task. They are an essential part of computer science and programming and are used to perform various computational tasks, such as sorting data, searching for information, and solving mathematical problems. -------------------------------------------------------------------------------- /src/algorithms/recursion/README.md: -------------------------------------------------------------------------------- 1 | ### Recursion 2 | 3 | Recursion is a programming technique where a function calls itself to solve a problem. Recursion is commonly used to 4 | solve problems that can be broken down into smaller, simpler subproblems. 5 | 6 | In Kotlin, a recursive function is defined like any other function, but it calls itself within its own body. Here's an 7 | example of a Kotlin function that uses recursion to calculate the factorial of a number: 8 | 9 | ```kotlin 10 | fun findFactorialRecursive(number: Int): Int { 11 | return if (number < 2) 12 | 1 13 | else 14 | number * findFactorialRecursive(number - 1) 15 | } 16 | ``` 17 | 18 | The `if` statement checks if the `number` is less than 2. If it is, the function returns 1, as the factorial of 0 and 1 19 | is 1. If the `number` is greater than or equal to 2, the function moves to the `else` block. 20 | 21 | In the `else` block, the function multiplies the `number` with the factorial of `number-1`. This is done by calling the 22 | same function recursively with the parameter `number-1`. The function keeps calling itself recursively with decreasing 23 | values of `number` until it reaches 1, at which point the base case is triggered and the recursion stops. 24 | 25 | For example, if `number` is 4, the function calculates `4*3*2*1` recursively. It first multiplies `4` with the result of 26 | calling `findFactorialRecursive(3)`. The `findFactorialRecursive` function then multiplies `3` with the result of 27 | calling `findFactorialRecursive(2)`, and so on until it reaches the base case of `findFactorialRecursive(1)`, which 28 | returns `1`. The multiplication chain is then complete and the final result of `4*3*2*1` is returned by the original 29 | call to `findFactorialRecursive(4)`. -------------------------------------------------------------------------------- /src/algorithms/recursion/factorial/Factorial.kt: -------------------------------------------------------------------------------- 1 | package algorithms.recursion.factorial 2 | 3 | class Factorial { 4 | 5 | /** 6 | * Returns the factorial of the given integer using a recursive approach. 7 | * 8 | * @param number the integer to calculate the factorial of 9 | * @return the factorial of the given integer 10 | */ 11 | fun findFactorialRecursive(number: Int): Int { 12 | return if (number < 2) 13 | 1 14 | else 15 | number * findFactorialRecursive(number - 1) 16 | } 17 | 18 | /** 19 | * Returns the factorial of the given integer using an iterative approach. 20 | * 21 | * @param number the integer to calculate the factorial of 22 | * @return the factorial of the given integer 23 | */ 24 | fun findFactorialIterative(number: Int): Int { 25 | return if (number < 2) { 26 | 1 27 | } else { 28 | var answer = 1 29 | for (i in 2..number) { 30 | answer *= i 31 | } 32 | answer 33 | } 34 | } 35 | } 36 | 37 | fun main() { 38 | 39 | val factorial = Factorial() 40 | println(factorial.findFactorialRecursive(5)) 41 | println(factorial.findFactorialIterative(5)) 42 | } -------------------------------------------------------------------------------- /src/algorithms/recursion/fibonacci/Fibonacci.kt: -------------------------------------------------------------------------------- 1 | package algorithms.recursion.fibonacci 2 | 3 | class Fibonacci { 4 | 5 | /** 6 | * Returns the nth number in the Fibonacci sequence using a recursive approach. 7 | * 8 | * @param n the position of the number to retrieve (starting from 0) 9 | * @return the nth number in the Fibonacci sequence 10 | */ 11 | fun fibonacciRecursive(n: Int): Int { 12 | if (n == 0) return 0 13 | else if (n == 1 || n == 2) return 1 14 | return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2) 15 | } 16 | 17 | /** 18 | * Returns the nth number in the Fibonacci sequence using an iterative approach. 19 | * 20 | * @param n the position of the number to retrieve (starting from 0) 21 | * @return the nth number in the Fibonacci sequence 22 | */ 23 | fun fibonacciIterative(n: Int): Int { 24 | return if (n == 0) { 25 | 0 26 | } else if (n == 1 || n == 2) { 27 | 1 28 | } else { 29 | val arr = intArrayOf(1, 1) 30 | for (i in 3..n) { 31 | val temp = arr[1] 32 | arr[1] = arr[0] + arr[1] 33 | arr[0] = temp 34 | } 35 | arr[1] 36 | } 37 | } 38 | } 39 | 40 | fun main() { 41 | 42 | val fibonacci = Fibonacci() 43 | println(fibonacci.fibonacciRecursive(5)) 44 | println(fibonacci.fibonacciIterative(5)) 45 | } -------------------------------------------------------------------------------- /src/algorithms/recursion/reverse_string/ReverseString.kt: -------------------------------------------------------------------------------- 1 | package algorithms.recursion.reverse_string 2 | 3 | class ReverseString { 4 | 5 | /** 6 | * Returns the reverse of the given string using a recursive approach. 7 | * 8 | * @param str the string to reverse 9 | * @return the reverse of the given string 10 | */ 11 | fun reverseStringRecursive(str: String): String { 12 | return if (str == "") 13 | "" 14 | else 15 | reverseStringRecursive(str.substring(1)) + str[0] 16 | } 17 | 18 | /** 19 | * Returns the reverse of the given string using an iterative approach. 20 | * 21 | * @param str the string to reverse 22 | * @return the reverse of the given string 23 | */ 24 | fun reverseStringIterative(str: String): String { 25 | val charArray = str.toCharArray() 26 | for (i in 0 until charArray.size / 2) { 27 | val temp = charArray[i] 28 | charArray[i] = charArray[charArray.size - 1 - i] 29 | charArray[charArray.size - 1 - i] = temp 30 | } 31 | return String(charArray) 32 | } 33 | } 34 | 35 | fun main() { 36 | 37 | val reverse = ReverseString() 38 | println(reverse.reverseStringRecursive("yoyo mastery")) 39 | println(reverse.reverseStringIterative("yoyo mastery")) 40 | } -------------------------------------------------------------------------------- /src/algorithms/searching/bfs/README.md: -------------------------------------------------------------------------------- 1 | ### Breadth-first search 2 | 3 | You can find [implementation of BFS](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/blob/master/src/data_structures/trees/binary_search_tree/BinarySearchTree.kt) in data structures section: 4 | `src/data_structures/trees/binary_search_tree/BinarySearchTree.kt` -------------------------------------------------------------------------------- /src/algorithms/searching/binary_search/BinarySearch.kt: -------------------------------------------------------------------------------- 1 | package algorithms.searching.binary_search 2 | 3 | class BinarySearch { 4 | 5 | /** 6 | * Performs a binary search on an array of integers to find the index of a specified key. 7 | * 8 | * @param arr the array to search 9 | * @param key the value to search for 10 | * @return a message indicating whether the element was found and its index, or that it was not found 11 | */ 12 | fun binarySearch(arr: IntArray, key: Int): String { 13 | var first = 0 14 | var last = arr.size - 1 15 | var middle = (first + last) / 2 16 | while (first <= last) { 17 | when { 18 | arr[middle] < key -> { 19 | first = middle + 1 20 | } 21 | arr[middle] == key -> { 22 | return "Element found at index $middle" 23 | } 24 | else -> { 25 | last = middle - 1 26 | } 27 | } 28 | middle = (first + last) / 2 29 | } 30 | return "Element not found!" 31 | } 32 | } 33 | 34 | fun main() { 35 | 36 | val binarySearch = BinarySearch() 37 | 38 | // Note: Binary search works only with sorted arrays. 39 | val array = intArrayOf(10, 25, 32, 45, 55, 68) 40 | val result = binarySearch.binarySearch(array, 55) 41 | println(result) 42 | 43 | val result2 = binarySearch.binarySearch(array, 100) 44 | println(result2) 45 | } -------------------------------------------------------------------------------- /src/algorithms/searching/binary_search/README.md: -------------------------------------------------------------------------------- 1 | ### Binary search 2 | 3 | This function performs a binary search on a sorted array of integers to find the index of a specified key. The algorithm 4 | works by repeatedly dividing the array in half and discarding the half that the key cannot be in, until the key is found 5 | or the entire array has been searched. 6 | 7 | ```kotlin 8 | fun binarySearch(arr: IntArray, key: Int): String { 9 | // Initialize first, last, and middle indexes 10 | var first = 0 11 | var last = arr.size - 1 12 | var middle = (first + last) / 2 13 | 14 | // Loop until the first index is greater than the last index 15 | while (first <= last) { 16 | when { 17 | // If the middle element is less than the key, update the first index to middle + 1 18 | // This means that the key is in the right half of the array 19 | arr[middle] < key -> { 20 | first = middle + 1 21 | } 22 | // If the middle element is equal to the key, return a message indicating that it was found 23 | // and its index 24 | arr[middle] == key -> { 25 | return "Element found at index $middle" 26 | } 27 | // If the middle element is greater than the key, update the last index to middle - 1 28 | // This means that the key is in the left half of the array 29 | else -> { 30 | last = middle - 1 31 | } 32 | } 33 | // Recalculate the middle index based on the updated first and last indexes 34 | middle = (first + last) / 2 35 | } 36 | // If the loop completes and the key is not found, return a message indicating that it was not found 37 | return "Element not found!" 38 | } 39 | ``` -------------------------------------------------------------------------------- /src/algorithms/searching/dfs/README.md: -------------------------------------------------------------------------------- 1 | ### Depth-first search 2 | 3 | You can find [implementation of DFS](https://github.com/abdurakhmonoff/data-structures-and-algorithms-kotlin/blob/master/src/data_structures/trees/binary_search_tree/BinarySearchTree.kt) in data structures section: 4 | `src/data_structures/trees/binary_search_tree/BinarySearchTree.kt` -------------------------------------------------------------------------------- /src/algorithms/searching/linear_search/LinearSearch.kt: -------------------------------------------------------------------------------- 1 | package algorithms.searching.linear_search 2 | 3 | class LinearSearch { 4 | 5 | /** 6 | * Performs a linear search on an array of integers to find the index of a specified key. 7 | * 8 | * @param arr the array to search 9 | * @param key the value to search for 10 | * @return the index of the key if it is found, or -1 if it is not found 11 | */ 12 | fun linearSearch(arr: IntArray, key: Int): Int { 13 | for (i in arr.indices) { 14 | if (arr[i] == key) { 15 | return i 16 | } 17 | } 18 | return -1 19 | } 20 | } 21 | 22 | fun main() { 23 | 24 | val linearSearch = LinearSearch() 25 | val arr = intArrayOf(12, 45, 69, 78, 89, 54) 26 | val answer = linearSearch.linearSearch(arr, 69) 27 | println(answer) 28 | } -------------------------------------------------------------------------------- /src/algorithms/searching/linear_search/README.md: -------------------------------------------------------------------------------- 1 | ### Linear search 2 | 3 | This function performs a linear search on an array of integers to find the index of a specified key. The algorithm works 4 | by iterating through each element in the array and comparing it to the key until the key is found or the entire array 5 | has been searched. 6 | 7 | ```kotlin 8 | fun linearSearch(arr: IntArray, key: Int): Int { 9 | // Loop through each element in the array 10 | for (i in arr.indices) { 11 | // If the current element is equal to the key, return its index 12 | if (arr[i] == key) { 13 | return i 14 | } 15 | } 16 | // If the loop completes without finding the key, return -1 17 | return -1 18 | } 19 | ``` -------------------------------------------------------------------------------- /src/algorithms/sorting/bubble_sort/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.bubble_sort 2 | 3 | /** 4 | * Performs a bubble sort on an array of integers to sort it in ascending order. 5 | * 6 | * @param array the array to sort 7 | */ 8 | fun bubbleSort(array: IntArray) { 9 | // Outer loop iterates over the array indices 10 | for (i in array.indices) { 11 | // Inner loop compares adjacent elements of the array and swaps them if 12 | // they are in the wrong order 13 | for (j in 0 until array.size - 1) { 14 | // If the current element is greater than the next element, swap them 15 | if (array[j] > array[j + 1]) { 16 | val temp = array[j] 17 | array[j] = array[j + 1] 18 | array[j + 1] = temp 19 | } 20 | } 21 | } 22 | } 23 | 24 | fun main() { 25 | 26 | val numbers = intArrayOf(99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0) 27 | bubbleSort(numbers) 28 | println(numbers.contentToString()) 29 | } -------------------------------------------------------------------------------- /src/algorithms/sorting/bubble_sort/README.md: -------------------------------------------------------------------------------- 1 | ### Bubble sort 2 | 3 | This code implements a bubble sort algorithm for an array of integers. The algorithm works by iterating over 4 | the array and comparing adjacent elements. If the adjacent elements are not in the correct order, they are swapped. This 5 | process is repeated until the entire array is sorted. The outer loop iterates over the array indices, while the inner 6 | loop compares adjacent elements of the array and swaps them if they are in the wrong order. 7 | 8 | ```kotlin 9 | fun bubbleSort(array: IntArray) { 10 | // Outer loop iterates over the array indices 11 | for (i in array.indices) { 12 | // Inner loop compares adjacent elements of the array and swaps them if 13 | // they are in the wrong order 14 | for (j in 0 until array.size - 1) { 15 | // If the current element is greater than the next element, swap them 16 | if (array[j] > array[j + 1]) { 17 | val temp = array[j] 18 | array[j] = array[j + 1] 19 | array[j + 1] = temp 20 | } 21 | } 22 | } 23 | } 24 | ``` -------------------------------------------------------------------------------- /src/algorithms/sorting/insertion_sort/InsertionSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.insertion_sort 2 | 3 | /** 4 | * Performs an insertion sort on an ArrayList of integers to sort it in ascending order. 5 | * 6 | * @param array the ArrayList to sort 7 | */ 8 | fun insertionSort(array: ArrayList) { 9 | // Iterate over the indices of the input array 10 | for (i in array.indices) { 11 | // If the current number is less than or equal to the first number, insert it at the beginning of the array 12 | if (array[i] <= array[0]) { 13 | array.add(0, array.removeAt(i)) 14 | } else { 15 | // If the current number is less than the previous number, find the correct position to insert it 16 | if (array[i] < array[i - 1]) { 17 | // Iterate over the array from the second number to the current number 18 | for (j in 1 until i) { 19 | // If the current number is less than the j-th number, insert it at that position 20 | if (array[i] < array[j]) { 21 | array.add(j, array.removeAt(i)) 22 | break 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | 30 | fun main() { 31 | 32 | val numbers = ArrayList(listOf(99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0)) 33 | insertionSort(numbers) 34 | println(numbers) 35 | } -------------------------------------------------------------------------------- /src/algorithms/sorting/insertion_sort/README.md: -------------------------------------------------------------------------------- 1 | ### Insertion sort 2 | 3 | This code implements an insertion sort algorithm for an ArrayList of integers. Insertion sort is a simple sorting 4 | algorithm that sorts an array by repeatedly shifting elements that are greater than 5 | the current element to be sorted one position ahead of their current position. The algorithm works by iterating over the 6 | input array, and at each iteration, it inserts the current element into its correct position in the sorted portion of 7 | the array that precedes it. 8 | 9 | ```kotlin 10 | fun insertionSort(array: ArrayList) { 11 | // Iterate over the indices of the input array 12 | for (i in array.indices) { 13 | // If the current number is less than or equal to the first number, insert it at the beginning of the array 14 | if (array[i] <= array[0]) { 15 | array.add(0, array.removeAt(i)) 16 | } else { 17 | // If the current number is less than the previous number, find the correct position to insert it 18 | if (array[i] < array[i - 1]) { 19 | // Iterate over the array from the second number to the current number 20 | for (j in 1 until i) { 21 | // If the current number is less than the j-th number, insert it at that position 22 | if (array[i] < array[j]) { 23 | array.add(j, array.removeAt(i)) 24 | break 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | ``` -------------------------------------------------------------------------------- /src/algorithms/sorting/merge_sort/MergeSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.merge_sort 2 | 3 | /** 4 | * Implementation of the merge sort algorithm for sorting a list of integers in ascending order. 5 | */ 6 | class MergeSort { 7 | 8 | /** 9 | * Recursively sorts the given list by dividing it into halves and merging them. 10 | * 11 | * @param array the list to sort 12 | * @return the sorted list 13 | */ 14 | fun mergeSort(array: List): List { 15 | // Base case: if array has only one number, it is already sorted 16 | if (array.size == 1) { 17 | return array 18 | } 19 | // Divide the array into two halves and recursively sort each half 20 | val left = array.subList(0, array.size / 2) 21 | val right = array.subList(array.size / 2, array.size) 22 | return merge(mergeSort(left), mergeSort(right)) 23 | } 24 | 25 | /** 26 | * Merges two sorted lists into a single sorted list. 27 | * 28 | * @param left the first sorted list 29 | * @param right the second sorted list 30 | * @return the merged sorted list 31 | */ 32 | private fun merge(left: List, right: List): List { 33 | val result = ArrayList() 34 | var leftIndex = 0 35 | var rightIndex = 0 36 | // Iterate through both lists, comparing the numbers and adding the smaller one to the result list 37 | while (leftIndex < left.size && rightIndex < right.size) { 38 | if (left[leftIndex] < right[rightIndex]) { 39 | result.add(left[leftIndex]) 40 | leftIndex++ 41 | } else { 42 | result.add(right[rightIndex]) 43 | rightIndex++ 44 | } 45 | } 46 | // Add any remaining numbers from the left and right lists 47 | val leftRemaining = left.subList(leftIndex, left.size) 48 | val rightRemaining = right.subList(rightIndex, right.size) 49 | result.addAll(leftRemaining) 50 | result.addAll(rightRemaining) 51 | return result 52 | } 53 | } 54 | 55 | fun main() { 56 | 57 | val mergeSort = MergeSort() 58 | val numbers = ArrayList(listOf(99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0)) 59 | println(mergeSort.mergeSort(numbers)) 60 | } -------------------------------------------------------------------------------- /src/algorithms/sorting/merge_sort/README.md: -------------------------------------------------------------------------------- 1 | ### Merge sort 2 | 3 | Merge Sort is a sorting algorithm that utilizes the divide-and-conquer strategy. The basic idea behind this algorithm is 4 | to divide the input array into two halves, sort them separately, and then merge them into a single sorted output array. 5 | It achieves this by recursively dividing the input array into smaller sub-arrays until the base case of a single element 6 | array is reached, which is trivially sorted. The sub-arrays are then merged together in a series of comparisons that 7 | result in a new sorted array. 8 | 9 | ```kotlin 10 | class MergeSort { 11 | 12 | fun mergeSort(array: List): List { 13 | // Base case: if array has only one number, it is already sorted 14 | if (array.size == 1) { 15 | return array 16 | } 17 | // Divide the array into two halves and recursively sort each half 18 | val left = array.subList(0, array.size / 2) 19 | val right = array.subList(array.size / 2, array.size) 20 | return merge(mergeSort(left), mergeSort(right)) 21 | } 22 | 23 | private fun merge(left: List, right: List): List { 24 | val result = ArrayList() 25 | var leftIndex = 0 26 | var rightIndex = 0 27 | // Iterate through both lists, comparing the numbers and adding the smaller one to the result list 28 | while (leftIndex < left.size && rightIndex < right.size) { 29 | if (left[leftIndex] < right[rightIndex]) { 30 | result.add(left[leftIndex]) 31 | leftIndex++ 32 | } else { 33 | result.add(right[rightIndex]) 34 | rightIndex++ 35 | } 36 | } 37 | // Add any remaining numbers from the left and right lists 38 | val leftRemaining = left.subList(leftIndex, left.size) 39 | val rightRemaining = right.subList(rightIndex, right.size) 40 | result.addAll(leftRemaining) 41 | result.addAll(rightRemaining) 42 | return result 43 | } 44 | } 45 | ``` -------------------------------------------------------------------------------- /src/algorithms/sorting/quick_sort/QuickSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.quick_sort 2 | 3 | /** 4 | * Implementation of the quick sort algorithm for sorting a list of integers in ascending order. 5 | */ 6 | class QuickSort { 7 | 8 | /** 9 | * Sorts an array of integers in ascending order using QuickSort algorithm. 10 | * 11 | * @param arr the array to be sorted. 12 | * @param low the starting index of the array. 13 | * @param high the ending index of the array. 14 | */ 15 | fun quickSort(arr: IntArray, low: Int, high: Int) { 16 | if (low < high) { 17 | // Find partition index 18 | val pIndex = partition(arr, low, high) 19 | // Recursively sort elements before and after partition index 20 | quickSort(arr, low, pIndex - 1) 21 | quickSort(arr, pIndex + 1, high) 22 | } 23 | } 24 | 25 | /** 26 | * Partitions the array into two sub-arrays around the pivot element. 27 | * 28 | * @param arr the array to be partitioned. 29 | * @param low the starting index of the array. 30 | * @param high the ending index of the array. 31 | * @return the index of the pivot element after partitioning. 32 | */ 33 | private fun partition(arr: IntArray, low: Int, high: Int): Int { 34 | val pivot = arr[high] 35 | var i = low - 1 36 | // Iterate through numbers from low to high-1 37 | for (j in low until high) { 38 | if (arr[j] <= pivot) { 39 | i++ 40 | // Swap arr[i] and arr[j] 41 | val temp = arr[i] 42 | arr[i] = arr[j] 43 | arr[j] = temp 44 | } 45 | } 46 | // Place pivot at correct position in array 47 | val temp = arr[i + 1] 48 | arr[i + 1] = arr[high] 49 | arr[high] = temp 50 | // Return partition index 51 | return i + 1 52 | } 53 | 54 | /** 55 | * Prints the elements of the array. 56 | * 57 | * @param arr the array to be printed. 58 | */ 59 | fun printArray(arr: IntArray) { 60 | for (value in arr) print("$value ") 61 | println() 62 | } 63 | } 64 | 65 | fun main() { 66 | 67 | val numbers = intArrayOf(99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0) 68 | val quickSort = QuickSort() 69 | quickSort.quickSort(numbers, 0, numbers.size - 1) 70 | quickSort.printArray(numbers) 71 | } -------------------------------------------------------------------------------- /src/algorithms/sorting/quick_sort/README.md: -------------------------------------------------------------------------------- 1 | ### Quick sort 2 | 3 | Quick sort is a sorting algorithm that uses the divide-and-conquer approach to sort an input array. It selects a pivot 4 | element from the array and partitions the array into two sub-arrays: one with elements smaller than the pivot and the 5 | other with elements greater than or equal to the pivot. This process is then recursively applied to the two sub-arrays 6 | until they contain only one element. The base case of a single element array is trivially sorted. The sub-arrays are 7 | then combined by concatenating the sorted sub-arrays with the pivot element in between them to produce the sorted output 8 | array. 9 | 10 | ```kotlin 11 | class QuickSort { 12 | 13 | fun quickSort(arr: IntArray, low: Int, high: Int) { 14 | if (low < high) { 15 | // Find partition index 16 | val pIndex = partition(arr, low, high) 17 | // Recursively sort elements before and after partition index 18 | quickSort(arr, low, pIndex - 1) 19 | quickSort(arr, pIndex + 1, high) 20 | } 21 | } 22 | 23 | private fun partition(arr: IntArray, low: Int, high: Int): Int { 24 | val pivot = arr[high] 25 | var i = low - 1 26 | // Iterate through numbers from low to high-1 27 | for (j in low until high) { 28 | if (arr[j] <= pivot) { 29 | i++ 30 | // Swap arr[i] and arr[j] 31 | val temp = arr[i] 32 | arr[i] = arr[j] 33 | arr[j] = temp 34 | } 35 | } 36 | // Place pivot at correct position in array 37 | val temp = arr[i + 1] 38 | arr[i + 1] = arr[high] 39 | arr[high] = temp 40 | // Return partition index 41 | return i + 1 42 | } 43 | } 44 | ``` -------------------------------------------------------------------------------- /src/algorithms/sorting/selection_sort/README.md: -------------------------------------------------------------------------------- 1 | ### Selection sort 2 | 3 | Selection sort is a simple sorting algorithm that works by repeatedly finding the smallest element from an unsorted part 4 | of the array and swapping it with the first element of the unsorted part. This process is repeated until the entire 5 | array is sorted. It is an in-place algorithm and is not recursive, making it easy to implement and understand. 6 | 7 | ```kotlin 8 | fun selectionSort(array: IntArray) { 9 | for (i in array.indices) { 10 | // Find the index of the smallest number in the unsorted portion of the array 11 | var smallest = i 12 | for (j in i until array.size) { 13 | if (array[j] < array[smallest]) { 14 | smallest = j 15 | } 16 | } 17 | // Swap the smallest number with the first number in the unsorted portion of the array 18 | val temp = array[i] 19 | array[i] = array[smallest] 20 | array[smallest] = temp 21 | } 22 | } 23 | ``` -------------------------------------------------------------------------------- /src/algorithms/sorting/selection_sort/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sorting.selection_sort 2 | 3 | /** 4 | * Sorts an array of integers using the selection sort algorithm. 5 | * 6 | * @param array the array to sort 7 | * @return the sorted array 8 | */ 9 | fun selectionSort(array: IntArray): IntArray { 10 | for (i in array.indices) { 11 | // Find the index of the smallest number in the unsorted portion of the array 12 | var smallest = i 13 | for (j in i until array.size) { 14 | if (array[j] < array[smallest]) { 15 | smallest = j 16 | } 17 | } 18 | // Swap the smallest number with the first number in the unsorted portion of the array 19 | val temp = array[i] 20 | array[i] = array[smallest] 21 | array[smallest] = temp 22 | } 23 | return array 24 | } 25 | 26 | fun main() { 27 | 28 | val numbers = intArrayOf(99, 44, 6, 2, 1, 5, 63, 87, 283, 4, 0) 29 | println(selectionSort(numbers).contentToString()) 30 | } -------------------------------------------------------------------------------- /src/data_structures/README.md: -------------------------------------------------------------------------------- 1 | ### Data structures overview 2 | 3 | - Arrays 4 | - Implementation (problem) 5 | - Merge sorted arrays (problem) 6 | - Reverse string (problem) 7 | - Hash Tables 8 | - First recurring character (problem) 9 | - Implementation (problem) 10 | - Linked Lists 11 | - Doubly linked list 12 | - Singly linked list 13 | - Stacks 14 | - Stack using array (problem) 15 | - Stack using linked list (problem) 16 | - Queues 17 | - Queue using linked list (problem) 18 | - Queue using stacks (problem) 19 | - Trees 20 | - Binary search tree 21 | - Priority queue 22 | - Trie 23 | - Graphs 24 | 25 | ### Data structures definition 26 | 27 | Data structures refer to a way of organizing and storing data in a computer program, which can be used to efficiently access and manipulate the data. A variety of data structures exist, each designed to meet different needs and perform different operations. -------------------------------------------------------------------------------- /src/data_structures/arrays/README.md: -------------------------------------------------------------------------------- 1 | ### Arrays 2 | 3 | An array is a fundamental data structure in computer programming that allows for efficient storage and retrieval of a 4 | collection of elements of the same type. It is a contiguous block of memory, where each element is accessed by its 5 | index. The index is an integer that represents the position of the element in the array, starting from 0. Arrays can be 6 | used to store primitive data types such as integers, characters, and booleans, as well as more complex objects such as 7 | strings and custom data types. The key advantage of arrays is their constant-time access to individual elements, which 8 | allows for fast retrieval and modification of data. However, the size of an array is fixed at creation time, so resizing 9 | requires creating a new array and copying the elements, which can be time-consuming and memory-intensive. Here's how we 10 | can implement an array that can dynamically resize itself as needed: 11 | 12 | ```kotlin 13 | class DynamicArray( 14 | private var array: Array = arrayOfNulls(1), 15 | private var capacity: Int = 1, 16 | var length: Int = 0 17 | ) { 18 | 19 | fun get(index: Int): String? { 20 | return if (index > -1 && index < length) array[index] else "IndexOutOfBounds" 21 | } 22 | 23 | fun push(string: String) { 24 | if (length == capacity) { 25 | val tempArray = arrayOfNulls(2 * capacity) 26 | for (i in array.indices) { 27 | tempArray[i] = array[i] 28 | } 29 | array = tempArray 30 | capacity *= 2 31 | } 32 | array[length] = string 33 | length++ 34 | } 35 | 36 | fun replace(index: Int, value: String) { 37 | if (index > -1 && index < length) 38 | array[index] = value 39 | else 40 | println("IndexOutOfBounds") 41 | } 42 | 43 | fun pop() { 44 | if (length > 0) { 45 | length-- 46 | } 47 | } 48 | 49 | fun delete(index: Int) { 50 | if (index > -1 && index < length) { 51 | if (index == length - 1) { 52 | pop() 53 | } else { 54 | for (i in index until length) { 55 | array[i] = array[i + 1] 56 | } 57 | length-- 58 | } 59 | } else { 60 | println("IndexOutOfBounds") 61 | } 62 | } 63 | 64 | fun array(): Array { 65 | val tempArr = arrayOfNulls(length) 66 | for (i in 0 until length) { 67 | tempArr[i] = array[i] 68 | } 69 | return tempArr 70 | } 71 | } 72 | ``` -------------------------------------------------------------------------------- /src/data_structures/arrays/implementation/DynamicArray.kt: -------------------------------------------------------------------------------- 1 | package data_structures.arrays.implementation 2 | 3 | /** 4 | * Implementation of array data structure that can dynamically resize itself as needed. 5 | * 6 | * @constructor Creates an empty dynamic array. 7 | * @property array The array used to store elements. 8 | * @property capacity The current capacity of the array. 9 | * @property length The current number of elements in the array. 10 | */ 11 | class DynamicArray( 12 | private var array: Array = arrayOfNulls(1), 13 | private var capacity: Int = 1, 14 | var length: Int = 0 15 | ) { 16 | 17 | /** 18 | * Returns the element at the specified index. 19 | * 20 | * @param index The index of the element to retrieve. 21 | * @return The element at the specified index, or "IndexOutOfBounds" if the index is out of range. 22 | */ 23 | fun get(index: Int): String? { 24 | return if (index > -1 && index < length) array[index] else "IndexOutOfBounds" 25 | } 26 | 27 | /** 28 | * Appends the specified element to the end of the array. 29 | * If the array is full, its capacity is doubled before the element is added. 30 | * 31 | * @param string The element to add. 32 | */ 33 | fun push(string: String) { 34 | if (length == capacity) { 35 | val tempArray = arrayOfNulls(2 * capacity) 36 | for (i in array.indices) { 37 | tempArray[i] = array[i] 38 | } 39 | array = tempArray 40 | capacity *= 2 41 | } 42 | array[length] = string 43 | length++ 44 | } 45 | 46 | /** 47 | * Replaces the element at the specified index with the specified value. 48 | * 49 | * @param index The index of the element to replace. 50 | * @param value The new value to set. 51 | */ 52 | fun replace(index: Int, value: String) { 53 | if (index > -1 && index < length) 54 | array[index] = value 55 | else 56 | println("IndexOutOfBounds") 57 | } 58 | 59 | /** 60 | * Removes the last element from the array. 61 | */ 62 | fun pop() { 63 | if (length > 0) { 64 | length-- 65 | } 66 | } 67 | 68 | /** 69 | * Removes the element at the specified index from the array. 70 | * 71 | * @param index The index of the element to remove. 72 | */ 73 | fun delete(index: Int) { 74 | if (index > -1 && index < length) { 75 | if (index == length - 1) { 76 | pop() 77 | } else { 78 | for (i in index until length) { 79 | array[i] = array[i + 1] 80 | } 81 | length-- 82 | } 83 | } else { 84 | println("IndexOutOfBounds") 85 | } 86 | } 87 | 88 | /** 89 | * Returns a new array containing all of the elements in this DynamicArray. 90 | * 91 | * @return An array containing all of the elements in this DynamicArray. 92 | */ 93 | fun array(): Array { 94 | val tempArr = arrayOfNulls(length) 95 | for (i in 0 until length) { 96 | tempArr[i] = array[i] 97 | } 98 | return tempArr 99 | } 100 | } 101 | 102 | fun main() { 103 | 104 | val dynamicArray = DynamicArray() 105 | dynamicArray.push("apple") 106 | dynamicArray.push("ball") 107 | dynamicArray.push("cat") 108 | dynamicArray.push("dog") 109 | 110 | println("Array length: " + dynamicArray.length) 111 | println("array: " + dynamicArray.array().contentToString()) 112 | println("item at index 2: " + dynamicArray.get(2)) 113 | 114 | dynamicArray.replace(1, "world") 115 | println("array: " + dynamicArray.array().contentToString()) 116 | 117 | dynamicArray.pop() 118 | println("array: " + dynamicArray.array().contentToString()) 119 | 120 | dynamicArray.delete(1) 121 | println("array: " + dynamicArray.array().contentToString()) 122 | 123 | println("Array length: " + dynamicArray.length) 124 | } -------------------------------------------------------------------------------- /src/data_structures/arrays/merge_sorted_arrays/MergeSortedArrays.kt: -------------------------------------------------------------------------------- 1 | package data_structures.arrays.merge_sorted_arrays 2 | 3 | /** 4 | * This function takes two sorted arrays of integers as input and returns a single sorted array that contains all the elements of both input arrays. 5 | * 6 | * @param arr1 First sorted integer array 7 | * @param arr2 Second sorted integer array 8 | * @return Single sorted integer array that contains all the elements of both input arrays 9 | */ 10 | fun mergeSortedArrays(arr1: IntArray, arr2: IntArray): IntArray { 11 | // Initialize indices for arr1, arr2, and the merged array 12 | var i = 0 13 | var j = 0 14 | var k = 0 15 | // Initialize a new array to hold the merged and sorted values 16 | val mergedArray = IntArray(arr1.size + arr2.size) 17 | // Compare values from arr1 and arr2 and insert them into the merged array in ascending order 18 | while (i < arr1.size && j < arr2.size) { 19 | if (arr1[i] < arr2[j]) { 20 | mergedArray[k] = arr1[i] 21 | i++ 22 | } else { 23 | mergedArray[k] = arr2[j] 24 | j++ 25 | } 26 | k++ 27 | } 28 | // If arr1 has remaining values, append them to the merged array 29 | while (i < arr1.size) { 30 | mergedArray[k] = arr1[i] 31 | i++ 32 | k++ 33 | } 34 | // If arr2 has remaining values, append them to the merged array 35 | while (j < arr2.size) { 36 | mergedArray[k] = arr2[j] 37 | j++ 38 | k++ 39 | } 40 | // Return the merged and sorted array 41 | return mergedArray 42 | } 43 | 44 | fun main() { 45 | 46 | val mergedArray = mergeSortedArrays(intArrayOf(0, 2, 3, 56), intArrayOf(0, 6, 7)) 47 | println(mergedArray.contentToString()) 48 | } -------------------------------------------------------------------------------- /src/data_structures/arrays/reverse_string/ReverseString.kt: -------------------------------------------------------------------------------- 1 | package data_structures.arrays.reverse_string 2 | 3 | /** 4 | * This function takes a String parameter and returns the reversed version of the input string. 5 | * 6 | * @param string String to reverse 7 | * @return Reversed input string 8 | */ 9 | private fun reverse(string: String): String { 10 | // Initialize an empty string 11 | var result = "" 12 | // Iterate through the characters of the input string in reverse order 13 | for (i in string.length - 1 downTo 0) { 14 | // Append each character to the result string to form the reversed string 15 | result += string[i] 16 | } 17 | // Return the resulting reversed string 18 | return result 19 | } 20 | 21 | /** 22 | * This function uses Kotlin built-in function called reversed(). 23 | * 24 | * @param string String to reverse 25 | * @return Reversed input string 26 | */ 27 | private fun reverse2(string: String) = string.reversed() 28 | 29 | fun main() { 30 | 31 | println(reverse("Hello, World!")) 32 | println(reverse2("Hello, World!")) 33 | } -------------------------------------------------------------------------------- /src/data_structures/graphs/Graph.kt: -------------------------------------------------------------------------------- 1 | package data_structures.graphs 2 | 3 | import java.util.* 4 | import kotlin.collections.ArrayList 5 | 6 | /** 7 | * A Kotlin class that represents an undirected graph using an adjacency list. 8 | * 9 | * @property numberOfNodes The number of nodes in the graph. 10 | * @property adjacentList The adjacency list that stores the connections between nodes. 11 | */ 12 | class Graph( 13 | var numberOfNodes: Int = 0, 14 | var adjacentList: Hashtable> = Hashtable() 15 | ) { 16 | 17 | /** 18 | * Adds a new vertex (node) to the graph. 19 | * @param node The node to be added to the graph. 20 | */ 21 | fun addVertex(node: Int) { 22 | adjacentList[node] = ArrayList() 23 | numberOfNodes++ 24 | } 25 | 26 | /** 27 | * Adds an edge between two nodes in the graph. 28 | * @param node1 The first node of the edge. 29 | * @param node2 The second node of the edge. 30 | */ 31 | fun addEdge(node1: Int, node2: Int) { 32 | adjacentList[node1]?.add(node2) 33 | adjacentList[node2]?.add(node1) 34 | } 35 | 36 | /** 37 | * Prints out the adjacency list that stores the connections between nodes in the graph. 38 | */ 39 | fun showConnections() { 40 | val keys: Array = adjacentList.keys.toTypedArray() 41 | for (key in keys) { 42 | println(key.toString() + " --> " + adjacentList[key.toString().toInt()]) 43 | } 44 | } 45 | } 46 | 47 | fun main() { 48 | 49 | val graph = Graph() 50 | graph.addVertex(5) 51 | graph.addVertex(54) 52 | graph.addVertex(44) 53 | graph.addEdge(5, 54) 54 | graph.addEdge(5, 44) 55 | graph.showConnections() 56 | } -------------------------------------------------------------------------------- /src/data_structures/graphs/README.md: -------------------------------------------------------------------------------- 1 | ### Graphs 2 | 3 | Graph is a data structure that consists of a collection of nodes, also known as vertices, connected by edges. Graphs are 4 | a fundamental data structure used in many applications, such as social networks, web page ranking, transportation 5 | networks, and more. In a graph, nodes represent objects, and edges represent the relationships between them. Graphs can 6 | be either directed or undirected, depending on whether the edges have a direction or not. They can also be weighted or 7 | unweighted, depending on whether the edges have a value or not. Here's how we can implement a graph data structure: 8 | 9 | ```kotlin 10 | class Graph( 11 | var numberOfNodes: Int = 0, 12 | var adjacentList: Hashtable> = Hashtable() 13 | ) { 14 | 15 | fun addVertex(node: Int) { 16 | adjacentList[node] = ArrayList() 17 | numberOfNodes++ 18 | } 19 | 20 | fun addEdge(node1: Int, node2: Int) { 21 | adjacentList[node1]?.add(node2) 22 | adjacentList[node2]?.add(node1) 23 | } 24 | 25 | fun showConnections() { 26 | val keys: Array = adjacentList.keys.toTypedArray() 27 | for (key in keys) { 28 | println(key.toString() + " --> " + adjacentList[key.toString().toInt()]) 29 | } 30 | } 31 | } 32 | ``` -------------------------------------------------------------------------------- /src/data_structures/hash_tables/README.md: -------------------------------------------------------------------------------- 1 | ### Hash tables 2 | 3 | A hash table is a data structure that uses a hash function to map keys to unique indexes in an array, allowing for fast 4 | insertion, deletion, and retrieval of data. Each key is hashed to an index in the array where the corresponding value is 5 | stored. In case of collisions, where multiple keys map to the same index, the values are typically stored in a linked 6 | list or other data structure. Here's how we can implement a hash table data structure using `KeyValue` model: 7 | 8 | ```kotlin 9 | data class KeyValue(val key: String, val value: Int) 10 | 11 | class HashTable(size: Int) { 12 | private var data: Array?> = arrayOfNulls?>(size) 13 | private var currentLength: Int = 0 14 | 15 | private fun _hash(key: String): Int { 16 | var hash = 0 17 | for (i in key.indices) { 18 | hash = (hash + key.codePointAt(i) * i) % data.size 19 | } 20 | return hash 21 | } 22 | 23 | operator fun set(key: String, value: Int) { 24 | val address = _hash(key) 25 | if (data[address] == null) { 26 | val arrayAtAddress = ArrayList() 27 | data[address] = arrayAtAddress 28 | currentLength++ 29 | } 30 | val pair = KeyValue(key, value) 31 | data[address]?.add(pair) 32 | } 33 | 34 | operator fun get(key: String): Int? { 35 | val address = _hash(key) 36 | val bucket = data[address] 37 | if (bucket != null) { 38 | for (keyValue in bucket) { 39 | if (keyValue.key == key) { 40 | return keyValue.value 41 | } 42 | } 43 | } 44 | return null 45 | } 46 | 47 | fun keys(): Array { 48 | val bucket = data 49 | val keysArray = arrayOfNulls(currentLength) 50 | var count = 0 51 | for (keyValues in bucket) { 52 | if (keyValues != null) { 53 | keysArray[count] = keyValues[0].key 54 | count++ 55 | } 56 | } 57 | return keysArray 58 | } 59 | } 60 | ``` -------------------------------------------------------------------------------- /src/data_structures/hash_tables/first_recurring_character/FirstRecurring.kt: -------------------------------------------------------------------------------- 1 | package data_structures.hash_tables.first_recurring_character 2 | 3 | /** 4 | * Finds the first recurring integer in an array of integers. 5 | * @param array an integer array to search for recurring integer 6 | * @return The first recurring integer or null if none found 7 | */ 8 | private fun firstRecurringCharacter(array: IntArray): Int? { 9 | val hashSet = HashSet() 10 | for (element in array) { 11 | if (hashSet.contains(element)) { 12 | return element 13 | } else { 14 | hashSet.add(element) 15 | } 16 | } 17 | return null 18 | } 19 | 20 | fun main() { 21 | 22 | val firstRecurring = firstRecurringCharacter(intArrayOf(2, 5, 5, 2, 3, 5, 1, 2, 4)) 23 | println(firstRecurring) 24 | } -------------------------------------------------------------------------------- /src/data_structures/hash_tables/implementation/HashTable.kt: -------------------------------------------------------------------------------- 1 | package data_structures.hash_tables.implementation 2 | 3 | import java.util.* 4 | 5 | /** 6 | * A class representing a hash table data structure that stores key-value pairs. 7 | * @param size the initial size of the hash table 8 | */ 9 | class HashTable(size: Int) { 10 | private var data: Array?> = arrayOfNulls?>(size) 11 | private var currentLength: Int = 0 12 | 13 | /** 14 | * A private hash function that takes a key as input and returns its corresponding hash code. 15 | * @param key the key to hash 16 | * @return the hash code of the key 17 | */ 18 | private fun _hash(key: String): Int { 19 | var hash = 0 20 | for (i in key.indices) { 21 | hash = (hash + key.codePointAt(i) * i) % data.size 22 | } 23 | return hash 24 | } 25 | 26 | /** 27 | * A method that inserts a key-value pair into the hash table. 28 | * @param key the key of the pair to insert 29 | * @param value the value of the pair to insert 30 | */ 31 | operator fun set(key: String, value: Int) { 32 | val address = _hash(key) 33 | if (data[address] == null) { 34 | val arrayAtAddress = ArrayList() 35 | data[address] = arrayAtAddress 36 | currentLength++ 37 | } 38 | val pair = KeyValue(key, value) 39 | data[address]?.add(pair) 40 | } 41 | 42 | /** 43 | * A method that retrieves the value associated with a given key from the hash table. 44 | * @param key the key of the pair to retrieve 45 | * @return the value associated with the given key or null if the key is not found 46 | */ 47 | operator fun get(key: String): Int? { 48 | val address = _hash(key) 49 | val bucket = data[address] 50 | if (bucket != null) { 51 | for (keyValue in bucket) { 52 | if (keyValue.key == key) { 53 | return keyValue.value 54 | } 55 | } 56 | } 57 | return null 58 | } 59 | 60 | /** 61 | * A method that returns an array of all keys in the hash table. 62 | * @return an array of all keys in the hash table 63 | */ 64 | fun keys(): Array { 65 | val bucket = data 66 | val keysArray = arrayOfNulls(currentLength) 67 | var count = 0 68 | for (keyValues in bucket) { 69 | if (keyValues != null) { 70 | keysArray[count] = keyValues[0].key 71 | count++ 72 | } 73 | } 74 | return keysArray 75 | } 76 | } 77 | 78 | fun main() { 79 | 80 | val hashTable = HashTable(50) 81 | hashTable["grapes"] = 1200 82 | hashTable["apple"] = 1500 83 | println("value for key grapes: " + hashTable["grapes"]) 84 | println("value for key apple: " + hashTable["apple"]) 85 | println("list of keys: " + hashTable.keys().contentToString()) 86 | } -------------------------------------------------------------------------------- /src/data_structures/hash_tables/implementation/KeyValue.kt: -------------------------------------------------------------------------------- 1 | package data_structures.hash_tables.implementation 2 | 3 | /** 4 | * A data class representing a key-value pair. 5 | * @param key the key of the pair 6 | * @param value the value of the pair 7 | */ 8 | data class KeyValue(val key: String, val value: Int) -------------------------------------------------------------------------------- /src/data_structures/linked_lists/doubly_linked_list/DoublyLinkedList.kt: -------------------------------------------------------------------------------- 1 | package data_structures.linked_lists.doubly_linked_list 2 | 3 | /** 4 | * Implementation of a doubly linked list data structure with basic operations. 5 | */ 6 | class DoublyLinkedList(value: Int) { 7 | var head: Node? = Node(value) 8 | var tail: Node? = head 9 | var length: Int = 1 10 | 11 | /** 12 | * Append a new node to the end of the list. 13 | * 14 | * @param value the value to be added to the list 15 | */ 16 | fun append(value: Int) { 17 | val newNode = Node(value) 18 | newNode.previous = tail 19 | tail?.next = newNode 20 | tail = newNode 21 | length++ 22 | } 23 | 24 | /** 25 | * Prepend a new node to the beginning of the list. 26 | * 27 | * @param value the value to be added to the list 28 | */ 29 | fun prepend(value: Int) { 30 | val newNode = Node(value) 31 | head?.previous = newNode 32 | newNode.next = head 33 | head = newNode 34 | length++ 35 | } 36 | 37 | /** 38 | * Print the values of the nodes in the list as an IntArray. 39 | * 40 | * @return an IntArray representing the values of the nodes in the list 41 | */ 42 | fun printList(): IntArray { 43 | val myList = IntArray(length) 44 | var current: Node? = head 45 | var i = 0 46 | while (current != null) { 47 | myList[i] = current.value 48 | current = current.next 49 | i++ 50 | } 51 | return myList 52 | } 53 | 54 | /** 55 | * Insert a new node at the specified index in the list. 56 | * 57 | * @param index the index at which to insert the new node 58 | * @param value the value to be inserted into the list 59 | */ 60 | fun insert(index: Int, value: Int) { 61 | if (index < 0 || index > length) { 62 | println("Index Out Of Bounds For Length $length") 63 | } else if (index == 0) { 64 | prepend(value) 65 | } else if (index == length) { 66 | append(value) 67 | } else { 68 | var current: Node? = head 69 | for (i in 0 until index - 1) { 70 | current = current?.next 71 | } 72 | val newNode = Node(value) 73 | newNode.next = current?.next 74 | current?.next = newNode 75 | newNode.previous = current 76 | newNode.next?.previous = newNode 77 | length++ 78 | } 79 | } 80 | 81 | /** 82 | * Remove the node at the specified index from the list. 83 | * 84 | * @param index the index of the node to be removed from the list 85 | */ 86 | fun remove(index: Int) { 87 | if (index < 0 || index > length) { 88 | println("Index Out Of Bounds For Length $length") 89 | } else if (index == 0) { 90 | head = head?.next 91 | head?.previous = null 92 | length-- 93 | } else { 94 | var current: Node? = head 95 | var i = 0 96 | while (i < index - 1) { 97 | current = current?.next 98 | i++ 99 | } 100 | current?.next = current?.next?.next 101 | length-- 102 | if (i == length - 1) { 103 | tail = current 104 | } else { 105 | current?.next?.previous = current 106 | } 107 | } 108 | } 109 | } 110 | 111 | fun main() { 112 | 113 | val myDoublyLinkedList = DoublyLinkedList(5) 114 | 115 | myDoublyLinkedList.append(3) 116 | myDoublyLinkedList.append(4) 117 | myDoublyLinkedList.prepend(2) 118 | myDoublyLinkedList.prepend(1) 119 | println(myDoublyLinkedList.printList().contentToString()) 120 | 121 | myDoublyLinkedList.remove(0) 122 | println(myDoublyLinkedList.printList().contentToString()) 123 | 124 | myDoublyLinkedList.insert(2, 200) 125 | println(myDoublyLinkedList.printList().contentToString()) 126 | 127 | println("length: " + myDoublyLinkedList.length) 128 | println("head value: " + myDoublyLinkedList.head?.value) 129 | println("head.previous: " + myDoublyLinkedList.head?.previous) 130 | println("tail value: " + myDoublyLinkedList.tail?.value) 131 | println("tail.next: " + myDoublyLinkedList.tail?.next) 132 | } -------------------------------------------------------------------------------- /src/data_structures/linked_lists/doubly_linked_list/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.linked_lists.doubly_linked_list 2 | 3 | class Node(var value: Int) { 4 | var next: Node? = null 5 | var previous: Node? = null 6 | } -------------------------------------------------------------------------------- /src/data_structures/linked_lists/singly_linked_list/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.linked_lists.singly_linked_list 2 | 3 | class Node(var value: Int) { 4 | var next: Node? = null 5 | } -------------------------------------------------------------------------------- /src/data_structures/linked_lists/singly_linked_list/SinglyLinkedList.kt: -------------------------------------------------------------------------------- 1 | package data_structures.linked_lists.singly_linked_list 2 | 3 | /** 4 | * Implementation of a singly linked list data structure with basic operations. 5 | */ 6 | class LinkedList(value: Int) { 7 | private var head: Node? = Node(value) 8 | private var tail: Node? = head 9 | var length: Int = 1 10 | 11 | /** 12 | * Appends a new node with the given value to the end of the list. 13 | * 14 | * @param value the integer value to add to the list 15 | */ 16 | fun append(value: Int) { 17 | val newNode = Node(value) 18 | tail?.next = newNode 19 | tail = newNode 20 | length++ 21 | } 22 | 23 | /** 24 | * Adds a new node with the given value to the beginning of the list. 25 | * 26 | * @param value the integer value to add to the list 27 | */ 28 | fun prepend(value: Int) { 29 | val newNode = Node(value) 30 | newNode.next = head 31 | head = newNode 32 | length++ 33 | } 34 | 35 | /** 36 | * Returns an array containing the values of all nodes in the list. 37 | * 38 | * @return an array containing the values of all nodes in the list 39 | */ 40 | fun printList(): IntArray { 41 | val myList = IntArray(length) 42 | var current: Node? = head 43 | var i = 0 44 | while (current != null) { 45 | myList[i] = current.value 46 | current = current.next 47 | i++ 48 | } 49 | return myList 50 | } 51 | 52 | /** 53 | * Inserts a new node with the given value at the specified index. 54 | * 55 | * @param index the index at which to insert the new node 56 | * @param value the integer value to add to the list 57 | */ 58 | fun insert(index: Int, value: Int) { 59 | if (index < 0 || index > length) { 60 | println("Index Out Of Bounds For Length $length") 61 | } else if (index == 0) { 62 | prepend(value) 63 | } else if (index == length) { 64 | append(value) 65 | } else { 66 | var current: Node? = head 67 | for (i in 0 until index - 1) { 68 | current = current?.next 69 | } 70 | val newNode = Node(value) 71 | newNode.next = current?.next 72 | current?.next = newNode 73 | length++ 74 | } 75 | } 76 | 77 | /** 78 | * Removes the node at the specified index from the list. 79 | * 80 | * @param index the index of the node to remove 81 | */ 82 | fun remove(index: Int) { 83 | if (index < 0 || index > length) { 84 | println("Index Out Of Bounds For Length $length") 85 | } else if (index == 0) { 86 | head = head?.next 87 | length-- 88 | } else { 89 | var current: Node? = head 90 | var i = 0 91 | while (i < index - 1) { 92 | current = current?.next 93 | i++ 94 | } 95 | current?.next = current?.next?.next 96 | length-- 97 | if (i == length - 1) { 98 | tail = current 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Reverses the order of nodes in the list. 105 | * 106 | * @param linkedList the list to be reversed 107 | * @return a new reversed LinkedList 108 | */ 109 | fun reverse(linkedList: LinkedList): LinkedList { 110 | val newList = LinkedList(linkedList.head!!.value) 111 | var current: Node? = linkedList.head 112 | while (current?.next != null) { 113 | current = current.next 114 | val newNode = Node(current!!.value) 115 | newNode.next = newList.head 116 | newList.head = newNode 117 | newList.length++ 118 | } 119 | return newList 120 | } 121 | } 122 | 123 | fun main() { 124 | 125 | val myLinkedList = LinkedList(10) 126 | 127 | myLinkedList.append(12) 128 | myLinkedList.append(16) 129 | 130 | myLinkedList.prepend(20) 131 | myLinkedList.prepend(50) 132 | println("Length: " + myLinkedList.length) 133 | println("List: " + myLinkedList.printList().contentToString()) 134 | 135 | myLinkedList.insert(2, 25) 136 | println("Length: " + myLinkedList.length) 137 | println("List: " + myLinkedList.printList().contentToString()) 138 | 139 | myLinkedList.remove(4) 140 | println("Length: " + myLinkedList.length) 141 | println("List: " + myLinkedList.printList().contentToString()) 142 | 143 | val linkedList2 = myLinkedList.reverse(myLinkedList) 144 | println("Reversed linked list: " + linkedList2.printList().contentToString()) 145 | } -------------------------------------------------------------------------------- /src/data_structures/queues/queue_using_linked_list/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.queues.queue_using_linked_list 2 | 3 | /** 4 | * Represents a node in the linked-list used for the Queue. 5 | * 6 | * @param value The value to be stored in the node. 7 | * @param next The reference to the next node in the linked-list. 8 | */ 9 | class Node(var value: String?, var next: Node? = null) -------------------------------------------------------------------------------- /src/data_structures/queues/queue_using_linked_list/Queue.kt: -------------------------------------------------------------------------------- 1 | package data_structures.queues.queue_using_linked_list 2 | 3 | /** 4 | * Simple implementation of a queue data structure using linked list. 5 | * 6 | * @property first Points to the first node in the queue. 7 | * @property last Points to the last node in the queue. 8 | * @property length Represents the number of elements currently in the queue. 9 | */ 10 | class Queue { 11 | var first: Node? = null 12 | var last: Node? = null 13 | var length = 0 14 | 15 | /** 16 | * Returns the value of the first element in the queue without removing it. 17 | * 18 | * @return The value of the first element if the queue is not empty, otherwise null. 19 | */ 20 | fun peek(): String? { 21 | return if (length > 0) { 22 | first?.value 23 | } else { 24 | null 25 | } 26 | } 27 | 28 | /** 29 | * Adds an element with the given value to the end of the queue. 30 | * 31 | * @param value The value to be added to the queue. 32 | */ 33 | fun enqueue(value: String?) { 34 | val newNode = Node(value) 35 | if (length == 0) { 36 | first = newNode 37 | } else { 38 | last?.next = newNode 39 | } 40 | last = newNode 41 | length++ 42 | } 43 | 44 | /** 45 | * Removes the first element from the queue if it's not empty. 46 | */ 47 | fun dequeue() { 48 | if (length > 0) { 49 | first = first?.next 50 | if (length == 1) { 51 | last = null 52 | } 53 | length-- 54 | } 55 | } 56 | 57 | /** 58 | * Checks if the queue is empty. 59 | * 60 | * @return True if the queue is empty, otherwise false. 61 | */ 62 | fun isEmpty() = length == 0 63 | } 64 | 65 | fun main() { 66 | 67 | val myQueue = Queue() 68 | println(myQueue.isEmpty()) 69 | println(myQueue.peek()) 70 | myQueue.enqueue("Apple") 71 | myQueue.enqueue("Ball") 72 | myQueue.enqueue("Cat") 73 | myQueue.dequeue() 74 | println(myQueue.peek()) 75 | } -------------------------------------------------------------------------------- /src/data_structures/queues/queue_using_stacks/Queue.kt: -------------------------------------------------------------------------------- 1 | package data_structures.queues.queue_using_stacks 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Simple implementation of a queue data structure using stacks. 7 | * 8 | * @property queueStack The main stack used to simulate the queue. 9 | * @property revStack A temporary stack used for reordering elements during the push operation. 10 | */ 11 | class Queue { 12 | var queueStack = Stack() 13 | 14 | private var revStack = Stack() 15 | /** 16 | * Adds an element with the value 'x' to the end of the queue. 17 | * 18 | * @param x The value to be added to the queue. 19 | */ 20 | fun push(x: Int) { 21 | if (queueStack.size == 0) { 22 | queueStack.push(x) 23 | } else { 24 | while (queueStack.size != 0) { 25 | revStack.push(queueStack.pop()) 26 | } 27 | revStack.push(x) 28 | while (revStack.size != 0) { 29 | queueStack.push(revStack.pop()) 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * Removes and returns the first element from the queue if it's not empty. 36 | * 37 | * @return The first element from the queue, or null if the queue is empty. 38 | */ 39 | fun pop(): Int? { 40 | return if (queueStack.size > 0) { 41 | queueStack.pop() 42 | } else { 43 | null 44 | } 45 | } 46 | 47 | /** 48 | * Returns the value of the first element in the queue without removing it. 49 | * 50 | * @return The value of the first element if the queue is not empty, otherwise null. 51 | */ 52 | fun peek(): Int? { 53 | return if (queueStack.size > 0) { 54 | queueStack[queueStack.size - 1] 55 | } else { 56 | null 57 | } 58 | } 59 | 60 | /** 61 | * Checks if the queue is empty. 62 | * 63 | * @return True if the queue is empty, otherwise false. 64 | */ 65 | fun empty() = queueStack.size == 0 66 | } 67 | 68 | fun main() { 69 | val myQueue = Queue() 70 | myQueue.push(10) 71 | myQueue.push(20) 72 | myQueue.push(30) 73 | println("queue: " + myQueue.queueStack) 74 | println("peek: " + myQueue.peek()) 75 | println("pop: " + myQueue.pop()) 76 | println("queue: " + myQueue.queueStack) 77 | println("peek: " + myQueue.peek()) 78 | println("is queue empty: " + myQueue.empty()) 79 | } -------------------------------------------------------------------------------- /src/data_structures/stacks/stack_using_array/Stack.kt: -------------------------------------------------------------------------------- 1 | package data_structures.stacks.stack_using_array 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Simple implementation of a stack data structure using array. 7 | * 8 | * @property stackArray An ArrayList used to store the elements of the stack. 9 | */ 10 | class Stack { 11 | private var stackArray: ArrayList = ArrayList() 12 | 13 | /** 14 | * Returns the value of the top element in the stack without removing it. 15 | * 16 | * @return The value of the top element if the stack is not empty, otherwise null. 17 | */ 18 | fun peek(): String? { 19 | return if (stackArray.size > 0) { 20 | stackArray[stackArray.size - 1] 21 | } else { 22 | null 23 | } 24 | } 25 | 26 | /** 27 | * Adds an element with the given value to the top of the stack. 28 | * 29 | * @param value The value to be added to the stack. 30 | */ 31 | fun push(value: String) { 32 | stackArray.add(value) 33 | } 34 | 35 | /** 36 | * Removes the top element from the stack if it's not empty. 37 | */ 38 | fun pop() { 39 | if (stackArray.size > 0) { 40 | stackArray.removeAt(stackArray.size - 1) 41 | } 42 | } 43 | 44 | /** 45 | * Checks if the stack is empty. 46 | * 47 | * @return True if the stack is empty, otherwise false. 48 | */ 49 | fun isEmpty() = stackArray.size == 0 50 | } 51 | 52 | fun main() { 53 | 54 | val myStack = Stack() 55 | myStack.push("Google") 56 | myStack.push("Udemy") 57 | myStack.push("Discord") 58 | println(myStack.peek()) 59 | myStack.pop() 60 | println(myStack.peek()) 61 | println(myStack.isEmpty()) 62 | } -------------------------------------------------------------------------------- /src/data_structures/stacks/stack_using_linked_list/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.stacks.stack_using_linked_list 2 | 3 | /** 4 | * Represents a node in the linked-list used for the Stack. 5 | * 6 | * @param value The value to be stored in the node. 7 | * @param next The reference to the next node in the linked-list. 8 | */ 9 | class Node(val value: String?, var next: Node? = null) -------------------------------------------------------------------------------- /src/data_structures/stacks/stack_using_linked_list/Stack.kt: -------------------------------------------------------------------------------- 1 | package data_structures.stacks.stack_using_linked_list 2 | 3 | /** 4 | * Simple implementation of a stack data structure using linked list. 5 | * 6 | * @property top Points to the top node in the stack. 7 | * @property bottom Points to the bottom node in the stack. 8 | * @property length Represents the number of elements currently in the stack. 9 | */ 10 | class Stack { 11 | private var top: Node? = null 12 | private var bottom: Node? = null 13 | private var length = 0 14 | 15 | /** 16 | * Returns the value of the top element in the stack without removing it. 17 | * 18 | * @return The value of the top element if the stack is not empty, otherwise null. 19 | */ 20 | fun peek(): String? { 21 | return if (length > 0) { 22 | top?.value 23 | } else { 24 | null 25 | } 26 | } 27 | 28 | /** 29 | * Adds an element with the given value to the top of the stack. 30 | * 31 | * @param value The value to be added to the stack. 32 | */ 33 | fun push(value: String?) { 34 | val newNode = Node(value) 35 | if (length == 0) { 36 | top = newNode 37 | bottom = newNode 38 | } else { 39 | newNode.next = top 40 | top = newNode 41 | } 42 | length++ 43 | } 44 | 45 | /** 46 | * Removes the top element from the stack if it's not empty. 47 | */ 48 | fun pop() { 49 | if (length > 0) { 50 | top = top?.next 51 | if (length == 1) { 52 | bottom = null 53 | } 54 | length-- 55 | } 56 | } 57 | 58 | /** 59 | * Checks if the stack is empty. 60 | * 61 | * @return True if the stack is empty, otherwise false. 62 | */ 63 | fun isEmpty() = length == 0 64 | 65 | /** 66 | * Returns the value of the last element in the stack without removing it. 67 | * 68 | * @return The value of the last element if the stack is not empty, otherwise null. 69 | */ 70 | val lastElement: String? 71 | get() = if (length > 0) { 72 | bottom?.value 73 | } else null 74 | } 75 | 76 | fun main() { 77 | 78 | val myStack = Stack() 79 | myStack.push("Google") 80 | myStack.push("Udemy") 81 | myStack.push("Discord") 82 | println(myStack.peek()) 83 | myStack.pop() 84 | println(myStack.isEmpty()) 85 | println(myStack.lastElement) 86 | } -------------------------------------------------------------------------------- /src/data_structures/trees/binary_search_tree/BinarySearchTree.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.binary_search_tree 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Simple implementation of a Binary Search Tree (BST) data structure. 7 | * 8 | * @property root Points to the root node of the binary search tree. 9 | */ 10 | class BinarySearchTree { 11 | var root: Node? = null 12 | 13 | /** 14 | * Inserts a new node with the given value into the binary search tree. 15 | * 16 | * @param value The value to be inserted into the binary search tree. 17 | */ 18 | fun insert(value: Int) { 19 | val newNode = Node(value) 20 | if (this.root == null) { 21 | // If the tree is empty, set the new node as the root. 22 | this.root = newNode 23 | } else { 24 | var current = this.root 25 | while (true) { 26 | // Traverse to the right subtree if the current node value is less than the new value. 27 | if (current!!.value < value) { 28 | if (current.right != null) { 29 | current = current.right 30 | } else { 31 | current.right = newNode 32 | break 33 | } 34 | } else { 35 | // Traverse to the left subtree if the current node value is greater than or equal to the new value. 36 | if (current.left != null) { 37 | current = current.left 38 | } else { 39 | current.left = newNode 40 | break 41 | } 42 | } 43 | } 44 | } 45 | } 46 | 47 | /** 48 | * Searches for a node with the given value in the binary search tree and returns true if found, otherwise false. 49 | * 50 | * @param value The value to be searched in the binary search tree. 51 | * @return True if the value is found, otherwise false. 52 | */ 53 | fun lookup(value: Int): Boolean { 54 | var current = this.root 55 | while (current != null) { 56 | current = when { 57 | current.value > value -> { 58 | current.left 59 | } 60 | current.value < value -> { 61 | current.right 62 | } 63 | else -> { 64 | return true 65 | } 66 | } 67 | } 68 | return false 69 | } 70 | 71 | /** 72 | * Removes a node with the given value from the binary search tree. 73 | * 74 | * @param value The value of the node to be removed. 75 | */ 76 | fun remove(value: Int) { 77 | if (this.root == null) { 78 | return 79 | } 80 | var current = this.root 81 | var parentNode: Node? = null 82 | 83 | // Traverse the tree to find the node to be removed. 84 | while (current != null) { 85 | when { 86 | value < current.value -> { 87 | // Go left if the value is smaller. 88 | parentNode = current 89 | current = current.left 90 | } 91 | value > current.value -> { 92 | // Go right if the value is greater. 93 | parentNode = current 94 | current = current.right 95 | } 96 | else -> { 97 | // Node to be removed is found. 98 | 99 | // Option 1: No right child. 100 | if (current.right == null) { 101 | // If parentNode is null, the root node is to be removed. 102 | if (parentNode == null) { 103 | this.root = current.left 104 | } else { 105 | // Make the left child of the current node a child of its parent. 106 | if (current.value < parentNode.value) { 107 | parentNode.left = current.left 108 | } else if (current.value > parentNode.value) { 109 | parentNode.right = current.left 110 | } 111 | } 112 | } else if (current.right!!.left == null) { 113 | // Option 2: Right child has no left child. 114 | if (parentNode == null) { 115 | this.root = current.right 116 | } else { 117 | // Make the right child of the current node a child of its parent. 118 | if (current.value < parentNode.value) { 119 | parentNode.left = current.right 120 | } else if (current.value > parentNode.value) { 121 | parentNode.right = current.right 122 | } 123 | } 124 | } else { 125 | // Option 3: Right child has left child. 126 | if (parentNode == null) { 127 | // Save references to the left and right nodes of the root. 128 | val leftNode = this.root!!.left 129 | val rightNode = this.root!!.right 130 | 131 | // Make the leftmost node of the right subtree the new root. 132 | this.root = current.right!!.left 133 | 134 | // Set the saved references of left and right nodes of the root. 135 | rightNode!!.left = rightNode.left!!.right 136 | this.root!!.left = leftNode 137 | this.root!!.right = rightNode 138 | } else { 139 | // Make the leftmost node of the right subtree a child of its parent. 140 | if (current.value < parentNode.value) { 141 | parentNode.left = current.right!!.left 142 | } else if (current.value > parentNode.value) { 143 | parentNode.right = current.right!!.left 144 | } 145 | } 146 | } 147 | return 148 | } 149 | } 150 | } 151 | } 152 | 153 | /** 154 | * Performs breadth-first search on the binary search tree and returns the values of the nodes in an ArrayList. 155 | * 156 | * @return An ArrayList containing the values of the nodes in breadth-first order. 157 | */ 158 | fun breadthFirstSearch(): ArrayList { 159 | var currentNode = this.root 160 | val resultArray = ArrayList() 161 | val queue = ArrayList() 162 | queue.add(currentNode) 163 | 164 | // Traverse the tree in breadth-first order using a queue. 165 | while (queue.size > 0) { 166 | currentNode = queue.removeAt(0) 167 | resultArray.add(currentNode!!.value) 168 | if (currentNode.left != null) { 169 | queue.add(currentNode.left) 170 | } 171 | if (currentNode.right != null) { 172 | queue.add(currentNode.right) 173 | } 174 | } 175 | return resultArray 176 | } 177 | 178 | /** 179 | * Helper function for recursive breadth-first search. 180 | * 181 | * @param queue The queue used for recursive breadth-first search. 182 | * @param resultArray The ArrayList to store the values of the nodes in breadth-first order. 183 | * @return An ArrayList containing the values of the nodes in breadth-first order. 184 | */ 185 | fun breadthFirstSearchRecursive( 186 | queue: ArrayList, 187 | resultArray: ArrayList 188 | ): ArrayList { 189 | // Base case: If the queue is empty, return the resultArray. 190 | if (queue.size == 0) { 191 | return resultArray 192 | } 193 | 194 | // Remove the first node from the queue and process it. 195 | val currentNode = queue.removeAt(0) 196 | resultArray.add(currentNode!!.value) 197 | 198 | // Add the left and right children of the current node to the queue if they exist. 199 | if (currentNode.left != null) { 200 | queue.add(currentNode.left) 201 | } 202 | if (currentNode.right != null) { 203 | queue.add(currentNode.right) 204 | } 205 | 206 | // Continue the recursive call with the updated queue and resultArray. 207 | return breadthFirstSearchRecursive(queue, resultArray) 208 | } 209 | 210 | /** 211 | * Performs depth-first search (in-order) on the binary search tree and returns the values of the nodes in an ArrayList. 212 | * 213 | * @return An ArrayList containing the values of the nodes in in-order depth-first search order. 214 | */ 215 | fun DFSInOrder(): ArrayList { 216 | val answer = ArrayList() 217 | return traverseInOrder(this.root, answer) 218 | } 219 | 220 | /** 221 | * Performs depth-first search (pre-order) on the binary search tree and returns the values of the nodes in an ArrayList. 222 | * 223 | * @return An ArrayList containing the values of the nodes in pre-order depth-first search order. 224 | */ 225 | fun DFSPreOrder(): ArrayList { 226 | val answer = ArrayList() 227 | return traversePreOrder(this.root, answer) 228 | } 229 | 230 | /** 231 | * Performs depth-first search (post-order) on the binary search tree and returns the values of the nodes in an ArrayList. 232 | * 233 | * @return An ArrayList containing the values of the nodes in post-order depth-first search order. 234 | */ 235 | fun DFSPostOrder(): ArrayList { 236 | val answer = ArrayList() 237 | return traversePostOrder(this.root, answer) 238 | } 239 | 240 | /** 241 | * Helper function for in-order traversal during depth-first search. 242 | * 243 | * @param node The current node being processed. 244 | * @param array The ArrayList to store the values of the nodes during in-order traversal. 245 | * @return An ArrayList containing the values of the nodes in in-order depth-first search order. 246 | */ 247 | private fun traverseInOrder(node: Node?, array: ArrayList): ArrayList { 248 | if (node?.left != null) { 249 | traverseInOrder(node.left, array) 250 | } 251 | array.add(node?.value) 252 | if (node?.right != null) { 253 | traverseInOrder(node.right, array) 254 | } 255 | return array 256 | } 257 | 258 | /** 259 | * Helper function for pre-order traversal during depth-first search. 260 | * 261 | * @param node The current node being processed. 262 | * @param array The ArrayList to store the values of the nodes during pre-order traversal. 263 | * @return An ArrayList containing the values of the nodes in pre-order depth-first search order. 264 | */ 265 | private fun traversePreOrder(node: Node?, array: ArrayList): ArrayList { 266 | array.add(node?.value) 267 | if (node?.left != null) { 268 | traversePreOrder(node.left, array) 269 | } 270 | if (node?.right != null) { 271 | traversePreOrder(node.right, array) 272 | } 273 | return array 274 | } 275 | 276 | /** 277 | * Helper function for post-order traversal during depth-first search. 278 | * 279 | * @param node The current node being processed. 280 | * @param array The ArrayList to store the values of the nodes during post-order traversal. 281 | * @return An ArrayList containing the values of the nodes in post-order depth-first search order. 282 | */ 283 | private fun traversePostOrder(node: Node?, array: ArrayList): ArrayList { 284 | if (node?.left != null) { 285 | traversePostOrder(node.left, array) 286 | } 287 | if (node?.right != null) { 288 | traversePostOrder(node.right, array) 289 | } 290 | array.add(node?.value) 291 | return array 292 | } 293 | } 294 | 295 | fun main() { 296 | val bst = BinarySearchTree() 297 | bst.insert(9) 298 | bst.insert(4) 299 | bst.insert(6) 300 | bst.insert(20) 301 | bst.insert(170) 302 | bst.insert(15) 303 | bst.insert(1) 304 | println("bfs: " + bst.breadthFirstSearch()) 305 | println("look for 20: " + bst.lookup(20)) 306 | val queue = ArrayList() 307 | queue.add(bst.root) 308 | println("bfs recursive: " + bst.breadthFirstSearchRecursive(queue, ArrayList())) 309 | println("dfs inOrder: " + bst.DFSInOrder()) 310 | println("dfs preOrder: " + bst.DFSPreOrder()) 311 | println("dfs postOrder: " + bst.DFSPostOrder()) 312 | bst.remove(20) 313 | println("look for 20 after removing 20: " + bst.lookup(20)) 314 | } -------------------------------------------------------------------------------- /src/data_structures/trees/binary_search_tree/BinarySearchTreeNoComments.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.binary_search_tree 2 | 3 | import java.util.ArrayList 4 | 5 | class BinarySearchTreeNoComments { 6 | var root: Node? = null 7 | 8 | fun insert(value: Int) { 9 | val newNode = Node(value) 10 | if (this.root == null) { 11 | this.root = newNode 12 | } else { 13 | var current = this.root 14 | while (true) { 15 | if (current!!.value < value) { 16 | if (current.right != null) { 17 | current = current.right 18 | } else { 19 | current.right = newNode 20 | break 21 | } 22 | } else { 23 | if (current.left != null) { 24 | current = current.left 25 | } else { 26 | current.left = newNode 27 | break 28 | } 29 | } 30 | } 31 | } 32 | } 33 | 34 | fun lookup(value: Int): Boolean { 35 | var current = this.root 36 | while (current != null) { 37 | current = when { 38 | current.value > value -> { 39 | current.left 40 | } 41 | current.value < value -> { 42 | current.right 43 | } 44 | else -> { 45 | return true 46 | } 47 | } 48 | } 49 | return false 50 | } 51 | 52 | fun remove(value: Int) { 53 | if (this.root == null) { 54 | return 55 | } 56 | var current = this.root 57 | var parentNode: Node? = null 58 | 59 | while (current != null) { 60 | when { 61 | value < current.value -> { 62 | parentNode = current 63 | current = current.left 64 | } 65 | value > current.value -> { 66 | parentNode = current 67 | current = current.right 68 | } 69 | else -> { 70 | if (current.right == null) { 71 | if (parentNode == null) { 72 | this.root = current.left 73 | } else { 74 | if (current.value < parentNode.value) { 75 | parentNode.left = current.left 76 | } else if (current.value > parentNode.value) { 77 | parentNode.right = current.left 78 | } 79 | } 80 | } else if (current.right!!.left == null) { 81 | if (parentNode == null) { 82 | this.root = current.right 83 | } else { 84 | if (current.value < parentNode.value) { 85 | parentNode.left = current.right 86 | } else if (current.value > parentNode.value) { 87 | parentNode.right = current.right 88 | } 89 | } 90 | } else { 91 | if (parentNode == null) { 92 | val leftNode = this.root!!.left 93 | val rightNode = this.root!!.right 94 | 95 | this.root = current.right!!.left 96 | 97 | rightNode!!.left = rightNode.left!!.right 98 | this.root!!.left = leftNode 99 | this.root!!.right = rightNode 100 | } else { 101 | if (current.value < parentNode.value) { 102 | parentNode.left = current.right!!.left 103 | } else if (current.value > parentNode.value) { 104 | parentNode.right = current.right!!.left 105 | } 106 | } 107 | } 108 | return 109 | } 110 | } 111 | } 112 | } 113 | 114 | fun breadthFirstSearch(): ArrayList { 115 | var currentNode = this.root 116 | val resultArray = ArrayList() 117 | val queue = ArrayList() 118 | queue.add(currentNode) 119 | 120 | while (queue.size > 0) { 121 | currentNode = queue.removeAt(0) 122 | resultArray.add(currentNode!!.value) 123 | if (currentNode.left != null) { 124 | queue.add(currentNode.left) 125 | } 126 | if (currentNode.right != null) { 127 | queue.add(currentNode.right) 128 | } 129 | } 130 | return resultArray 131 | } 132 | 133 | fun breadthFirstSearchRecursive( 134 | queue: ArrayList, 135 | resultArray: ArrayList 136 | ): ArrayList { 137 | if (queue.size == 0) { 138 | return resultArray 139 | } 140 | 141 | val currentNode = queue.removeAt(0) 142 | resultArray.add(currentNode!!.value) 143 | 144 | if (currentNode.left != null) { 145 | queue.add(currentNode.left) 146 | } 147 | if (currentNode.right != null) { 148 | queue.add(currentNode.right) 149 | } 150 | 151 | return breadthFirstSearchRecursive(queue, resultArray) 152 | } 153 | 154 | fun DFSInOrder(): ArrayList { 155 | val answer = ArrayList() 156 | return traverseInOrder(this.root, answer) 157 | } 158 | 159 | fun DFSPreOrder(): ArrayList { 160 | val answer = ArrayList() 161 | return traversePreOrder(this.root, answer) 162 | } 163 | 164 | fun DFSPostOrder(): ArrayList { 165 | val answer = ArrayList() 166 | return traversePostOrder(this.root, answer) 167 | } 168 | 169 | private fun traverseInOrder(node: Node?, array: ArrayList): ArrayList { 170 | if (node?.left != null) { 171 | traverseInOrder(node.left, array) 172 | } 173 | array.add(node?.value) 174 | if (node?.right != null) { 175 | traverseInOrder(node.right, array) 176 | } 177 | return array 178 | } 179 | 180 | private fun traversePreOrder(node: Node?, array: ArrayList): ArrayList { 181 | array.add(node?.value) 182 | if (node?.left != null) { 183 | traversePreOrder(node.left, array) 184 | } 185 | if (node?.right != null) { 186 | traversePreOrder(node.right, array) 187 | } 188 | return array 189 | } 190 | 191 | private fun traversePostOrder(node: Node?, array: ArrayList): ArrayList { 192 | if (node?.left != null) { 193 | traversePostOrder(node.left, array) 194 | } 195 | if (node?.right != null) { 196 | traversePostOrder(node.right, array) 197 | } 198 | array.add(node?.value) 199 | return array 200 | } 201 | } 202 | 203 | fun main() { 204 | val bst = BinarySearchTreeNoComments() 205 | bst.insert(9) 206 | bst.insert(4) 207 | bst.insert(6) 208 | bst.insert(20) 209 | bst.insert(170) 210 | bst.insert(15) 211 | bst.insert(1) 212 | println("bfs: " + bst.breadthFirstSearch()) 213 | println("look for 20: " + bst.lookup(20)) 214 | val queue = ArrayList() 215 | queue.add(bst.root) 216 | println("bfs recursive: " + bst.breadthFirstSearchRecursive(queue, ArrayList())) 217 | println("dfs inOrder: " + bst.DFSInOrder()) 218 | println("dfs preOrder: " + bst.DFSPreOrder()) 219 | println("dfs postOrder: " + bst.DFSPostOrder()) 220 | bst.remove(20) 221 | println("look for 20 after removing 20: " + bst.lookup(20)) 222 | } -------------------------------------------------------------------------------- /src/data_structures/trees/binary_search_tree/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.binary_search_tree 2 | 3 | /** 4 | * Represents a node in the binary search tree. 5 | * 6 | * @param value The value to be stored in the node. 7 | * @param left The reference to the left child node. 8 | * @param right The reference to the right child node. 9 | */ 10 | class Node(val value: Int, var left: Node? = null, var right: Node? = null) -------------------------------------------------------------------------------- /src/data_structures/trees/priority_queue/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.priority_queue 2 | 3 | /** 4 | * Represents a node in the priority queue. 5 | * 6 | * @param value The value to be stored in the node. 7 | * @param priority The priority of the node. 8 | */ 9 | class Node(val value: String, val priority: Int) -------------------------------------------------------------------------------- /src/data_structures/trees/priority_queue/PriorityQueue.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.priority_queue 2 | 3 | /** 4 | * Simple implementation of a priority queue data structure. 5 | * 6 | * @property queueArray ArrayList that holds the nodes in the priority queue. 7 | */ 8 | class PriorityQueue { 9 | private val queueArray = ArrayList() 10 | 11 | /** 12 | * Adds a new node with the given value and priority to the priority queue. 13 | * 14 | * @param value The value to be added to the priority queue. 15 | * @param priority The priority of the value. Higher values represent higher priority. 16 | */ 17 | fun enqueue(value: String?, priority: Int) { 18 | val newNode = Node(value!!, priority) 19 | var contain = false 20 | for (i in queueArray.indices) { 21 | if (queueArray[i].priority > newNode.priority) { 22 | queueArray.add(i, newNode) 23 | contain = true 24 | break 25 | } 26 | } 27 | if (!contain) { 28 | queueArray.add(newNode) 29 | } 30 | } 31 | 32 | /** 33 | * Removes and returns the value of the node with the highest priority from the priority queue. 34 | * 35 | * @return The value of the node with the highest priority, or "empty array" if the queue is empty. 36 | */ 37 | fun dequeue(): String { 38 | if (queueArray.isEmpty()) { 39 | return "empty array" 40 | } 41 | val toRemove = queueArray[0] 42 | queueArray.removeAt(0) 43 | return toRemove.value 44 | } 45 | 46 | /** 47 | * Returns the node with the highest priority in the priority queue without removing it. 48 | * 49 | * @return The node with the highest priority, or null if the queue is empty. 50 | */ 51 | fun front(): Node? { 52 | return if (queueArray.isNotEmpty()) { 53 | queueArray[0] 54 | } else null 55 | } 56 | 57 | /** 58 | * Returns the node with the lowest priority in the priority queue without removing it. 59 | * 60 | * @return The node with the lowest priority, or null if the queue is empty. 61 | */ 62 | fun rear(): Node? { 63 | return if (queueArray.isNotEmpty()) { 64 | queueArray[queueArray.size - 1] 65 | } else null 66 | } 67 | 68 | /** 69 | * Property that indicates whether the priority queue is empty or not. 70 | */ 71 | val isEmpty: Boolean 72 | get() = queueArray.isEmpty() 73 | 74 | /** 75 | * Returns an array containing the values of all nodes in the priority queue. 76 | * 77 | * @return An array of String? containing the values of all nodes in the priority queue. 78 | */ 79 | fun printQueue(): Array { 80 | val array = arrayOfNulls(queueArray.size) 81 | for (i in array.indices) { 82 | array[i] = queueArray[i].value 83 | } 84 | return array 85 | } 86 | } 87 | 88 | fun main() { 89 | 90 | val queue = PriorityQueue() 91 | queue.enqueue("piyush", 3) 92 | queue.enqueue("rohan", 1) 93 | queue.enqueue("jeet", 2) 94 | queue.enqueue("john", 5) 95 | queue.enqueue("Dave", 4) 96 | println(queue.printQueue().contentToString()) 97 | println(queue.dequeue()) 98 | println(queue.printQueue().contentToString()) 99 | println(queue.front()?.value) 100 | println(queue.rear()?.value) 101 | println(queue.isEmpty) 102 | } -------------------------------------------------------------------------------- /src/data_structures/trees/trie/Node.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.trie 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Represents a node in the trie. 7 | * 8 | * @param children Children of the current node. 9 | * @param isWord Indicates if the node represents the end of a complete word. 10 | */ 11 | class Node(var children: HashMap = HashMap(), var isWord: Boolean = false) -------------------------------------------------------------------------------- /src/data_structures/trees/trie/Trie.kt: -------------------------------------------------------------------------------- 1 | package data_structures.trees.trie 2 | 3 | /** 4 | * Simple implementation of a trie queue data structure. 5 | * 6 | * @property root The root node of the trie. 7 | */ 8 | class Trie { 9 | val root = Node() 10 | 11 | /** 12 | * Inserts a string into the trie. 13 | * 14 | * @param string The string to be inserted into the trie. 15 | */ 16 | fun insert(string: String) { 17 | var current: Node? = root 18 | for (ch in string.toCharArray()) { 19 | current!!.children.putIfAbsent(ch, Node()) 20 | current = current.children[ch] 21 | } 22 | current!!.isWord = true 23 | } 24 | 25 | /** 26 | * Searches for a complete word in the trie and returns true if found, otherwise false. 27 | * 28 | * @param string The word to be searched in the trie. 29 | * @return True if the complete word is found, otherwise false. 30 | */ 31 | fun findWord(string: String): Boolean { 32 | var current: Node? = root 33 | for (ch in string.toCharArray()) { 34 | current = if (current!!.children.containsKey(ch)) { 35 | current.children[ch] 36 | } else { 37 | return false 38 | } 39 | } 40 | return current!!.isWord 41 | } 42 | 43 | /** 44 | * Deletes a word from the trie and returns true if the word was successfully deleted, otherwise false. 45 | * 46 | * @param word The word to be deleted from the trie. 47 | * @return True if the word was successfully deleted, otherwise false. 48 | */ 49 | fun delete(word: String): Boolean { 50 | var current: Node? = root 51 | var deleteAfter: Node? = root 52 | var ch1 = word[0] 53 | for (i in word.indices) { 54 | val ch = word[i] 55 | if (current!!.children.containsKey(ch)) { 56 | current = current.children[ch] 57 | if (current!!.children.size > 1) { 58 | deleteAfter = current 59 | ch1 = word[i + 1] 60 | } 61 | } else { 62 | return false 63 | } 64 | } 65 | if (current!!.children.isEmpty()) { 66 | deleteAfter!!.children.remove(ch1) 67 | return true 68 | } 69 | return false 70 | } 71 | } 72 | 73 | fun main() { 74 | val trie = Trie() 75 | trie.insert("heating") 76 | trie.insert("heat") 77 | println(trie.root.children['h']!!.children['e']?.children) 78 | println(trie.delete("heat")) 79 | println(trie.findWord("heat")) 80 | println(trie.root.children['h']!!.children['e']?.children) 81 | } -------------------------------------------------------------------------------- /src/dynamic_programming/README.md: -------------------------------------------------------------------------------- 1 | ### Dynamic programming overview 2 | 3 | - Dynamic fibonacci 4 | - Dynamic fibonacci (sample) 5 | - Memoization 6 | - Memoization (sample) 7 | 8 | ### Dynamic programming definition 9 | 10 | Dynamic programming is a technique for solving complex problems by breaking them down into smaller, simpler subproblems and storing the solutions to those subproblems in a table. By solving these subproblems once and reusing their solutions, dynamic programming can dramatically reduce the amount of computation required to solve the original problem. -------------------------------------------------------------------------------- /src/dynamic_programming/dynamic_fibonacci/DynamicFibonacci.kt: -------------------------------------------------------------------------------- 1 | package dynamic_programming.dynamic_fibonacci 2 | 3 | import dynamic_programming.dynamic_fibonacci.DynamicFibonacci.Companion.operationsCount 4 | import java.util.* 5 | 6 | 7 | /** 8 | * A class to calculate Fibonacci numbers using dynamic programming with memoization. 9 | */ 10 | class DynamicFibonacci { 11 | 12 | /** 13 | * Calculates the nth Fibonacci number using memoization to avoid redundant calculations. 14 | * 15 | * @param n The index of the Fibonacci number to calculate. 16 | * @return The nth Fibonacci number. 17 | */ 18 | fun fibonacciMaster(n: Int): Int { 19 | // Check if the value for 'n' is already available in the cache. 20 | return if (cache.containsKey(n)) { 21 | // If it's in the cache, return the cached value. 22 | cache[n]!! 23 | } else { 24 | // If not in the cache, calculate the Fibonacci number and add it to the cache. 25 | if (n < 2) { 26 | n 27 | } else { 28 | operationsCount++ 29 | cache[n] = fibonacciMaster(n - 1) + fibonacciMaster(n - 2) 30 | cache[n]!! 31 | } 32 | } 33 | } 34 | 35 | companion object { 36 | // HashMap to store calculated Fibonacci numbers (memoization cache). 37 | var cache = HashMap() 38 | // Counter to keep track of the number of recursive calls made during calculations. 39 | var operationsCount = 0 40 | } 41 | } 42 | 43 | fun main() { 44 | 45 | val fib = DynamicFibonacci() 46 | println(fib.fibonacciMaster(30)) 47 | println(operationsCount) 48 | } -------------------------------------------------------------------------------- /src/dynamic_programming/memoization/Memoization.kt: -------------------------------------------------------------------------------- 1 | package dynamic_programming.memoization 2 | 3 | import java.util.* 4 | 5 | /** 6 | * A class demonstrating the concept of memoization in Kotlin. 7 | */ 8 | class Memoization { 9 | 10 | /** 11 | * Calculates the sum of the given integer 'n' and 80 directly without memoization. 12 | * 13 | * @param n The integer to add to 80. 14 | * @return The sum of 'n' and 80. 15 | */ 16 | fun addTo80(n: Int): Int { 17 | println("long time") 18 | return n + 80 19 | } 20 | 21 | /** 22 | * Calculates the sum of the given integer 'n' and 80 using memoization to avoid redundant calculations. 23 | * 24 | * @param n The integer to add to 80. 25 | * @return The sum of 'n' and 80, retrieved from the cache if previously calculated. 26 | */ 27 | fun memoizeAddTo80(n: Int): Int { 28 | if (!cache.containsKey(n)) { 29 | println("long time") 30 | cache[n] = n + 80 31 | } 32 | return cache[n]!! 33 | } 34 | 35 | companion object { 36 | // HashMap to store previously calculated results for memoization (cache). 37 | var cache = HashMap() 38 | } 39 | } 40 | 41 | fun main() { 42 | 43 | val memo = Memoization() 44 | println("addTo80: " + memo.addTo80(5)) 45 | println("addTo80: " + memo.addTo80(5)) 46 | println("memoizeAddTo80: " + memo.memoizeAddTo80(5)) 47 | println("memoizeAddTo80: " + memo.memoizeAddTo80(5)) 48 | println("memoizeAddTo80: " + memo.memoizeAddTo80(5)) 49 | } --------------------------------------------------------------------------------