├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── algorithms ├── graph │ └── search │ │ ├── README.md │ │ ├── breadth_first_search.go │ │ └── breadth_first_search_test.go ├── recursion │ ├── palindromes.go │ ├── palindromes_test.go │ ├── powers.go │ ├── powers_test.go │ ├── towers_of_hanoi.go │ └── towers_of_hanoi_test.go ├── searching │ ├── binary │ │ ├── README.md │ │ ├── binary.go │ │ └── binary_test.go │ └── linear │ │ ├── README.md │ │ ├── linear.go │ │ └── linear_test.go ├── sorting │ ├── bubble │ │ ├── README.md │ │ ├── bubble.go │ │ └── bubble_test.go │ ├── insertion │ │ ├── README.md │ │ ├── insertion.go │ │ └── insertion_test.go │ ├── merge │ │ ├── README.md │ │ ├── merge.go │ │ └── merge_test.go │ ├── quick │ │ ├── README.md │ │ ├── quick.go │ │ └── quick_test.go │ ├── selection │ │ ├── README.md │ │ ├── selection.go │ │ └── selection_test.go │ └── shell │ │ ├── README.md │ │ ├── shell.go │ │ └── shell_test.go └── string-matching │ └── knuth-morris-pratt │ ├── README.md │ ├── kmp.go │ └── kmp_test.go ├── cryptography ├── README.md └── symmetric-key │ └── stream-ciphers │ └── rc4 │ ├── README.md │ ├── rc4.go │ └── rc4_test.go ├── data-structures ├── binary-search-tree │ ├── README.md │ ├── bst.go │ ├── bst_test.go │ └── traverse.go ├── deque │ ├── README.md │ ├── deque.go │ └── deque_test.go ├── graph │ ├── README.md │ ├── graph.go │ └── graph_test.go ├── hash-table │ ├── README.md │ ├── hash_table.go │ ├── hash_table_test.go │ └── list.go ├── heap │ ├── README.md │ ├── heap.go │ ├── heap_test.go │ ├── max_heap.go │ └── min_heap.go ├── linked-list │ ├── README.md │ ├── linkedlist.go │ └── linkedlist_test.go ├── priority-queue │ ├── README.md │ ├── pq.go │ └── pq_test.go ├── queue │ ├── README.md │ ├── queue.go │ └── queue_test.go ├── red-black-tree │ ├── README.md │ ├── images │ │ ├── Red-black_tree_delete_case_2.svg │ │ ├── Red-black_tree_delete_case_3.svg │ │ ├── Red-black_tree_delete_case_4.svg │ │ ├── Red-black_tree_delete_case_5.svg │ │ ├── Red-black_tree_delete_case_6.svg │ │ ├── Red-black_tree_insert_case_3.svg │ │ ├── Red-black_tree_insert_case_4.svg │ │ └── Red-black_tree_insert_case_5.svg │ ├── rbt.go │ ├── rbt_test.go │ └── traverse.go ├── set │ ├── README.md │ ├── set.go │ └── set_test.go ├── stack │ ├── README.md │ ├── stack.go │ └── stack_test.go └── suffix-array │ ├── README.md │ ├── suffix_array.go │ └── suffix_array_test.go ├── design-patterns ├── adapter │ ├── README.md │ ├── adapter.go │ └── adapter_test.go ├── decorator │ ├── README.md │ ├── decorator.go │ └── decorator_test.go ├── factory │ ├── README.md │ ├── factory.go │ └── factory_test.go ├── iterator │ ├── README.md │ ├── iterator.go │ └── iterator_test.go ├── mvc │ ├── README.md │ ├── mvc.go │ └── mvc_test.go ├── observer │ ├── README.md │ ├── observer.go │ └── observer_test.go └── singleton │ ├── README.md │ ├── singleton.go │ └── singleton_test.go ├── interview-questions └── strings │ ├── is_substring.go │ ├── is_substring_test.go │ ├── run_length_encoding.go │ └── run_length_encoding_test.go └── notes ├── asymptotic_notation.md ├── bit_manipulation.md └── powers_of_two.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # GOPATH SRC 27 | src/* 28 | bin/ 29 | 30 | # Personal List 31 | TODO.md 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for showing interest in this project! As it currently stands, I will 4 | not be accepting pull requests for items on my list that have not been 5 | completed. I say this because for me, I'm working on learning these concepts 6 | and practice of implementation is important to me. Instead of submitting my 7 | own pull requests on repositories like this, I decided to reinvent the wheel 8 | and do things as I learn them to get a better grasp of how each of these items 9 | works. 10 | 11 | I will accept pull requests for modifications and especially performance-based 12 | gains. The only thing that I ask is that in your pull request, please explain 13 | why you are submitting the change. If it's something that I haven't read or is 14 | common practice, I want to know so I can get better! 15 | 16 | I will also accept pull requests on the usage of Golang. I'm not a Golang 17 | expert so the way I do things may not be the best for the language and I 18 | accept that as I'm also learning more about that as I go. Like before, if you 19 | submit a pull request on the code quality, please explain to me why this is 20 | more suited so I can learn. 21 | 22 | Ultimately, I want to absorb all the knowledge that I can by doing this 23 | project. I appreciate any help or advice offered in this process. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Dan Sackett 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Computer Science - Practice and Exploration 2 | 3 | This repository will be a place to practice computer science and crytpography 4 | principles in Golang. The following items are going to be examined: 5 | 6 | ### Notes 7 | 8 | * [Asymptotic Notation](https://github.com/dansackett/computer-science/blob/master/notes/asymptotic_notation.md) 9 | * [Powers of 2 Reference](https://github.com/dansackett/computer-science/blob/master/notes/powers_of_two.md) 10 | * [Bit Manipulation](https://github.com/dansackett/computer-science/blob/master/notes/bit_manipulation.md) 11 | 12 | ### Data Structures 13 | 14 | * [Linked List](https://github.com/dansackett/computer-science/blob/master/data-structures/linked-list) 15 | * [Binary Search Tree](https://github.com/dansackett/computer-science/blob/master/data-structures/binary-search-tree) 16 | * [Red-Black Tree](https://github.com/dansackett/computer-science/blob/master/data-structures/red-black-tree) 17 | * AVL Tree 18 | * Treap 19 | * B-Tree 20 | * [Hash Tables](https://github.com/dansackett/computer-science/blob/master/data-structures/hash-table) 21 | * Matrix 22 | * [Heap (Max / Min)](https://github.com/dansackett/computer-science/blob/master/data-structures/heap) 23 | * Trie 24 | * [Suffix Array](https://github.com/dansackett/computer-science/blob/master/data-structures/suffix-array) 25 | 26 | ### Abstract Data Types 27 | 28 | * [Set](https://github.com/dansackett/computer-science/blob/master/data-structures/set) 29 | * [Deque](https://github.com/dansackett/computer-science/blob/master/data-structures/deque) 30 | * [Stack](https://github.com/dansackett/computer-science/blob/master/data-structures/stack) 31 | * [Queue](https://github.com/dansackett/computer-science/blob/master/data-structures/queue) 32 | * [Priority Queue](https://github.com/dansackett/computer-science/blob/master/data-structures/priority-queue) 33 | * [Graph](https://github.com/dansackett/computer-science/blob/master/data-structures/graph) (Directed, Undirected) 34 | 35 | ### Graph algorithms 36 | 37 | **Shortest path:** 38 | * Dijkstra 39 | * Floyd–Warshall 40 | 41 | **Sorting:** 42 | * Topological Sort 43 | 44 | **Searcing:** 45 | * [Breadth First Search](https://github.com/dansackett/computer-science/blob/master/algorithms/graph/search) 46 | * Depth First Search 47 | 48 | ### Math algorithms 49 | 50 | * Binary GCD algorithm 51 | * Closest pairs 52 | * FastPower 53 | * Fibonacci 54 | * Fisher-Yates Shuffle 55 | * Erastothenes Sieve 56 | * Extented GCD algorithm 57 | * Karatsuba's Multiplication 58 | * Newton's Square Root 59 | * Permutations Count 60 | * Strassen's matrix multiplication 61 | * Randomized Selection 62 | 63 | ### Sorting algorithms 64 | 65 | **Bad:** 66 | * [Bubble Sort](https://github.com/dansackett/computer-science/blob/master/algorithms/sorting/bubble) 67 | 68 | **Basic:** 69 | * [Insertion Sort](https://github.com/dansackett/computer-science/blob/master/algorithms/sorting/insertion) 70 | * [Selection Sort](https://github.com/dansackett/computer-science/blob/master/algorithms/sorting/selection) 71 | * [Shell Sort](https://github.com/dansackett/computer-science/blob/master/algorithms/sorting/shell) 72 | 73 | **Fast:** 74 | * [Quick Sort](https://github.com/dansackett/computer-science/blob/master/algorithms/sorting/quick) 75 | * [Merge Sort](https://github.com/dansackett/computer-science/blob/master/algorithms/sorting/merge) 76 | * Heap Sort 77 | 78 | **Other** 79 | * Bucket Sort 80 | * Radix Sort 81 | 82 | ### Searching algorithms 83 | 84 | * [Binary Search](https://github.com/dansackett/computer-science/blob/master/algorithms/searching/binary) 85 | * [Linear Search](https://github.com/dansackett/computer-science/blob/master/algorithms/searching/linear) 86 | 87 | ### String Matching 88 | 89 | * [Knuth-Morris-Pratt](https://github.com/dansackett/computer-science/blob/master/algorithms/string-matching/knuth-morris-pratt) 90 | * Rabin-Karp 91 | 92 | ## Common Interview Questions 93 | 94 | * [Run-length Encoding / Decoding](https://github.com/dansackett/computer-science/blob/master/interview-questions/strings/run_length_encoding.go) 95 | * [Check if string is a substring](https://github.com/dansackett/computer-science/blob/master/interview-questions/strings/is_substring.go) 96 | 97 | ### Misc 98 | 99 | * [Recursive Palindromes](https://github.com/dansackett/computer-science/blob/master/algorithms/recursion/palindromes.go) 100 | * [Towers of Hanoi](https://github.com/dansackett/computer-science/blob/master/algorithms/recursion/towers_of_hanoi.go) 101 | * [Recursive Powers](https://github.com/dansackett/computer-science/blob/master/algorithms/recursion/powers.go) 102 | 103 | ### Design Patterns 104 | 105 | * [Factory](https://github.com/dansackett/computer-science/blob/master/design-patterns/factory/) 106 | * [Singleton](https://github.com/dansackett/computer-science/blob/master/design-patterns/singleton/) 107 | * [Adapter](https://github.com/dansackett/computer-science/blob/master/design-patterns/adapter/) 108 | * [Decorator](https://github.com/dansackett/computer-science/blob/master/design-patterns/decorator/) 109 | * [Observer](https://github.com/dansackett/computer-science/blob/master/design-patterns/observer/) 110 | * [Iterator](https://github.com/dansackett/computer-science/blob/master/design-patterns/iterator/) 111 | * [MVC](https://github.com/dansackett/computer-science/blob/master/design-patterns/mvc/) 112 | 113 | ### Cryptography ([Terms](https://github.com/dansackett/computer-science/blob/master/cryptography/)) 114 | 115 | **[Symmetric-Key](https://github.com/dansackett/computer-science/blob/master/cryptography/symmetric-key/)** 116 | * [Stream Ciphers](https://github.com/dansackett/computer-science/blob/master/cryptography/symmetric-key/stream-ciphers/) ([RC4](https://github.com/dansackett/computer-science/blob/master/cryptography/symmetric-key/stream-ciphers/rc4/), Salsa20, SOSEMANUK, FISH) 117 | * Block Ciphers (AES, DES, Triple DES, Blowfish) 118 | 119 | **Public Key Cryptography** 120 | * RSA Algorithm 121 | * DSA Algorithm 122 | 123 | **Misc** 124 | * Hash Functions (MD5, SHA1, SHA2, HMAC) 125 | * PGP 126 | -------------------------------------------------------------------------------- /algorithms/graph/search/README.md: -------------------------------------------------------------------------------- 1 | # Breadth-First Search (Graph) 2 | 3 | Breadth-first search is a method of traversing and searching tree / graph 4 | data structures. The basic concept is starting at a base vertex, we check 5 | the nearest neighbors assigning a distance from the base vertex and the 6 | vertex which leads to it. We then visit the neighbors of those neighbors 7 | until each vertex has been visited. 8 | 9 | For example, if we have a graph represented as an adjacency list: 10 | 11 | ``` 12 | 0: [1], 13 | 1: [0, 4, 5], 14 | 2: [3, 4, 5], 15 | 3: [2, 6], 16 | 4: [1, 2], 17 | 5: [1, 2, 6], 18 | 6: [3, 5], 19 | 7: [] 20 | ``` 21 | 22 | We can choose to start at vertex 3. Since it has no parent, we set it as a 23 | null-like value and set the distance to this as 0 since it's the base 24 | vertex. We then look to see the nearest neighbor vertices which in this 25 | case is vertex 2. 26 | 27 | With vertex 2, we set the distance to 1 and the parent to our base. Since 28 | there are no more neighbors at a distance of 1, we find the vertices which 29 | vertex 2 leds to. These are the new neighbors and they are vertex 4 and 30 | vertex 5. 31 | 32 | This pattern continues until each vertex has been assigned values. If we 33 | already visited the vertex, we skip it. In the end, you will have a mapping 34 | of each vertex with the distance from the base vertex and the vertex that 35 | leads to it. This is important because it allows us to: 36 | 37 | - Build a tree based on the distances 38 | - Find the shortest path from one vertex to another 39 | - Draw paths between vertices 40 | 41 | In this algorith, a Queue is used to keep track of the next vertices it 42 | needs to visit. This allows us to iterate through the neighbors and then 43 | pop those off the queue to get their next neighbors. 44 | 45 | ## Time Complexity 46 | 47 | `|V|` represents the number of vertices and `|E|` represents the number of edges. 48 | 49 | | Case | Complexity | 50 | | --------- |:--------------:| 51 | | Find | `O(|V| + |E|)` | 52 | -------------------------------------------------------------------------------- /algorithms/graph/search/breadth_first_search.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import q "../../../data-structures/queue" 4 | 5 | func BreadthFirstSearch(graph [][]int, source int) []map[string]int { 6 | data := make([]map[string]int, len(graph)) 7 | 8 | for i, _ := range graph { 9 | data[i] = map[string]int{ 10 | "distance": -1, 11 | "parent": -1, 12 | } 13 | } 14 | 15 | data[source]["distance"] = 0 16 | 17 | queue := q.NewQueue() 18 | queue.Enqueue(source) 19 | 20 | for !queue.IsEmpty() { 21 | currentVertex := queue.Dequeue() 22 | 23 | for i, _ := range graph[currentVertex] { 24 | neighbor := graph[currentVertex][i] 25 | 26 | if data[neighbor]["distance"] < 0 { 27 | data[neighbor]["distance"] = data[currentVertex]["distance"] + 1 28 | data[neighbor]["parent"] = currentVertex 29 | queue.Enqueue(neighbor) 30 | } 31 | } 32 | } 33 | 34 | return data 35 | } 36 | -------------------------------------------------------------------------------- /algorithms/graph/search/breadth_first_search_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "testing" 4 | import g "../../../data-structures/graph" 5 | 6 | func GetTestGraph() *g.Graph { 7 | graph := g.NewGraph() 8 | v0 := graph.AddVertex(0) 9 | v1 := graph.AddVertex(1) 10 | v2 := graph.AddVertex(2) 11 | v3 := graph.AddVertex(3) 12 | v4 := graph.AddVertex(4) 13 | v5 := graph.AddVertex(5) 14 | v6 := graph.AddVertex(6) 15 | graph.AddVertex(7) 16 | 17 | graph.AddEdge(v0, v1) 18 | graph.AddEdge(v1, v4) 19 | graph.AddEdge(v1, v5) 20 | graph.AddEdge(v2, v3) 21 | graph.AddEdge(v2, v4) 22 | graph.AddEdge(v2, v5) 23 | graph.AddEdge(v3, v6) 24 | graph.AddEdge(v5, v6) 25 | return graph 26 | } 27 | 28 | func TestBreadthFirstSearchWorks(t *testing.T) { 29 | graph := GetTestGraph() 30 | result := BreadthFirstSearch(graph.ToAdjacencyList(), 3) 31 | 32 | if result[0]["distance"] != 4 && result[0]["parent"] != 1 { 33 | msg := "For vertex 0, distance should be 4 and parent should be 1. Found distance as %d and parent as %d" 34 | t.Errorf(msg, result[0]["distance"], result[0]["parent"]) 35 | } 36 | 37 | if result[1]["distance"] != 3 && result[1]["parent"] != 4 { 38 | msg := "For vertex 1, distance should be 3 and parent should be 4. Found distance as %d and parent as %d" 39 | t.Errorf(msg, result[1]["distance"], result[1]["parent"]) 40 | } 41 | 42 | if result[2]["distance"] != 1 && result[2]["parent"] != 3 { 43 | msg := "For vertex 2, distance should be 1 and parent should be 3. Found distance as %d and parent as %d" 44 | t.Errorf(msg, result[2]["distance"], result[2]["parent"]) 45 | } 46 | 47 | if result[3]["distance"] != 0 && result[3]["parent"] != -1 { 48 | msg := "For vertex 3, distance should be 0 and parent should be -1. Found distance as %d and parent as %d" 49 | t.Errorf(msg, result[3]["distance"], result[3]["parent"]) 50 | } 51 | 52 | if result[4]["distance"] != 2 && result[4]["parent"] != 2 { 53 | msg := "For vertex 4, distance should be 2 and parent should be 2. Found distance as %d and parent as %d" 54 | t.Errorf(msg, result[4]["distance"], result[4]["parent"]) 55 | } 56 | 57 | if result[5]["distance"] != 2 && result[5]["parent"] != 2 { 58 | msg := "For vertex 5, distance should be 2 and parent should be 2. Found distance as %d and parent as %d" 59 | t.Errorf(msg, result[5]["distance"], result[5]["parent"]) 60 | } 61 | 62 | if result[6]["distance"] != 1 && result[6]["parent"] != 3 { 63 | msg := "For vertex 6, distance should be 1 and parent should be 3. Found distance as %d and parent as %d" 64 | t.Errorf(msg, result[6]["distance"], result[6]["parent"]) 65 | } 66 | 67 | if result[7]["distance"] != -1 && result[7]["parent"] != -1 { 68 | msg := "For vertex 7, distance should be -1 and parent should be -1. Found distance as %d and parent as %d" 69 | t.Errorf(msg, result[7]["distance"], result[7]["parent"]) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /algorithms/recursion/palindromes.go: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Algorithms - Recursive Palindrome 3 | // ---------------------------------------------------------------------------- 4 | // 5 | // Palindromes are words that are spelled the same way both forwards and 6 | // backwards. We can use recursion to determine this by checking if the first 7 | // and last letters are equal, stripping the first and last letters, and 8 | // checking again until we are sure that the word is in fact a palindrome. 9 | package recursion 10 | 11 | // FirstChar finds the first character of a string 12 | func FirstChar(str string) string { 13 | return string(str[0]) 14 | } 15 | 16 | // LastChar finds the last character of a string 17 | func LastChar(str string) string { 18 | return string(str[len(str)-1]) 19 | } 20 | 21 | // MiddleChars finds the characters between the first and last characters 22 | func MiddleChars(str string) string { 23 | return string(str[1 : len(str)-1]) 24 | } 25 | 26 | // IsPalindrome returns whether a string is a palindrome through recursion 27 | func IsPalindrome(str string) bool { 28 | if len(str) <= 1 { 29 | return true 30 | } 31 | 32 | if FirstChar(str) != LastChar(str) { 33 | return false 34 | } 35 | 36 | return IsPalindrome(MiddleChars(str)) 37 | } 38 | -------------------------------------------------------------------------------- /algorithms/recursion/palindromes_test.go: -------------------------------------------------------------------------------- 1 | package recursion 2 | 3 | import "testing" 4 | 5 | func TestSingleLetterIsPalindrome(t *testing.T) { 6 | result := IsPalindrome("a") 7 | 8 | if !result { 9 | t.Errorf("`a` should be a palindrome but our function says it is not.") 10 | } 11 | } 12 | 13 | func TestWordsArePalindromes(t *testing.T) { 14 | result1 := IsPalindrome("brick") 15 | result2 := IsPalindrome("racecar") 16 | 17 | if result1 { 18 | t.Errorf("`brick` should not be a palindrome but our function says it is.") 19 | } 20 | 21 | if !result2 { 22 | t.Errorf("`racecar` should be a palindrome but our function says it is not.") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /algorithms/recursion/powers.go: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Algorithms - Recursive Powers 3 | // ---------------------------------------------------------------------------- 4 | // 5 | // When computing a number to a specific power, you can use recursion to make 6 | // life easier. We can do this following these rules: 7 | // 8 | // - Anything to the power of 0 == 1 9 | // - Any negative power == 1 / x^-n 10 | // - Any positive even number == x^n/2 * x^n/2 11 | // - Any positive odd number == x^n-1 * x 12 | package recursion 13 | 14 | func Power(x, n int) int { 15 | if n == 0 { 16 | return 1 17 | } 18 | 19 | if n < 0 { 20 | return int(1 / Power(x, -n)) 21 | } 22 | 23 | if n%2 != 0 { 24 | return Power(x, n-1) * x 25 | } 26 | 27 | if n%2 == 0 { 28 | result := Power(x, n/2) 29 | return result * result 30 | } 31 | 32 | return 0 33 | } 34 | -------------------------------------------------------------------------------- /algorithms/recursion/powers_test.go: -------------------------------------------------------------------------------- 1 | package recursion 2 | 3 | import "testing" 4 | 5 | func TestCanComputeZeroPower(t *testing.T) { 6 | result := Power(9, 0) 7 | 8 | if result != 1 { 9 | t.Errorf("9^0 should equal 1, found %d", result) 10 | } 11 | } 12 | 13 | func TestCanComputeOddPower(t *testing.T) { 14 | result := Power(9, 3) 15 | 16 | if result != 729 { 17 | t.Errorf("9^3 should equal 729, found %d", result) 18 | } 19 | } 20 | 21 | func TestCanComputeEvenPower(t *testing.T) { 22 | result := Power(9, 2) 23 | 24 | if result != 81 { 25 | t.Errorf("9^2 should equal 81, found %d", result) 26 | } 27 | } 28 | 29 | // This test shows that since I'm using integers, we won't get a correct 30 | // answer here since we chop the decimal places. 31 | func TestCanComputeNegativePower(t *testing.T) { 32 | result := Power(9, -4) 33 | 34 | if result != 0 { 35 | t.Errorf("9^-4 should equal 0, found %d", result) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /algorithms/recursion/towers_of_hanoi.go: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Algorithms - Towers of Hanoi 3 | // ---------------------------------------------------------------------------- 4 | // 5 | // The Tower of Hanoi is a great problem in recursion involving three pegs and 6 | // n number of blocks. The goal is to move the n blocks to the middle peg but 7 | // there are rules: 8 | // 9 | // - You may only move one block at a time 10 | // - A block may not have smaller blocks beneath it 11 | // - A block can move to any peg 12 | // 13 | // Solving this with recursion is actually quite fun! 14 | package recursion 15 | 16 | import "fmt" 17 | 18 | // Peg is one of the rods hoding a tower of blocks 19 | type Peg struct { 20 | Blocks []int 21 | } 22 | 23 | // NewPeg inializes a new peg with some blocks 24 | func NewPeg(blocks []int) *Peg { 25 | return &Peg{Blocks: blocks} 26 | } 27 | 28 | // Pop grabs the last value from a peg, removes it from the list, and returns it 29 | func (p *Peg) Pop() int { 30 | val := p.Blocks[len(p.Blocks)-1] 31 | p.Blocks = p.Blocks[0 : len(p.Blocks)-1] 32 | return val 33 | } 34 | 35 | // Push places a new value onto the end of a peg 36 | func (p *Peg) Push(val int) { 37 | p.Blocks = append(p.Blocks, val) 38 | } 39 | 40 | // MoveBlock pops a block off the source and pushes it onto the destination 41 | func MoveBlock(source, dest *Peg) { 42 | dest.Push(source.Pop()) 43 | } 44 | 45 | // Hanoi represents the set of pegs in the game 46 | type Hanoi struct { 47 | Pegs map[string]*Peg 48 | } 49 | 50 | // NewHanoi initializes a new game of Hanoi by setting the number of blocks on 51 | // peg `A` and none on pegs `B` and `C` 52 | func NewHanoi(numBlocks int) *Hanoi { 53 | var initial []int 54 | for i := numBlocks; i > 0; i-- { 55 | initial = append(initial, i) 56 | } 57 | 58 | pegs := map[string]*Peg{ 59 | "A": NewPeg(initial), 60 | "B": NewPeg([]int{}), 61 | "C": NewPeg([]int{}), 62 | } 63 | 64 | return &Hanoi{Pegs: pegs} 65 | } 66 | 67 | // GetPegs returns three pegs based on a `from` and `to` peg programatically 68 | // determining the third peg based on those two 69 | func (h *Hanoi) GetPegs(from, to string) (*Peg, *Peg, *Peg) { 70 | pegs := map[string]*Peg{ 71 | "A": h.Pegs["A"], 72 | "B": h.Pegs["B"], 73 | "C": h.Pegs["C"], 74 | } 75 | 76 | fromPeg := pegs[from] 77 | delete(pegs, from) 78 | 79 | toPeg := pegs[to] 80 | delete(pegs, to) 81 | 82 | // Find the last peg in the set 83 | var key string 84 | for k := range pegs { 85 | key = k 86 | } 87 | otherPeg := pegs[key] 88 | 89 | return fromPeg, toPeg, otherPeg 90 | } 91 | 92 | // Print shows a basic display of the pegs in Hanoi in their current state 93 | func (h *Hanoi) Print() { 94 | a, b, c := h.GetPegs("A", "B") 95 | pegs := []*Peg{a, b, c} 96 | 97 | fmt.Println("////////////////////") 98 | for index, peg := range pegs { 99 | fmt.Println("PEG", index+1) 100 | fmt.Println(peg.Blocks) 101 | } 102 | } 103 | 104 | // MoveForward is the recursion workhose which rotates the blocks around 105 | func (h *Hanoi) MoveForward(index int, source, dest, spare *Peg) { 106 | if index == 0 { 107 | MoveBlock(source, dest) 108 | } else { 109 | h.MoveForward(index-1, source, spare, dest) 110 | MoveBlock(source, dest) 111 | h.MoveForward(index-1, spare, dest, source) 112 | } 113 | } 114 | 115 | // Run is the entry point of Hanoi allowing you to specify the number of 116 | // blocks to use and the `from` and `to` pegs 117 | func (h *Hanoi) Run(block int, from, to string) { 118 | fromPeg, toPeg, otherPeg := h.GetPegs(from, to) 119 | h.MoveForward(block-1, fromPeg, toPeg, otherPeg) 120 | } 121 | -------------------------------------------------------------------------------- /algorithms/recursion/towers_of_hanoi_test.go: -------------------------------------------------------------------------------- 1 | package recursion 2 | 3 | import "testing" 4 | 5 | func TestHanoiCanMoveFromFullTower(t *testing.T) { 6 | hanoi := NewHanoi(5) 7 | 8 | if len(hanoi.Pegs["A"].Blocks) != 5 { 9 | t.Errorf("`A` does not have the initial blocks") 10 | } 11 | 12 | hanoi.Run(5, "A", "B") 13 | 14 | if len(hanoi.Pegs["A"].Blocks) != 0 { 15 | t.Errorf("`A` has blocks that it should not have") 16 | } 17 | 18 | if len(hanoi.Pegs["B"].Blocks) != 5 { 19 | t.Errorf("`B` does not have the new blocks") 20 | } 21 | } 22 | 23 | func TestHanoiCanMovePartialTower(t *testing.T) { 24 | hanoi := NewHanoi(5) 25 | 26 | if len(hanoi.Pegs["A"].Blocks) != 5 { 27 | t.Errorf("`A` does not have the initial blocks") 28 | } 29 | 30 | hanoi.Run(3, "A", "B") 31 | 32 | if len(hanoi.Pegs["A"].Blocks) != 2 { 33 | t.Errorf("`A` has blocks that it should not have") 34 | } 35 | 36 | if len(hanoi.Pegs["B"].Blocks) != 3 { 37 | t.Errorf("`B` does not have the new blocks") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /algorithms/searching/binary/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search 2 | 3 | With Binary Search, the goal is to constantly work on smaller data sets in 4 | order to limit the number of guesses one must make. Based on the size of 5 | the array (slice) we're searching, we can quickly find an answer by 6 | guessing the number in the middle of our maximum and minimum numbers. So 7 | for instance, if we have: 8 | 9 | ``` 10 | [1, 2, 3, 4, 5, 6, 7] 11 | ``` 12 | 13 | And the number we're looking for is 5 then in a linear search it would 14 | take us 5 guesses to get there. With binary search, it would only take us 3 15 | guesses: 16 | 17 | - We find that the middle index of the list is 3 18 | - The value at index 3 is 4. Since our value we wish to find (5) is larger 19 | than 4, we discard the first half leaving the new set as `[5, 6, 7]`. 20 | - We find the new middle index of the list is 5 (based on the original list) 21 | - The value at index 5 is 6. Since our value we wish to find (5) is less than 22 | 6, we discard the upper half of the set leaving the new set as `[5]`. 23 | - The remaining middle index is at 4 and that value is 5, the number we are 24 | looking for. 25 | 26 | ## Time Complexity 27 | 28 | | Case | Complexity | 29 | | --------- |:-----------:| 30 | | Find | `O(lg n)` | 31 | -------------------------------------------------------------------------------- /algorithms/searching/binary/binary.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | func BinarySearch(haystack []int, needle int) int { 4 | min := 0 5 | max := len(haystack) - 1 6 | 7 | for max >= min { 8 | guess := (max + min) / 2 9 | if haystack[guess] == needle { 10 | return guess 11 | } else if haystack[guess] < needle { 12 | min = guess + 1 13 | } else { 14 | max = guess - 1 15 | } 16 | } 17 | return -1 18 | } 19 | -------------------------------------------------------------------------------- /algorithms/searching/binary/binary_test.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import "testing" 4 | 5 | func TestBinaryCanFindInteger(t *testing.T) { 6 | primes := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 7 | 59, 61, 67, 71, 73, 79, 83, 89, 97} 8 | 9 | result1 := BinarySearch(primes, 73) 10 | result2 := BinarySearch(primes, 11) 11 | result3 := BinarySearch(primes, 65) 12 | 13 | if result1 != 20 { 14 | t.Errorf("73 is at index 20 not %d", result1) 15 | } 16 | 17 | if result2 != 4 { 18 | t.Errorf("11 is at index 4 not %d", result2) 19 | } 20 | 21 | if result3 != -1 { 22 | t.Errorf("65 IS NOT in this array, found it at %d", result3) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /algorithms/searching/linear/README.md: -------------------------------------------------------------------------------- 1 | # Linear Search 2 | 3 | Linear search visits each item in a sequence checking if the value is equal. 4 | If it is, we stop. If not, we keep looking. So for a list like the following, 5 | if we're looking for 5 then it would take us 5 guesses to get there. 6 | 7 | ``` 8 | [1, 2, 3, 4, 5, 6, 7] 9 | ``` 10 | 11 | ## Time Complexity 12 | 13 | | Case | Complexity | 14 | | --------- |:-----------:| 15 | | Find | `O(n)` | 16 | -------------------------------------------------------------------------------- /algorithms/searching/linear/linear.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | func LinearSearch(haystack []int, needle int) int { 4 | for i, guess := range haystack { 5 | if guess == needle { 6 | return i 7 | } 8 | } 9 | return -1 10 | } 11 | -------------------------------------------------------------------------------- /algorithms/searching/linear/linear_test.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import "testing" 4 | 5 | func TestLinearCanFindInteger(t *testing.T) { 6 | primes := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 7 | 59, 61, 67, 71, 73, 79, 83, 89, 97} 8 | 9 | result1 := LinearSearch(primes, 73) 10 | result2 := LinearSearch(primes, 11) 11 | result3 := LinearSearch(primes, 65) 12 | 13 | if result1 != 20 { 14 | t.Errorf("73 is at index 20 not %d", result1) 15 | } 16 | 17 | if result2 != 4 { 18 | t.Errorf("11 is at index 4 not %d", result2) 19 | } 20 | 21 | if result3 != -1 { 22 | t.Errorf("65 IS NOT in this array, found it at %d", result3) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /algorithms/sorting/bubble/README.md: -------------------------------------------------------------------------------- 1 | # Bubble Sort 2 | 3 | Bubble sort is a brute-force sorting algorithm which repeatedly steps through 4 | a list comparing adjacent elements and swapping them if in the wrong order. It 5 | continues to do this pass through until no more swaps are needed. Bubble sort 6 | in almost all cases is a poor choice for sorting in real world applications 7 | but since the algorithm doesn't manipulate anything on a pass where the data 8 | is already sorted it is good to verify that an array is sorted. This differs 9 | from other sorting techniques since they actually do their full sorting 10 | behavior since it doesn't know differently. 11 | 12 | An example of operations is as follows: 13 | 14 | **First Pass** 15 | 16 | [ `5` `1` 4 2 8 ] -> [ `1` `5` 4 2 8 ], Here, algorithm compares the first two elements, and swaps since 5 > 1.
17 | [ 1 `5` `4` 2 8 ] -> [ 1 `4` `5` 2 8 ], Swap since 5 > 4
18 | [ 1 4 `5` `2` 8 ] -> [ 1 4 `2` `5` 8 ], Swap since 5 > 2
19 | [ 1 4 2 `5` `8` ] -> [ 1 4 2 `5` `8` ], Now, since these elements are already in order [8 > 5], algorithm does not swap them. 20 | 21 | **Second Pass** 22 | 23 | [ `1` `4` 2 5 8 ] -> [ `1` `4` 2 5 8 ]
24 | [ 1 `4` `2` 5 8 ] -> [ 1 `2` `4` 5 8 ], Swap since 4 > 2
25 | [ 1 2 `4` `5` 8 ] -> [ 1 2 `4` `5` 8 ]
26 | [ 1 2 4 `5` `8` ] -> [ 1 2 4 `5` `8` ] 27 | 28 | Now, the array is already sorted, but the algorithm does not know if it is completed. The algorithm needs one whole pass without any swap to know it is sorted. 29 | 30 | **Third Pass** 31 | 32 | [ `1` `2` 4 5 8 ] -> [ `1` `2` 4 5 8 ]
33 | [ 1 `2` `4` 5 8 ] -> [ 1 `2` `4` 5 8 ]
34 | [ 1 2 `4` `5` 8 ] -> [ 1 2 `4` `5` 8 ]
35 | [ 1 2 4 `5` `8` ] -> [ 1 2 4 `5` `8` ] 36 | 37 | ## Time Complexity 38 | 39 | | Case | Complexity | 40 | | --------- |:-----------:| 41 | | Worst | `O(n^2)` | 42 | | Best | `O(n)` | 43 | | Average | `O(n^2)` | 44 | -------------------------------------------------------------------------------- /algorithms/sorting/bubble/bubble.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | // BubbleSort takes a slice of integers and sorts it in place. 4 | func BubbleSort(data []int) { 5 | n := len(data) 6 | swapped := true 7 | for swapped { 8 | swapped = false 9 | for i := 1; i <= n-1; i++ { 10 | if data[i-1] > data[i] { 11 | Swap(data, i-1, i) 12 | swapped = true 13 | } 14 | } 15 | } 16 | } 17 | 18 | // Swap takes two indices and switches the values between them. 19 | func Swap(data []int, i, j int) { 20 | data[i], data[j] = data[j], data[i] 21 | } 22 | -------------------------------------------------------------------------------- /algorithms/sorting/bubble/bubble_test.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import "testing" 4 | 5 | func TestBubbleSortWithOneValue(t *testing.T) { 6 | testCase := []int{1} 7 | target := []int{1} 8 | 9 | BubbleSort(testCase) 10 | 11 | for i, val := range testCase { 12 | if val != target[i] { 13 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 14 | } 15 | } 16 | } 17 | 18 | func TestBubbleSortWithMultipleValues(t *testing.T) { 19 | testCase := []int{3, 6, 2, 9, 4, 10} 20 | target := []int{2, 3, 4, 6, 9, 10} 21 | 22 | BubbleSort(testCase) 23 | 24 | for i, val := range testCase { 25 | if val != target[i] { 26 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/sorting/insertion/README.md: -------------------------------------------------------------------------------- 1 | # Insertion Sort 2 | 3 | This is an example of Insertion Sort. It takes a set of data starting at 4 | the second index. As it moves through the array, it checks the values 5 | before the current index for a value less than itself and if it finds it, 6 | it inserts itself in that index. For example: 7 | 8 | If we have a set: `[3, 1, 6, 0, 5]` 9 | 10 | We start at index 1 with value 1. We move backwards to index 0 and see that 3 11 | is greater than 1. Since this is the beginning of the set we place our current 12 | value (1) at index 0 and move the value at index 0 to index 1. Our new set is: 13 | 14 | ``` 15 | [1, 3, 6, 0, 5] 16 | ``` 17 | 18 | On the next iteration, we move to index 2 where we see the value is 6. We 19 | check index 1 and 0 and see that both values are smaller than 6 so we don't 20 | do anything. Our set remains the same: 21 | 22 | ``` 23 | [1, 3, 6, 0, 5] 24 | ``` 25 | 26 | On the third iteration, we start at index 3 and see the value 0. We check 27 | the values at index 2, 1, and 0 and see that they are all larger than our 28 | value so we move the set to the right and place our value at the front of 29 | the set giving us a new set: 30 | 31 | ``` 32 | [0, 1, 3, 6, 5] 33 | ``` 34 | 35 | On our last iteration, we start at index 4 and see the value 5. We check 36 | index 3 and see the value greater than 5 but at index 2 we see the value is 37 | less than our value (3). We stop there, move the 6 over to the right and 38 | place 5 in the index 4. 39 | 40 | Our set is now sorted! 41 | 42 | ## Time Complexity 43 | 44 | | Case | Complexity | 45 | | --------- |:----------:| 46 | | Worst | `O(n^2)` | 47 | | Best | `O(n^2)` | 48 | | Average | `O(n^2)` | 49 | -------------------------------------------------------------------------------- /algorithms/sorting/insertion/insertion.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | // InsertionSort takes a slice of integers and sorts it in place. 4 | func InsertionSort(data []int) { 5 | for i := 1; i < len(data); i++ { 6 | Insert(data, i-1, data[i]) 7 | } 8 | } 9 | 10 | // Insert loops backwards through and array until it finds a value less than 11 | // itself and places itself directly after it. 12 | func Insert(data []int, rightBound, val int) { 13 | var i int 14 | for i = rightBound; i >= 0 && data[i] > val; i-- { 15 | data[i+1] = data[i] 16 | } 17 | data[i+1] = val 18 | } 19 | -------------------------------------------------------------------------------- /algorithms/sorting/insertion/insertion_test.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import "testing" 4 | 5 | func TestInsertionSortWithOneValue(t *testing.T) { 6 | testCase := []int{1} 7 | target := []int{1} 8 | 9 | InsertionSort(testCase) 10 | 11 | for i, val := range testCase { 12 | if val != target[i] { 13 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 14 | } 15 | } 16 | } 17 | 18 | func TestInsertionSortWithMultipleValues(t *testing.T) { 19 | testCase := []int{3, 6, 2, 9, 4, 10} 20 | target := []int{2, 3, 4, 6, 9, 10} 21 | 22 | InsertionSort(testCase) 23 | 24 | for i, val := range testCase { 25 | if val != target[i] { 26 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/sorting/merge/README.md: -------------------------------------------------------------------------------- 1 | # Merge Sort 2 | 3 | Merge sort is a recursive sorting algorithm which utilizes the divide and 4 | conquer concept. It works by taking a set of data, splitting it into two 5 | halves, and merging those two halves in sorted order. For example: 6 | 7 | If we have a set: `[3, 1, 6, 0, 5]` 8 | 9 | We split that into two halves `[3, 1, 6]` and `[0, 5]`. We then split those 10 | halves into halves again: `[3, 1]` and `[6]` and `[0]` and `[5]`. We split the last 11 | half into two and then we begin the merge as we bubble back up: 12 | 13 | ``` 14 | --> [3] 15 | --> [3, 1] --| 16 | | --> [1] 17 | --> [3, 1, 6] --| 18 | | --> [6] 19 | [3, 1, 6, 0, 5] --| 20 | | --> [0] 21 | --> [0, 5 ] --| 22 | --> [5] 23 | ``` 24 | 25 | We can start from the final split and compare 3 with 1 and see that `1 < 3` so 26 | we get the new set `[1, 3]`. Since we don't have the value to compare to `[6]` 27 | yet we can check out the other split and compare `0 < 5` so we get `[0, 5]` as 28 | we already had. Our new graphic would look like this for the next round: 29 | 30 | ``` 31 | --> [1, 3] 32 | --> [3, 1, 6] --| 33 | | --> [6] 34 | [3, 1, 6, 0, 5] --| 35 | --> [0, 5 ] 36 | ``` 37 | 38 | On this round, we do a sort between our `[1, 3]` and `[6]`. We compare the 39 | first indices of the sets and see that `1 < 6`. Since the left side won, we 40 | remove the value at index 0 from the left side (1) and we then move onto 41 | another comparison at index 0 between the new value on the left and the old 42 | value on the right: `3 < 6`. We add 3 to the resulting set and add 6 onto the 43 | end of the resulting set since it's the last number giving us `[1, 3, 6]`. Our 44 | new graphic with what's left to merge looks like: 45 | 46 | ``` 47 | --> [1, 3, 6] 48 | [3, 1, 6, 0, 5] --| 49 | --> [0, 5 ] 50 | ``` 51 | 52 | This final merging step does as we did before comparing numbers at the 0 53 | index. When one side wins, we remove the winning value from the set and 54 | compare the next value with the losing sides value. So in short: 55 | 56 | ``` 57 | 0 < 1 58 | 1 < 5 59 | 3 < 5 60 | 5 < 6 61 | ``` 62 | 63 | This gives us the final sorted set of `[0, 1, 3, 5, 6]`. 64 | 65 | ## Time Complexity 66 | 67 | | Case | Complexity | 68 | | --------- |:-----------:| 69 | | Worst | `O(n lg n)` | 70 | | Best | `O(n lg n)` | 71 | | Average | `O(n lg n)` | 72 | -------------------------------------------------------------------------------- /algorithms/sorting/merge/merge.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | // MergeSort takes a slice of integers and sorts it 4 | func MergeSort(data []int) []int { 5 | if len(data) <= 1 { 6 | return data 7 | } 8 | 9 | left := MergeSort(data[:len(data)/2]) 10 | right := MergeSort(data[len(data)/2:]) 11 | 12 | return Merge(left, right) 13 | } 14 | 15 | // Merge takes the left and right side merging the two into a sorted slice 16 | func Merge(left, right []int) []int { 17 | var result []int 18 | 19 | // While both the left and right sides have values, sort accordingly 20 | for len(left) > 0 && len(right) > 0 { 21 | if left[0] < right[0] { 22 | result = append(result, left[0]) 23 | left = left[1:] 24 | } else { 25 | result = append(result, right[0]) 26 | right = right[1:] 27 | } 28 | } 29 | 30 | // Add any extra values from the left and right sides 31 | result = append(result, left...) 32 | result = append(result, right...) 33 | return result 34 | } 35 | -------------------------------------------------------------------------------- /algorithms/sorting/merge/merge_test.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import "testing" 4 | 5 | func TestMergeSortWithOneValue(t *testing.T) { 6 | testCase := []int{1} 7 | target := []int{1} 8 | 9 | testCase = MergeSort(testCase) 10 | 11 | for i, val := range testCase { 12 | if val != target[i] { 13 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 14 | } 15 | } 16 | } 17 | 18 | func TestMergeSortWithMultipleValues(t *testing.T) { 19 | testCase := []int{3, 6, 2, 9, 4, 10} 20 | target := []int{2, 3, 4, 6, 9, 10} 21 | 22 | testCase = MergeSort(testCase) 23 | 24 | for i, val := range testCase { 25 | if val != target[i] { 26 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/sorting/quick/README.md: -------------------------------------------------------------------------------- 1 | # Quick Sort 2 | 3 | Quick sort is like merge sort in that it's a divide and conquer recursive 4 | algorithm. It differs however by where the work is being done. In merge 5 | sort, we do the bulk of the work in the combine step, but with quick sort 6 | the work is done in the divide step. In general, it will outperform merge 7 | sort and it is done in place which is even better. As an example: 8 | 9 | If we have a set: `[3, 7, 8, 5, 4]` 10 | 11 | Using quick sort, we find the **pivot** or point which we will split the set 12 | into two parts. In practice, we select the last item in the set. This is the 13 | **partition** phase. In our set, our first pivot is `4`. We then take this 14 | pivot and compare it with each other number in the set: 15 | 16 | ``` 17 | 4 > 3, set is: [3, 7, 8, 5, [4]] 18 | 4 < 7, set is: [3, 8, 5, [4], 7] 19 | 4 < 8, set is: [3, 5, [4], 8, 7] 20 | 4 < 5, set is: [3, [4], 5, 8, 7] 21 | ``` 22 | 23 | Now that the data is partitioned as `[3]` and `[5, 8, 7]` we can break them 24 | apart and do a quick sort on each smaller set like we did before. In this 25 | case, we see that `[3]` is alone so this is already sorted. Our other set is 26 | not though. On that side, the pivot value becomes `7`. 27 | 28 | ``` 29 | 7 > 5, set is: [5, 8, [7]] 30 | 7 < 8, set is: [5, [7], 8] 31 | ``` 32 | 33 | This gives us single values on each side as well and now we have the following 34 | values that need to bubble up: 35 | 36 | ``` 37 | [3] (Initial left single number) 38 | [4] (Initial pivot value) 39 | [5] (Smaller of right subset) 40 | [7] (Second pivot value) 41 | [8] (Larger of right subset) 42 | ``` 43 | 44 | Bubbling up, we get the sorted set of `[3, 4, 5, 7, 8]`. 45 | 46 | ## Time Complexity 47 | 48 | | Case | Complexity | 49 | | --------- |:-----------:| 50 | | Worst | `O(n^2)` | 51 | | Best | `O(n lg n)` | 52 | | Average | `O(n lg n)` | 53 | -------------------------------------------------------------------------------- /algorithms/sorting/quick/quick.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | // QuickSort is the entry to the sorter taking in just the data to be sorted 4 | // determining the high and low values for us. 5 | func QuickSort(data []int) { 6 | quickSort(data, 0, len(data)-1) 7 | } 8 | 9 | // quickSort is the recursive function which breaks the data into smaller 10 | // chunks based on a calculated pivot value. 11 | func quickSort(data []int, low, high int) { 12 | if low < high { 13 | pivot := Partition(data, low, high) 14 | quickSort(data, low, pivot-1) 15 | quickSort(data, pivot+1, high) 16 | } 17 | } 18 | 19 | // Swap takes two indices and switches the values between them. 20 | func Swap(data []int, i, j int) { 21 | data[i], data[j] = data[j], data[i] 22 | } 23 | 24 | // Partition swaps values in the data while holding a reference to the pivot 25 | // value to return for the recursive search to decrease. 26 | func Partition(data []int, low, high int) int { 27 | pivot := data[high] 28 | pivotIndex := low 29 | for j := low; j <= high-1; j++ { 30 | if data[j] <= pivot { 31 | Swap(data, pivotIndex, j) 32 | pivotIndex++ 33 | } 34 | } 35 | Swap(data, pivotIndex, high) 36 | return pivotIndex 37 | } 38 | -------------------------------------------------------------------------------- /algorithms/sorting/quick/quick_test.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import "testing" 4 | 5 | func TestQuickSortWithOneValue(t *testing.T) { 6 | testCase := []int{1} 7 | target := []int{1} 8 | 9 | QuickSort(testCase) 10 | 11 | for i, val := range testCase { 12 | if val != target[i] { 13 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 14 | } 15 | } 16 | } 17 | 18 | func TestQuickSortWithMultipleValues(t *testing.T) { 19 | testCase := []int{3, 6, 2, 9, 4, 10} 20 | target := []int{2, 3, 4, 6, 9, 10} 21 | 22 | QuickSort(testCase) 23 | 24 | for i, val := range testCase { 25 | if val != target[i] { 26 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/sorting/selection/README.md: -------------------------------------------------------------------------------- 1 | # Selection Sort 2 | 3 | This is an example of Selection Sort. Selection sort does an in place sort 4 | on a set (slice). It's easiest to explain how it works with an example: 5 | 6 | If we have a set: `[3, 1, 6, 0, 5]` 7 | 8 | We start at index 0 and see the number 3. We then go from index 1 through the 9 | end of the set looking for the next smallest number. So: 10 | 11 | ``` 12 | 1 < 6 13 | 1 > 0 14 | 0 < 5 15 | ``` 16 | 17 | This means the the number 0 at index 3 is the smallest number and we swap the 18 | number 3 at index 0 with the number 0 at index 3 giving us: 19 | 20 | ``` 21 | [0, 1, 6, 3, 5] 22 | ``` 23 | 24 | We move onto index 1 where the value is 1 and check each number following it 25 | like before: 26 | 27 | ``` 28 | 1 < 6 29 | 1 < 3 30 | 1 < 5 31 | ``` 32 | 33 | This is the smallest number so our list is the same at: 34 | 35 | ``` 36 | [0, 1, 6, 3, 5] 37 | ``` 38 | 39 | We move onto index 2 where the value is 6 and we check each number following 40 | it like before: 41 | 42 | ``` 43 | 6 > 3 44 | 3 < 5 45 | ``` 46 | 47 | The number at index 3 is smaller than our 6 so we swap those values leaving us 48 | with: 49 | 50 | ``` 51 | [0, 1, 3, 6, 5] 52 | ``` 53 | 54 | We move onto index 3 where the value is 6 and we check each number following 55 | it like before: 56 | 57 | ``` 58 | 6 > 5 59 | ``` 60 | 61 | The number at index 4 is smaller than our 6 so we swap those values leaving us 62 | with: 63 | 64 | ``` 65 | [0, 1, 3, 5, 6] 66 | ``` 67 | 68 | On the last iteration, we see that this is the final item in the list so it 69 | must be sorted and in fact, it is! 70 | 71 | While this process is certainly tedious, it does give us a way to 72 | programatticaly go through a set and sort it. 73 | 74 | ## Time Complexity 75 | 76 | | Case | Complexity | 77 | | --------- |:-----------:| 78 | | Worst | `O(n^2)` | 79 | | Best | `O(n^2)` | 80 | | Average | `O(n^2)` | 81 | -------------------------------------------------------------------------------- /algorithms/sorting/selection/selection.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | // SelectionSort takes a slice of integers and sorts it in place. 4 | func SelectionSort(data []int) { 5 | for i, _ := range data { 6 | min := MinIndex(data, i) 7 | Swap(data, i, min) 8 | } 9 | } 10 | 11 | // Swap takes two indices and switches the values between them. 12 | func Swap(data []int, i, j int) { 13 | data[i], data[j] = data[j], data[i] 14 | } 15 | 16 | // MinIndex finds the index of the next smallest number in a slice. 17 | func MinIndex(data []int, initial int) int { 18 | minValue := data[initial] 19 | minIndex := initial 20 | 21 | for i := initial; i < len(data); i++ { 22 | if data[i] < minValue { 23 | minIndex = i 24 | minValue = data[i] 25 | } 26 | } 27 | 28 | return minIndex 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/sorting/selection/selection_test.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import "testing" 4 | 5 | func TestSelectionSortWithOneValue(t *testing.T) { 6 | testCase := []int{1} 7 | target := []int{1} 8 | 9 | SelectionSort(testCase) 10 | 11 | for i, val := range testCase { 12 | if val != target[i] { 13 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 14 | } 15 | } 16 | } 17 | 18 | func TestSelectionSortWithMultipleValues(t *testing.T) { 19 | testCase := []int{3, 6, 2, 9, 4, 10} 20 | target := []int{2, 3, 4, 6, 9, 10} 21 | 22 | SelectionSort(testCase) 23 | 24 | for i, val := range testCase { 25 | if val != target[i] { 26 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/sorting/shell/README.md: -------------------------------------------------------------------------------- 1 | # Shell Sort 2 | 3 | Shell sort is an improvement on insertion sort which uses the concept of 4 | comparing values that are far apart and reducing that gap until all values are 5 | sorted. It is this gap sequence that defines the shell sort algorithm as the 6 | gap determines the running time of the algorith. The challenging part about 7 | gaps is that they are largely dependent on the input size. 8 | 9 | To get an idea of how the process works we can think of these steps: 10 | 11 | - Determine a reasonable gap 12 | - Compare the element at an index to another element at index + gap 13 | - If the elements are out of order, swap them over the gap 14 | - Continue through the array swapping over the gap until you cannot 15 | - Cut the gap in half and compare the values against this new gap 16 | - The array is sorted after a gap of 1 17 | 18 | The reasoning behind this is if you sort elements at a larger gap then the 19 | array will become more sorted faster and later passes with smaller gap sizes 20 | will be faster. This is the main difference from insertion sort since it 21 | doesn't do as many swaps. 22 | 23 | An example can help in describing this algorithm: 24 | 25 | Let's assume we have the array: `[3, 7, 8, 5, 4, 1, 10]` 26 | 27 | We can find a suitable gap by seeing the length of the array is 7. We can use 28 | that knowlege to see `(7 / 2) + 1`. This will give us a gap which effectively 29 | halves the data on first pass. This gap is `4`. 30 | 31 | We can now compare elements at the gap positions: 32 | 33 | **Pass 1** 34 | 35 | [`3`, 7, 8, 5, `4`, 1, 10] 3 < 4 so no swap
36 | [3, `7`, 8, 5, 4, `1`, 10] 7 > 1 so swap
37 | [3, 1, `8`, 5, 4, 7, `10`] 8 < 10 so no swap 38 | 39 | On second pass we determine the new gap which will be half of the previous gap 40 | so we can use `2`. 41 | 42 | **Pass 2** 43 | 44 | [`3`, 1, `8`, 5, 4, 7, 10] 3 < 8 so no swap
45 | [3, `1`, 8, `5`, 4, 7, 10] 1 < 5 so no swap
46 | [3, 1, `8`, 5, `4`, 7, 10] 8 > 4 so swap
47 | [3, 1, 4, `5`, 8, `7`, 10] 5 < 7 so no swap
48 | [3, 1, 4, 5, `8`, 7, `10`] 8 < 10 so no swap 49 | 50 | On the third pass, the new gap is `1` so we compare every value like a bubble 51 | sort would. 52 | 53 | **Pass 3** 54 | 55 | [`3`, `1`, 4, 5, 8, 7, 10] 3 > 1 so swap
56 | [1, `3`, `4`, 5, 8, 7, 10] 3 < 4 so no swap
57 | [1, 3, `4`, `5`, 8, 7, 10] 4 < 5 so no swap
58 | [1, 3, 4, `5`, `8`, 7, 10] 5 < 8 so no swap
59 | [1, 3, 4, 5, `8`, `7`, 10] 8 > 7 so swap
60 | [1, 3, 4, 5, 7, `8`, `10`] 8 < 10 so no swap 61 | 62 | As we can see, the array is mostly sorted by the time we finish the second 63 | pass and this is the reason for the shell sort over the insertion sort. 64 | 65 | ## Time Complexity 66 | 67 | | Case | Complexity | 68 | | --------- |:-----------:| 69 | | Worst | `O(n^2)` | 70 | | Best | `O(n lg2 n)`| 71 | | Average | `Depends` | 72 | -------------------------------------------------------------------------------- /algorithms/sorting/shell/shell.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | // InsertionSort is the internal swapping algorithm. 4 | func InsertionSort(data []int, interval int) { 5 | // Outer is the right bound 6 | for outer := interval; outer < len(data); outer++ { 7 | val := data[outer] 8 | inner := outer 9 | 10 | // Inner is the left bound 11 | for inner > interval-1 && data[inner-interval] >= val { 12 | Swap(data, inner, inner-1) 13 | inner -= interval 14 | } 15 | 16 | data[inner] = val 17 | } 18 | } 19 | 20 | // ShellSort takes a slice of integers and sorts it in place. 21 | func ShellSort(data []int) { 22 | interval := (len(data) / 2) + 1 23 | 24 | for interval > 0 { 25 | InsertionSort(data, interval) 26 | interval = (interval - 1) / 3 27 | } 28 | } 29 | 30 | // Swap takes two indices and switches the values between them. 31 | func Swap(data []int, i, j int) { 32 | data[i], data[j] = data[j], data[i] 33 | } 34 | -------------------------------------------------------------------------------- /algorithms/sorting/shell/shell_test.go: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | import "testing" 4 | 5 | func TestShellSortWithOneValue(t *testing.T) { 6 | testCase := []int{1} 7 | target := []int{1} 8 | 9 | ShellSort(testCase) 10 | 11 | for i, val := range testCase { 12 | if val != target[i] { 13 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 14 | } 15 | } 16 | } 17 | 18 | func TestShellSortWithMultipleValues(t *testing.T) { 19 | testCase := []int{3, 6, 2, 9, 4, 10} 20 | target := []int{2, 3, 4, 6, 9, 10} 21 | 22 | ShellSort(testCase) 23 | 24 | for i, val := range testCase { 25 | if val != target[i] { 26 | t.Errorf("Slice values don't match, testCase is %d target is %d", testCase[i], target[i]) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /algorithms/string-matching/knuth-morris-pratt/README.md: -------------------------------------------------------------------------------- 1 | # Knuth-Morris-Pratt String Searching Algorithm 2 | 3 | The Knuth-Morris-Pratt (KMP) algorithm is a string searching algorithm which 4 | attempts to find occurrences of a word in text based on information from 5 | previous mismatches. It improves on a brute force approach by not checking 6 | previously matched characters allowing for smart skipping through a set of 7 | text. 8 | 9 | The part that makes this algorithm better than some others is its' computation 10 | of a **partial match table**. This table is used to determine where the next checks 11 | begin in case of a match failure. 12 | 13 | ## The Partial Match Table 14 | 15 | The partial match table, or failure function, is the piece that tells us where 16 | to pick up if a mismatch occurs. Generating a partial match table was pretty 17 | confusing to me until I read [this description](http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/). 18 | I will attempt to summarize this further into my own explanation using the same 19 | pattern he mentions (`ABABABCA`). 20 | 21 | To start, we can look at the pattern like this for an idea about our end 22 | result: 23 | 24 | | Char | A | B | A | B | A | B | C | A | 25 | |:-----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 26 | | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 27 | | Value | ? | ? | ? | ? | ? | ? | ? | ? | 28 | 29 | Looking at the table we can see that we lay out our pattern and match it to the 30 | string index where it resides. The final row of values is the output of our 31 | table and what we use in the search algorithm. 32 | 33 | What's in this value row? 34 | 35 | It's a computation based on proper prefixes and suffixes. The following should 36 | explain what each is: 37 | 38 | - **Proper Prefix:** The characters of a string where one or more is left off 39 | from the end. For example: `d, da` are both prefixes of `dan`. 40 | - **Proper Suffix:** The characters of a string where one or more is left off 41 | from the beginning. For example: `n, an` are both suffixes of `dan`. 42 | 43 | When looking for these things it's important to remember that we will iterate 44 | through the string looking at different indices throughout the process. This 45 | means that if we're looking at index 2 of our pattern in the table above then 46 | we only mean the subpattern `ABA` and not the entire pattern. 47 | 48 | With that in mind our goal for each index is to find the length of the longest 49 | proper prefix that matches a proper suffix in the same subpattern. So when 50 | looking at index 2 and the pattern `ABA` we can conclude that prefixes are `A, 51 | AB` and suffixes are `A, BA`. Immediately we can see that `A` matches in each 52 | and we can assign the value of 1 to this index: 53 | 54 | | Char | A | B | A | B | A | B | C | A | 55 | |:-----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 56 | | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 57 | | Value | ? | ? | 1 | ? | ? | ? | ? | ? | 58 | 59 | Looking at the table, you may be wondering about the values before this match. 60 | Let's look at them quickly. Since the first character will always have the same 61 | prefix and suffix (none) then we will usually assign it a value of -1. For the 62 | second index we see there is no match so we can assign it a value of 0. This 63 | will gives us a new table: 64 | 65 | | Char | A | B | A | B | A | B | C | A | 66 | |:-----:|:--:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 67 | | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 68 | | Value | -1 | 0 | 1 | ? | ? | ? | ? | ? | 69 | 70 | We can now move forward in the algorithm looking at index 3 now we can see that 71 | the subpattern is `ABAB` with prefices of `A, AB, ABA` and suffixes of `B, AB, 72 | BAB`. Since `AB` matches both then we can mark this index as a value of 2 since 73 | the length of the match is 2: 74 | 75 | | Char | A | B | A | B | A | B | C | A | 76 | |:-----:|:--:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 77 | | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 78 | | Value | -1 | 0 | 1 | 2 | ? | ? | ? | ? | 79 | 80 | Now moving into index 4 we have the subpattern `ABABA` with prefixes `A, AB, 81 | ABA, ABAB` and suffixes `A, BA, ABA, BABA`. Here we can see two matches which 82 | makes this more interesting. In a case like this we choose the longer prefix 83 | setting the value for index 4 as 3. 84 | 85 | At this point the concept should make sense so we can finish the table and our 86 | result is: 87 | 88 | | Char | A | B | A | B | A | B | C | A | 89 | |:-----:|:--:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 90 | | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 91 | | Value | -1 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 92 | 93 | This is great but how do we achieve this in code? 94 | 95 | The essence will be using two pointers which tell us the current state of the 96 | search. One pointer will move forward through the text incrementing on each 97 | iteration. Out second pointer will always start at the front of the pattern. With 98 | each iteration we check if the first pointer character matches the front of the 99 | pattern. If it does then we have a match and need to see how long the match 100 | runs for. In the case of a mismatch we need to move to the last known match 101 | start so we can check the next substring. 102 | 103 | While this is cofusing to explain in text, the code provided can clarify it a 104 | bit. 105 | 106 | ## The Search Function 107 | 108 | Once we have developed a partial match table we can use this to make our 109 | algorithm more efficient. This efficiency comes from knowing if old comparisons 110 | have already been done and effectively skipping those in favor of fresh 111 | comparisons. 112 | 113 | How does the table help? 114 | 115 | When we find a partial match in the text we continue checking until we find a 116 | mismatch or the pattern is found. In the case of a mismatch we use the partial 117 | match table to decide where we look next. At this point we need to know the 118 | length of the partial match that we found. We use this (minus 1 for indexing) 119 | and that finds us the correct partial match entry. We can then move forward 120 | `partial match length - table[partial match length - 1]`. Why this value? With 121 | the partial match we need to ensure that we don't skip any other potential 122 | matches. By looking up the value in our table we can see how many potential 123 | matches we have up to this point. Subtracting that value from the actual 124 | partial match should bring us to a safe place to continue the search. 125 | 126 | With this I find that it can be harder to explain everything when the code 127 | itself does a good job of giving understanding. 128 | 129 | 130 | ## Time Complexity 131 | 132 | | Case | Complexity | 133 | | --------- |:-----------:| 134 | | Worst | `O(2n)` | 135 | | Best | `O(n)` | 136 | | Average | `O(n + m)` | 137 | -------------------------------------------------------------------------------- /algorithms/string-matching/knuth-morris-pratt/kmp.go: -------------------------------------------------------------------------------- 1 | package kmp 2 | 3 | func InitializeFailureTable(needle string, table *[]int) { 4 | var curPos, nextCandidate int 5 | 6 | // Dereference table pointer for convenience 7 | T := (*table) 8 | 9 | curPos, nextCandidate = 2, 0 10 | T[0], T[1] = -1, 0 11 | 12 | for curPos < len(needle) { 13 | if needle[curPos-1] == needle[nextCandidate] { 14 | T[curPos] = nextCandidate + 1 15 | nextCandidate++ 16 | curPos++ 17 | } else if nextCandidate > 0 { 18 | nextCandidate = T[nextCandidate] 19 | } else { 20 | T[curPos] = 0 21 | curPos++ 22 | } 23 | } 24 | } 25 | 26 | func KMP(haystack, needle string) int { 27 | var T []int 28 | var curMatchIdx, curPatternIdx int 29 | 30 | // Initialize FailureTable 31 | T = make([]int, len(needle)) 32 | InitializeFailureTable(needle, &T) 33 | 34 | // curMatchIdx is current match start in the haystack 35 | curMatchIdx = 0 36 | 37 | // curPatternIdx is the current index of the pattern we're looking for 38 | curPatternIdx = 0 39 | 40 | for curMatchIdx+curPatternIdx < len(haystack) { 41 | if needle[curPatternIdx] == haystack[curMatchIdx+curPatternIdx] { 42 | if curPatternIdx == len(needle)-1 { 43 | return curMatchIdx 44 | } 45 | curPatternIdx++ 46 | } else { 47 | if T[curPatternIdx] > -1 { 48 | curMatchIdx = curMatchIdx + curPatternIdx - T[curPatternIdx] 49 | curPatternIdx = T[curPatternIdx] 50 | } else { 51 | curPatternIdx = 0 52 | curMatchIdx++ 53 | } 54 | } 55 | } 56 | return -1 57 | } 58 | -------------------------------------------------------------------------------- /algorithms/string-matching/knuth-morris-pratt/kmp_test.go: -------------------------------------------------------------------------------- 1 | package kmp 2 | 3 | import "testing" 4 | 5 | type TestData struct { 6 | Text, Pattern string 7 | Expected int 8 | } 9 | 10 | func TestIt(t *testing.T) { 11 | testInput := []TestData{ 12 | TestData{ 13 | Text: "ABACAABACABACAB", 14 | Pattern: "ABACAB", 15 | Expected: 5, 16 | }, 17 | TestData{ 18 | Text: "ABCDABD", 19 | Pattern: "ABD", 20 | Expected: 4, 21 | }, 22 | TestData{ 23 | Text: "BACBABABAABCBAB", 24 | Pattern: "ABABABCA", 25 | Expected: -1, 26 | }, 27 | } 28 | 29 | for _, input := range testInput { 30 | result := KMP(input.Text, input.Pattern) 31 | if result != input.Expected { 32 | t.Errorf("Expected index to be %d for pattern '%s' in '%s' found %d", input.Expected, input.Pattern, input.Text, result) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cryptography/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Cryptography can be a bit obscure when dealing with the how and why of 4 | algorithms. I'm hoping to understand in more detail why certain decisions are 5 | being made and how to use these practices to strengthen my own knowledge. The 6 | basis of this are the terms used and below I'll try and outline the terms I 7 | have come across that could use proper explanation. 8 | 9 | ## Types of Ciphers 10 | 11 | The following are descriptions of the types of ciphers used in cryptography. 12 | 13 | ### Symmetric Key 14 | 15 | The basis of symmetric key encryption is that the same key is used for both 16 | encryption and decryption. It is considered a "shared secret" that two or more 17 | parties share to exchange information. Of course, sharing a secret has its own 18 | set of problems and therefore is considered the main drawback of these 19 | algorithms. 20 | 21 | There are two main types of ciphers used in this category: stream ciphers and 22 | block ciphers. 23 | 24 | #### Stream Ciphers 25 | 26 | Stream ciphers combine the bits of the plaintext with the bits of a 27 | pseudorandom generator one at a time creating a ciphertext. Typically these 28 | algorithms maintain an internal state since each byte of ciphertext depends on 29 | the previous bit dubbing this a **state cipher**. In practice we use 30 | exclusive-or (XOR) to combine the keystream bytes with the plaintext bytes to 31 | develop a ciphertext. 32 | 33 | #### Block Ciphers 34 | 35 | Block ciphers work on a fixed-length group of bits (blocks) instead of going 36 | one by one. The plaintext is then padded so it is a multiple of the block size 37 | selected. This typically comes to 64 bits but AES actually uses 128 bits. 38 | 39 | ### Public Key 40 | 41 | Public key encryption (also known as asymmetric key encryption) uses a pair of 42 | keys: public and private keys. The public key can be passed out but the private 43 | key belongs only to the owner. In this way, anyone can encrypt a message with a 44 | public key but only the proper private key may decrypt the ciphertext. The best 45 | example that I can think of is SSH when using the RSA key pairs. The owner has 46 | the private key locally and place their public key on the server. When SSHing 47 | into a server the user's public key is looked up and matches up with the 48 | private key allowing access to the server. 49 | 50 | ## Terms 51 | 52 | The following are terms I have come across that could use explanation. 53 | 54 | ### State Cipher 55 | 56 | A state cipher is one where each round of encryption is dependent on the 57 | previous round. Stream ciphers typically have an internal state. 58 | 59 | ### Keystream 60 | 61 | A keystream is a stream of random or pseudorandom characters that are combined 62 | with a plaintext message to produce an encrypted message (the ciphertext). 63 | Usually each character in the keystream is either added, subtracted or XORed 64 | with a character in the plaintext to produce the ciphertext. 65 | 66 | ### Pseudorandom 67 | 68 | A pseudorandom process is a process that appears to be random but is not. 69 | Pseudorandom sequences typically show statistical randomness while being 70 | generated by an entirely deterministic process. Such a process is easier 71 | to produce than a genuinely random one, and has the benefit that it can be used 72 | again and again to produce exactly the same numbers - useful for testing and 73 | fixing software. 74 | -------------------------------------------------------------------------------- /cryptography/symmetric-key/stream-ciphers/rc4/README.md: -------------------------------------------------------------------------------- 1 | # RC4 Cryptographic Algorithm 2 | 3 | The RC4 algorithm today is not a secure form of cryptography but it does 4 | provide a good way to learn about some cryptographic ideas. In its simplest 5 | description, it generates a pseudorandom stream of bytes which can be used to 6 | encrypt plaintext into a ciphertext. 7 | 8 | When starting with RC4 we first must initialize a state. The state has two 9 | parts to it: 10 | 11 | - A permutation of 256 possible bits 12 | - Two 8-bit index pointers 13 | 14 | The permutation is initialized with a key typically between 40 and 256 bits. 15 | We use a **key-scheduling** algorithm to randomize the bits through 256 16 | swaps. We can then use those shuffled bits in a **pseudo-random generation algorithm**. 17 | The end result is a keystream that can encrypt each bit of the plaintext into a 18 | ciphertext. 19 | 20 | ## Key Scheduling Portion 21 | 22 | Key scheduling helps us setup our permutation of 256 bit pool based on a 23 | given key. We first start by initializing our array: 24 | 25 | ``` 26 | var S [256]int 27 | for i := 0; i < 256; i++ { 28 | s[i] = i 29 | } 30 | ``` 31 | 32 | Once initialized then we can send the result through 256 rounds of iteration. 33 | Here is where we randomize the bytes based on the key size. We do this like so: 34 | 35 | ``` 36 | j := 0 37 | for i := 0; i < 256; i++ { 38 | j = (j + key[i % key_length] + S[i]) % 256 39 | S[i], S[j] = S[j], S[i] 40 | } 41 | ``` 42 | 43 | We can see that for each index `i` in `S[0..255]` we determine an index `j` to 44 | swap places with. This swap index is based on the previous state 45 | plus the bit from the key located at `i % key_length` plus our permutation 46 | index at `i`. We then make sure it's a valid index in `S` by getting a modulo 47 | of 256 (our bits length). 48 | 49 | Why like this? 50 | 51 | Using the key we can make a reproducible randomization of our bits. If we were 52 | to initialize the cipher again with the same key then we would get the same 53 | result making is predictable for reproducing a keystream. Each different key 54 | will give us different permutations leading to a different keystream. 55 | 56 | ## Pseudo-Random Generation Portion 57 | 58 | Once we have our pool of randomized bits we can use it to develop a 59 | keystream. The keystream is what we use to encrypt plaintext. In a typical 60 | implementation (and mine) we will generate a bit for each bit of the 61 | plaintext. We can do this like: 62 | 63 | ``` 64 | i, j = 0, 0 65 | for v := range src { 66 | i = (i + 1) % 255 67 | j = (j + S[i]) % 255 68 | S[i], S[j] = S[j], S[i] 69 | output S[(S[i] + S[j]) % 255] 70 | ``` 71 | 72 | We first initialize our indices to 0 for a fresh start. We then find our first 73 | index `i` by incrementing it by one (and a modulo by 255 to not overflow the 74 | 8-bit integer size). We then determine the index `j` by adding the bit located 75 | at `S[i]` to j. We swap those values in the bit pool and then find the random 76 | bit from the pool located at `(S[i] + S[j]) % 255`. This random bit is a 77 | value of our keystream. 78 | 79 | At this point we can XOR our source bits with this value of the keystream and get 80 | the first resulting byte of our ciphertext. 81 | 82 | We continue this until we have generated all of our keystream or XORed the 83 | entire source string. 84 | 85 | ## An Example 86 | 87 | It may help to see an example of the process. In this example we'll use the 88 | secret key of `hello` and a plaintext of `Attack at dawn`. 89 | 90 | We first create our bit pool of `S[0..255]`. We then begin key scheduling by 91 | starting our swap loop. 92 | 93 | **First Iteration** 94 | 95 | ``` 96 | i = 0 97 | j = 0 98 | 99 | j = (j + key[i % key_length] + S[i]) % 255 100 | j = (0 + key[0 % 5] + 0) % 255 101 | j = (0 + "h" + 0) % 255 102 | j = 104 % 255 103 | j = 104 104 | ``` 105 | 106 | Above I showed how the equation is solved in each step. The one thing to note 107 | here is that we had to convert `h`, our key value at index 0, to its ordinal 108 | value (104). Once we have j we then do a swap in our bits for i and j (0 and 109 | 104). Since all of our bits are in order we simply switch `S[0]` with `S[104]` 110 | which is 0 for 104. 111 | 112 | **Second Iteration** 113 | ``` 114 | i = 1 115 | j = 104 116 | 117 | j = (j + key[i % key_length] + S[i]) % 255 118 | j = (104 + key[1 % 5] + 1) % 255 119 | j = (104 + "e" + 1) % 255 120 | j = 206 % 255 121 | j = 206 122 | ``` 123 | 124 | Like before, we can swap `S[1]` with `S[206]` and move to the next round. The 125 | thing to pay attention to here is that we are actively using the previous 126 | state for each swap. The process continues until we have swapped all values of 127 | our bit pool. The result is: 128 | 129 | ``` 130 | [ 131 | 124 227 177 171 214 169 219 105 38 82 199 13 175 40 192 119 145 136 114 17 66 132 | 123 56 170 68 165 158 67 146 95 163 76 178 233 193 247 108 118 71 190 57 153 133 | 180 198 236 129 16 39 187 113 245 186 84 22 166 230 131 29 53 120 226 151 88 134 | 217 142 121 185 215 181 157 51 221 222 126 220 81 116 101 42 8 9 250 23 194 112 135 | 252 49 211 137 246 83 94 69 182 183 4 160 87 74 109 11 152 106 132 239 238 111 136 | 34 18 197 164 33 5 26 65 240 46 0 75 79 231 130 62 30 104 36 195 32 167 73 31 137 | 243 191 54 1 248 25 241 91 41 86 122 7 52 93 205 162 223 154 2 140 212 156 6 138 | 128 103 45 148 207 147 15 110 133 55 204 48 43 196 203 139 27 107 210 14 242 139 | 224 125 60 96 19 47 144 202 174 117 253 141 235 208 206 150 188 77 209 200 90 140 | 63 216 234 24 143 173 20 35 232 44 10 3 72 99 78 70 155 201 21 102 58 159 213 141 | 37 218 254 255 161 127 168 179 189 251 80 244 184 237 64 115 89 100 28 59 249 142 | 176 172 135 134 97 12 50 92 229 85 228 61 149 138 225 98 143 | ] 144 | ``` 145 | 146 | We now move into the pseudorandom generator portion. Since our plaintext is 147 | 14 characters long then we will need a keystream of 14 bits to encrypt with. 148 | 149 | **First Iteration** 150 | 151 | Our algorithm looks like the following: 152 | 153 | ``` 154 | i = (i + 1) % 255 155 | j = (j + S[i]) % 255 156 | 157 | S[i], S[j] = S[j], S[i] 158 | 159 | S[(S[i] + S[j]) % 255] 160 | ``` 161 | 162 | Now filling in those solved values we get: 163 | 164 | ``` 165 | i = 1 166 | j = 227 167 | 168 | S[1], S[227] = S[227], S[1] 169 | 170 | S[(189 + 227) % 255] 171 | S[161] 172 | 110 173 | ``` 174 | 175 | Now we see the first bit of the keystream is 110. If we want to get the first 176 | bit of the ciphertext then we convert the first bit of the plaintext to its 177 | ordinal value (65) and XOR it against the first bit of the keystream (110) 178 | giving us 47! This is the first bit of the ciphertext. 179 | 180 | We do the same process until our plaintext has been encrypted. Our keysteam 181 | ends up being: 182 | 183 | ``` 184 | [110, 116, 54, 23, 142, 168, 123, 4, 93, 199, 230, 53, 207, 136] 185 | ``` 186 | 187 | Our ciphertext is: 188 | 189 | ``` 190 | [47, 0, 66, 118, 237, 195, 91, 101, 41, 231, 130, 84, 184, 230] 191 | ``` 192 | 193 | Let's test that we're doing this right. With XOR we can encrypt the plaintext 194 | but we can also decrypt it. If we XOR each bit of the keystream against the 195 | corresponding bit of the ciphertext we can see if the decryption works: 196 | 197 | ``` 198 | 47 ^ 110 = 65 = A 199 | 0 ^ 116 = 116 = t 200 | 66 ^ 54 = 116 = t 201 | 118 ^ 23 = 97 = a 202 | 237 ^ 142 = 99 = c 203 | 195 ^ 168 = 107 = k 204 | 91 ^ 123 = 32 = 205 | 101 ^ 4 = 97 = a 206 | 41 ^ 93 = 116 = t 207 | 231 ^ 199 = 32 = 208 | 130 ^ 230 = 100 = d 209 | 84 ^ 53 = 97 = a 210 | 184 ^ 207 = 119 = w 211 | 230 ^ 136 = 110 = n 212 | ``` 213 | 214 | Looks like it worked. Hopefully that clarifies exactly how it works. 215 | 216 | ## More Information 217 | 218 | While this algorithm has been exploited in a number of ways as not truly secure 219 | (WEP is based on this) it does have some upside. The speed and simplicity of it 220 | makes it very favorable in a number of applications. It can be made efficient 221 | in both hardware and software. 222 | 223 | ## Cipher Detail 224 | 225 | | Details | Notes | 226 | | --------- |-------------| 227 | | **Key Sizes** | 40 - 2048 bits | 228 | | **State Sizes** | 2064 bits (1684 Effective) | 229 | | **Rounds** | 1 | 230 | | **Speed** | 7 Cycles per byte on original Pentium | 231 | -------------------------------------------------------------------------------- /cryptography/symmetric-key/stream-ciphers/rc4/rc4.go: -------------------------------------------------------------------------------- 1 | package rc4 2 | 3 | // CipherState holds the information about the RC4 Instance 4 | type CipherState struct { 5 | // S is a permutation of 256 possible bytes 6 | S [256]int 7 | // Index pointers for indexing the possible bytes 8 | i, j uint8 9 | } 10 | 11 | // MakeRC4 Returns a new RC4 Instance with the key initiated for excryption 12 | func MakeRC4(key []byte) *CipherState { 13 | var c CipherState 14 | 15 | l := len(key) 16 | 17 | // First we build the possible bytes with every number in range 0..256 18 | for i := 0; i < 256; i++ { 19 | c.S[i] = i 20 | } 21 | 22 | // This is known as key-scheduling where we take the key and over 256 23 | // iterations we swap bytes to reach a pseudorandom shuffle of bytes. 24 | var j uint8 = 0 25 | for i := 0; i < 256; i++ { 26 | j = (j + uint8(key[i%l]) + uint8(c.S[i])) % 255 27 | c.S[i], c.S[j] = c.S[j], c.S[i] 28 | } 29 | return &c 30 | } 31 | 32 | // Encrypt takes in a sequence of bytes and returns a ciphertext of said bytes 33 | func (c *CipherState) Encrypt(src []byte) []byte { 34 | result := make([]byte, len(src)) 35 | i, j := c.i, c.j 36 | 37 | // We loop for the length of the source bytes to get xor each byte 38 | for k, v := range src { 39 | i = (i + 1) % 255 40 | j = (j + uint8(c.S[i])) % 255 41 | c.S[i], c.S[j] = c.S[j], c.S[i] 42 | 43 | result[k] = v ^ uint8(c.S[(c.S[i]+c.S[j])%255]) 44 | } 45 | c.i, c.j = i, j 46 | return result 47 | } 48 | -------------------------------------------------------------------------------- /cryptography/symmetric-key/stream-ciphers/rc4/rc4_test.go: -------------------------------------------------------------------------------- 1 | package rc4 2 | 3 | import "testing" 4 | 5 | func TestAlg(t *testing.T) { 6 | var secret, plaintext, encryptedOutput []byte 7 | 8 | secret = []byte("Secret") 9 | plaintext = []byte("Attack at dawn") 10 | encryptedOutput = []byte{135, 128, 194, 160, 112, 180, 75, 155, 220, 52, 116, 4, 157, 192} 11 | 12 | cipher := MakeRC4(secret) 13 | 14 | for i, b := range cipher.Encrypt(plaintext) { 15 | if b != encryptedOutput[i] { 16 | t.Errorf("Ciphertext bit at %d (%d) does not match expected bit %d", i, b, encryptedOutput[i]) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data-structures/binary-search-tree/README.md: -------------------------------------------------------------------------------- 1 | # Binary Search Tree 2 | 3 | A binary search tree (BST) is a data structure which allows fast lookups and 4 | addition and removal of items. A BST's keys remain in sorted order so binary 5 | search can be used when traversing a tree. Since we can skip different roots 6 | of the tree, traversal is much faster than an unsorted array. 7 | 8 | Every BST starts with a root node. Root nodes point to left and right subtrees 9 | with the property that nodes with values less than the root are stored to the 10 | left and nodes with values greater than the root are to the right. For 11 | example: 12 | 13 | ``` 14 | [ 5 ] 15 | / \ 16 | / \ 17 | [ 3 ] [ 10 ] 18 | ``` 19 | 20 | **Insertion** 21 | 22 | When inserting nodes in a BST, we need to remember to always follow the 23 | principle of "smaller values on the left, larger on the right". Using images, 24 | let's start with an empty BST and insert a value of `5`: 25 | 26 | ``` 27 | [ 5 ] 28 | ``` 29 | 30 | This value is now our root node since the BST was previously empty. If we want 31 | to add another value such as `10`, we would do so by starting at the root node. 32 | We compare the value at the root node (`5`) to the new node value (`10`) and 33 | see that our new node is greater so we add it to the right subtree: 34 | 35 | ``` 36 | [ 5 ] 37 | \ 38 | \ 39 | [ 10 ] 40 | ``` 41 | 42 | This represents the BST property and shows why searching for values is quick. 43 | Since we know that values to the right are always greater than the current 44 | node, we can continue to look down the right subtree to find our node. 45 | 46 | If we want to add another node such as `3` then we would compare `3 < 5` and 47 | see that we would add it to the left subtree like so: 48 | 49 | ``` 50 | [ 5 ] 51 | / \ 52 | / \ 53 | [ 3 ] [ 10 ] 54 | ``` 55 | 56 | As a last look, let's add a few nodes and see how they would be arranged. 57 | We're going to add `1`, `8`, and `20`: 58 | 59 | ``` 60 | [ 5 ] 61 | / \ 62 | / \ 63 | [ 3 ] [ 10 ] 64 | / / \ 65 | / / \ 66 | [ 1 ] [ 8 ] [ 20 ] 67 | ``` 68 | 69 | As we can see, if we wanted to find `20` it would only take 3 iterations where 70 | a normal sorted array would take 6 iterations. 71 | 72 | **Deletion** 73 | 74 | When deleting, we have three cases to consider in order to keep the integrity 75 | of the BST: 76 | 77 | - Nodes with two children 78 | - Nodes with on child 79 | - Nodes with no children 80 | 81 | Obviously, the most complex deletion involves two children so I'll start with 82 | the easiest: no children. If we were to delete `1` from the final tree above 83 | then our tree would look like: 84 | 85 | ``` 86 | [ 5 ] 87 | / \ 88 | / \ 89 | [ 3 ] [ 10 ] 90 | / \ 91 | / \ 92 | [ 8 ] [ 20 ] 93 | ``` 94 | 95 | Since deleting `1` does not affect any children, all we have to do is update 96 | the parent node's (`3`) left subtree to nil. 97 | 98 | Using the initial tree again, we can see what deleting `3` would simulate a 99 | node having only one child. In this case, we check to see which subtree is the 100 | child. In our case, it would be the left where `1` exists. So deleting `3` 101 | would then need to link `1` to the root node `5` giving us: 102 | 103 | ``` 104 | [ 5 ] 105 | / \ 106 | / \ 107 | [ 1 ] [ 10 ] 108 | / \ 109 | / \ 110 | [ 8 ] [ 20 ] 111 | ``` 112 | 113 | Using the initial tree again, we can see that deleting `10` would simulate a 114 | node having both children. With this, we first must find the smallest node 115 | that exists in the right subtree. For us, this is `20`. We now can update node 116 | `10` to be `20` while preserving the link to the root and the left subtree. We 117 | then make sure we go down the right subtree to delete the exchanged value (`20`). 118 | Our tree would now look like: 119 | 120 | ``` 121 | [ 5 ] 122 | / \ 123 | / \ 124 | [ 3 ] [ 20 ] 125 | / / 126 | / / 127 | [ 1 ] [ 8 ] 128 | ``` 129 | 130 | As we can see, our BST property remains in tact this way. 131 | 132 | **Traversing** 133 | 134 | When traversing a BST, there are three main methods used: 135 | 136 | - In Order Traversal: This is proper sorted order. It will recurse down the 137 | left subtree first, then the root of that tree, and then the right. 138 | - Pre Order Traversal: This is a way to get the root nodes first. For our tree 139 | it would give us `5, 3, 1, 10, 8, 20`. 140 | - Post Order Traversal: This is a way to get the left and right nodes first. 141 | For our tree it would give us `1, 3, 8, 20, 10, 5`. 142 | 143 | ## Use Cases 144 | 145 | Binary Search Trees are good for a number of cases but at its worst case, it's 146 | no better than a linked list (where all nodes are greater). If you plan on 147 | incrementally adding and deleting values though, a BST is a great data 148 | structure to keep that sequence sorted at all times. 149 | 150 | Another good use case of a BST is the basis of a min or max heap. Since we can 151 | easily recurse subtrees in logarithmic time then we can remove the min or max 152 | value to satisfy the heap. 153 | 154 | A deque is another data structure which, like the heap, makes a good candidate 155 | for an underlying BST. 156 | 157 | ## Time Complexity 158 | 159 | | Case | Average | Worst Case | 160 | | --------- |:-------------:|:-------------:| 161 | | Space | `O(n)` | `O(n)` | 162 | | Search | `O(log n)` | `O(n)` | 163 | | Insert | `O(log n)` | `O(n)` | 164 | | Delete | `O(log n)` | `O(n)` | 165 | -------------------------------------------------------------------------------- /data-structures/binary-search-tree/bst.go: -------------------------------------------------------------------------------- 1 | package bst 2 | 3 | import "fmt" 4 | 5 | type Node struct { 6 | Val int 7 | Parent, Left, Right *Node 8 | } 9 | 10 | // BST is a binary search tree beginning with a root node. 11 | type BST struct { 12 | root *Node 13 | size int 14 | } 15 | 16 | // NewBST creates an empty instance of a binary search tree with. 17 | func NewBST() *BST { 18 | return &BST{size: 0} 19 | } 20 | 21 | // Cardinality returns the current size of the BST. 22 | func (bst *BST) Cardinality() int { 23 | return bst.size 24 | } 25 | 26 | // IsEmpty checks the cardinality of the BST for 0. 27 | func (bst *BST) IsEmpty() bool { 28 | return bst.Cardinality() == 0 29 | } 30 | 31 | // Root returns the root node of a BST. 32 | func (bst *BST) Root() *Node { 33 | return bst.root 34 | } 35 | 36 | // String satisfies the println interface allowing us to print a 37 | // representation of the tree. 38 | // 39 | // This is borrowed from https://github.com/emirpasic/gods/blob/master/trees/redblacktree/redblacktree.go 40 | func (bst *BST) String() string { 41 | str := "BST\n" 42 | if !bst.IsEmpty() { 43 | output(bst.Root(), "", true, &str) 44 | } 45 | return str 46 | } 47 | 48 | // output is the recursive print tree function borrowed from: 49 | // https://github.com/emirpasic/gods/blob/master/trees/redblacktree/redblacktree.go 50 | func output(node *Node, prefix string, isTail bool, str *string) { 51 | if node.Right != nil { 52 | newPrefix := prefix 53 | if isTail { 54 | newPrefix += "| " 55 | } else { 56 | newPrefix += " " 57 | } 58 | output(node.Right, newPrefix, false, str) 59 | } 60 | *str += prefix 61 | if isTail { 62 | *str += "└── " 63 | } else { 64 | *str += "┌── " 65 | } 66 | *str += fmt.Sprintf("%v", node.Val) + "\n" 67 | if node.Left != nil { 68 | newPrefix := prefix 69 | if isTail { 70 | newPrefix += " " 71 | } else { 72 | newPrefix += "| " 73 | } 74 | output(node.Left, newPrefix, true, str) 75 | } 76 | } 77 | 78 | // insert is an internal recursive function which does the work required to 79 | // properly place a new node in a BST. It compares values of the new node to 80 | // be inserted with the current node. If the new value is less than the 81 | // current value and the left subtree is not empty, we recurse into that left 82 | // subtree. Otherwise, we set the new node on that left subtree. The same goes 83 | // for the right subtree for values greater than the current. 84 | func (bst *BST) insert(curNode, newNode *Node) *Node { 85 | if bst.IsEmpty() { 86 | bst.root = newNode 87 | return newNode 88 | } else if newNode.Val < curNode.Val { 89 | if curNode.Left == nil { 90 | curNode.Left = newNode 91 | newNode.Parent = curNode 92 | return newNode 93 | } 94 | return bst.insert(curNode.Left, newNode) 95 | } else if newNode.Val > curNode.Val { 96 | if curNode.Right == nil { 97 | curNode.Right = newNode 98 | newNode.Parent = curNode 99 | return newNode 100 | } 101 | return bst.insert(curNode.Right, newNode) 102 | } 103 | return nil 104 | } 105 | 106 | // Insert is the entry to the recursive insert function. If we have an empty 107 | // BST, we set the root node. Otherwise, we recurse through the tree comparing 108 | // nodes until it is placed. Unless the new value is a duplicate, it will be 109 | // placed. 110 | func (bst *BST) Insert(val int) *Node { 111 | if inserted := bst.insert(bst.Root(), &Node{Val: val}); inserted != nil { 112 | bst.size++ 113 | return inserted 114 | } 115 | return nil 116 | } 117 | 118 | // link is a supplementry function to deleting a node. Based on the node being 119 | // deleted, it sets the parents left or right subtree root as the new node. 120 | // This allows us to properly remove a node and keep the child nodes under a 121 | // deleted node. 122 | func (bst *BST) link(curNode, newNode *Node) { 123 | if curNode.Parent != nil { 124 | if curNode == curNode.Parent.Left { 125 | curNode.Parent.Left = newNode 126 | } else { 127 | curNode.Parent.Right = newNode 128 | } 129 | } 130 | if newNode != nil { 131 | curNode = curNode.Parent 132 | } 133 | } 134 | 135 | // del is the recursive supplementry function used when deleting a node. It 136 | // checks to see if the values are less than, greater than, or equal to each 137 | // other. If less than, we have to go deeper down the left subtree. If greater 138 | // than, we go deeper down the right subtree. If they are equal, we determine 139 | // whether the node has two children, one child, or no children. In the case 140 | // of two children, we must find the minimum node in the right subtree and set 141 | // that as the new parent to the child subtrees. We then must delete this 142 | // value from its location in the child tree. If there is only one child, we 143 | // accordingly link the parent node to this child to keep the connection. If 144 | // it has no children, we can safely remove this node. 145 | func (bst *BST) del(node *Node, val int) bool { 146 | switch { 147 | case node == nil: 148 | return false 149 | case val < node.Val: 150 | return bst.del(node.Left, val) 151 | case val > node.Val: 152 | return bst.del(node.Right, val) 153 | case val == node.Val: 154 | if node.Left != nil && node.Right != nil { 155 | // Handle node with two children 156 | next := node.Right 157 | for next.Left != nil { 158 | next = next.Left 159 | } 160 | node.Val = next.Val 161 | bst.del(next, next.Val) 162 | } else if node.Left != nil { 163 | // Handle node with only left child 164 | bst.link(node, node.Left) 165 | } else if node.Right != nil { 166 | // Handle node with only right child 167 | bst.link(node, node.Right) 168 | } else { 169 | // Handle node with no children 170 | bst.link(node, nil) 171 | } 172 | return true 173 | } 174 | return false 175 | } 176 | 177 | // Delete is the entry point to deleting a node on a BST. If the BST is empty, 178 | // we don't have anything to delete. If it's the root node, we need to find 179 | // the new root node. In any other case, we recurse down the left and right 180 | // subtrees to see if the value exists to delete. 181 | func (bst *BST) Delete(val int) bool { 182 | if bst.IsEmpty() { 183 | return false 184 | } else if bst.Root().Val == val && bst.del(bst.Root(), val) { 185 | bst.size-- 186 | return true 187 | } else if bst.del(bst.Root().Left, val) || bst.del(bst.Root().Right, val) { 188 | bst.size-- 189 | return true 190 | } 191 | return false 192 | } 193 | 194 | // Iter creates a channel used for iterating through a BST and using each 195 | // value for the current node. 196 | func (bst *BST) Iter(t Traversable) chan int { 197 | ch := make(chan int) 198 | go func() { 199 | t.Traverse(bst.Root(), ch) 200 | close(ch) 201 | }() 202 | return ch 203 | } 204 | -------------------------------------------------------------------------------- /data-structures/binary-search-tree/bst_test.go: -------------------------------------------------------------------------------- 1 | package bst 2 | 3 | import "testing" 4 | 5 | func TestEmptyBST(t *testing.T) { 6 | bst := NewBST() 7 | 8 | if !bst.IsEmpty() { 9 | t.Errorf("BST should be empty") 10 | } 11 | } 12 | 13 | func TestSingleNodeBST(t *testing.T) { 14 | bst := NewBST() 15 | bst.Insert(1) 16 | 17 | if bst.IsEmpty() { 18 | t.Errorf("BST should be NOT empty") 19 | } 20 | 21 | if bst.Cardinality() != 1 { 22 | t.Errorf("BST should have 1 item, has %d", bst.Cardinality()) 23 | } 24 | 25 | if bst.Root().Val != 1 { 26 | t.Errorf("Root node value should be equal to 1, is %d", bst.Root().Val) 27 | } 28 | } 29 | 30 | func TestBSTSubtrees(t *testing.T) { 31 | bst := NewBST() 32 | bst.Insert(10) 33 | bst.Insert(5) 34 | bst.Insert(20) 35 | 36 | if bst.Cardinality() != 3 { 37 | t.Errorf("BST should have 3 items, has %d", bst.Cardinality()) 38 | } 39 | 40 | if bst.Root().Val != 10 { 41 | t.Errorf("Root node value should be equal to 10, is %d", bst.Root().Val) 42 | } 43 | 44 | if bst.Root().Left.Val != 5 { 45 | t.Errorf("Root node value should be equal to 5, is %d", bst.Root().Left.Val) 46 | } 47 | 48 | if bst.Root().Right.Val != 20 { 49 | t.Errorf("Root node value should be equal to 20, is %d", bst.Root().Right.Val) 50 | } 51 | } 52 | 53 | func TestBSTDoesNotAllowDuplicates(t *testing.T) { 54 | bst := NewBST() 55 | bst.Insert(10) 56 | res := bst.Insert(10) 57 | 58 | if res != nil { 59 | t.Errorf("Added duplicate but should not have") 60 | } 61 | 62 | if bst.Cardinality() != 1 { 63 | t.Errorf("BST should have 1 item, has %d", bst.Cardinality()) 64 | } 65 | } 66 | 67 | func TestEmptyBSTCannotDelete(t *testing.T) { 68 | bst := NewBST() 69 | res := bst.Delete(1) 70 | 71 | if res { 72 | t.Errorf("Deleted from an empty BST") 73 | } 74 | } 75 | 76 | func TestBSTCanDeleteRoot(t *testing.T) { 77 | bst := NewBST() 78 | bst.Insert(1) 79 | bst.Insert(2) 80 | bst.Insert(0) 81 | res := bst.Delete(1) 82 | 83 | if !res { 84 | t.Errorf("Tried to delete root but could not") 85 | } 86 | 87 | if bst.Cardinality() != 2 { 88 | t.Errorf("BST should have 2 items, has %d", bst.Cardinality()) 89 | } 90 | 91 | if bst.Root().Val != 2 { 92 | t.Errorf("New root node should be 2, is %d", bst.Root().Val) 93 | } 94 | 95 | if bst.Root().Left.Val != 0 { 96 | t.Errorf("New root node's left node should be 0, is %d", bst.Root().Left.Val) 97 | } 98 | 99 | if bst.Root().Right != nil { 100 | t.Errorf("New root node's right node should nil") 101 | } 102 | } 103 | 104 | func TestBSTCanDeleteParentWithTwoChildren(t *testing.T) { 105 | bst := NewBST() 106 | bst.Insert(1) 107 | bst.Insert(5) 108 | bst.Insert(0) 109 | bst.Insert(2) 110 | bst.Insert(6) 111 | res := bst.Delete(5) 112 | 113 | if !res { 114 | t.Errorf("Tried to delete 5 but could not") 115 | } 116 | 117 | if bst.Cardinality() != 4 { 118 | t.Errorf("BST should have 4 items, has %d", bst.Cardinality()) 119 | } 120 | 121 | if bst.Root().Right.Val != 6 { 122 | t.Errorf("New node should be 6, is %d", bst.Root().Right.Val) 123 | } 124 | 125 | if bst.Root().Right.Left.Val != 2 { 126 | t.Errorf("New node's left node should be 2, is %d", bst.Root().Right.Left.Val) 127 | } 128 | 129 | if bst.Root().Right.Right != nil { 130 | t.Errorf("New node's right node should be nil") 131 | } 132 | } 133 | 134 | func TestBSTCanDeleteParentWithOneChild(t *testing.T) { 135 | bst := NewBST() 136 | bst.Insert(1) 137 | bst.Insert(5) 138 | bst.Insert(0) 139 | bst.Insert(2) 140 | res := bst.Delete(5) 141 | 142 | if !res { 143 | t.Errorf("Tried to delete 5 but could not") 144 | } 145 | 146 | if bst.Cardinality() != 3 { 147 | t.Errorf("BST should have 3 items, has %d", bst.Cardinality()) 148 | } 149 | 150 | if bst.Root().Right.Val != 2 { 151 | t.Errorf("New node should be 2, is %d", bst.Root().Right.Val) 152 | } 153 | 154 | if bst.Root().Right.Left != nil { 155 | t.Errorf("New node's left node should be nil") 156 | } 157 | 158 | if bst.Root().Right.Right != nil { 159 | t.Errorf("New node's right node should be nil") 160 | } 161 | } 162 | 163 | func TestBSTCanDeleteParentWithNoChildren(t *testing.T) { 164 | bst := NewBST() 165 | bst.Insert(1) 166 | bst.Insert(5) 167 | bst.Insert(0) 168 | res := bst.Delete(0) 169 | 170 | if !res { 171 | t.Errorf("Tried to delete 0 but could not") 172 | } 173 | 174 | if bst.Cardinality() != 2 { 175 | t.Errorf("BST should have 2 items, has %d", bst.Cardinality()) 176 | } 177 | 178 | if bst.Root().Left != nil { 179 | t.Errorf("New node's left node should be nil") 180 | } 181 | 182 | if bst.Root().Right == nil { 183 | t.Errorf("New node's right node should not be nil") 184 | } 185 | } 186 | 187 | func TestBSTCanIterInOrder(t *testing.T) { 188 | bst := NewBST() 189 | bst.Insert(1) 190 | bst.Insert(5) 191 | bst.Insert(0) 192 | 193 | expected := []int{0, 1, 5} 194 | 195 | idx := 0 196 | for e := range bst.Iter(InOrder) { 197 | if e != expected[idx] { 198 | t.Errorf("InOrder iteration returned %d should have been %d", e, expected[idx]) 199 | } 200 | idx++ 201 | } 202 | } 203 | 204 | func TestBSTCanIterPreOrder(t *testing.T) { 205 | bst := NewBST() 206 | bst.Insert(1) 207 | bst.Insert(5) 208 | bst.Insert(0) 209 | 210 | expected := []int{1, 0, 5} 211 | 212 | idx := 0 213 | for e := range bst.Iter(PreOrder) { 214 | if e != expected[idx] { 215 | t.Errorf("PreOrder iteration returned %d should have been %d", e, expected[idx]) 216 | } 217 | idx++ 218 | } 219 | } 220 | 221 | func TestBSTCanIterPostOrder(t *testing.T) { 222 | bst := NewBST() 223 | bst.Insert(1) 224 | bst.Insert(5) 225 | bst.Insert(0) 226 | 227 | expected := []int{0, 5, 1} 228 | 229 | idx := 0 230 | for e := range bst.Iter(PostOrder) { 231 | if e != expected[idx] { 232 | t.Errorf("PostOrder iteration returned %d should have been %d", e, expected[idx]) 233 | } 234 | idx++ 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /data-structures/binary-search-tree/traverse.go: -------------------------------------------------------------------------------- 1 | package bst 2 | 3 | // Traversable is an interface to define a method for traversing through a BST. 4 | type Traversable interface { 5 | Traverse(*Node, chan int) 6 | } 7 | 8 | // InOrderTraversable is an empty struct used to handle the methods of the 9 | // Traversable interface. 10 | type InOrderTraversable struct{} 11 | 12 | // InOrder is a valid type used to specify the iteration method. 13 | var InOrder InOrderTraversable 14 | 15 | // Traverse satisfies the Traversable interface sending values back on the 16 | // channel. This one if for in-order traversing. 17 | func (bst InOrderTraversable) Traverse(node *Node, ch chan int) { 18 | if node != nil { 19 | bst.Traverse(node.Left, ch) 20 | ch <- node.Val 21 | bst.Traverse(node.Right, ch) 22 | } 23 | } 24 | 25 | // PreOrderTraversable is an empty struct used to handle the methods of the 26 | // Traversable interface. 27 | type PreOrderTraversable struct{} 28 | 29 | // PreOrder is a valid type used to specify the iteration method. 30 | var PreOrder PreOrderTraversable 31 | 32 | // Traverse satisfies the Traversable interface sending values back on the 33 | // channel. This one if for pre-order traversing. 34 | func (bst PreOrderTraversable) Traverse(node *Node, ch chan int) { 35 | if node != nil { 36 | ch <- node.Val 37 | bst.Traverse(node.Left, ch) 38 | bst.Traverse(node.Right, ch) 39 | } 40 | } 41 | 42 | // PostOrderTraversable is an empty struct used to handle the methods of the 43 | // Traversable interface. 44 | type PostOrderTraversable struct{} 45 | 46 | // PostOrder is a valid type used to specify the iteration method. 47 | var PostOrder PostOrderTraversable 48 | 49 | // Traverse satisfies the Traversable interface sending values back on the 50 | // channel. This one if for post-order traversing. 51 | func (bst PostOrderTraversable) Traverse(node *Node, ch chan int) { 52 | if node != nil { 53 | bst.Traverse(node.Left, ch) 54 | bst.Traverse(node.Right, ch) 55 | ch <- node.Val 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /data-structures/deque/README.md: -------------------------------------------------------------------------------- 1 | # Deque 2 | 3 | A deque or a "double-ended queue" is an abstact data type which works much 4 | like a queue except we can perform operations on either end of the queue. 5 | These operations include appending, prepending, popping, and popping left. 6 | 7 | In this implementation, I'm using a circular doubly linked list as the 8 | container. 9 | 10 | ## Use Cases 11 | 12 | A deque is funny in that it doesn't have a concrete usage as a normal queue or 13 | stack can be used in a lot of the places where this is needed. The one real 14 | world usage of this pattern is mentioned in this [wiki article](https://en.wikipedia.org/wiki/Double-ended_queue#Applications). 15 | 16 | ## Time Complexity 17 | 18 | | Case | Complexity | 19 | | --------- |:-----------:| 20 | | All | `O(1)` | 21 | -------------------------------------------------------------------------------- /data-structures/deque/deque.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import list "../linked-list" 4 | 5 | // Deque holds the items. 6 | type Deque struct { 7 | Items *list.List 8 | } 9 | 10 | // NewDeque returns an empty deque. 11 | func NewDeque() *Deque { 12 | return &Deque{Items: list.New()} 13 | } 14 | 15 | // Append pushes an item to the back of the deque. 16 | func (q *Deque) Append(val interface{}) { 17 | q.Items.Append(val) 18 | } 19 | 20 | // Prepend pushes an item to the front of the deque. 21 | func (q *Deque) Prepend(val interface{}) { 22 | q.Items.Prepend(val) 23 | } 24 | 25 | // Pop pulls an item off the back of the deque. 26 | func (q *Deque) Pop() interface{} { 27 | return q.Items.Pop().Value 28 | } 29 | 30 | // PopLeft pulls and item off the front of the deque. 31 | func (q *Deque) PopLeft() interface{} { 32 | return q.Items.PopLeft().Value 33 | } 34 | 35 | // IsEmpty checks if there are any items currently in the deque. 36 | func (q *Deque) IsEmpty() bool { 37 | if q.Size() > 0 { 38 | return false 39 | } 40 | return true 41 | } 42 | 43 | // Size checks the length of the deque. 44 | func (q *Deque) Size() int { 45 | return q.Items.Size() 46 | } 47 | -------------------------------------------------------------------------------- /data-structures/deque/deque_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import "testing" 4 | 5 | func TestInitialDequeIsEmpty(t *testing.T) { 6 | deque := NewDeque() 7 | 8 | if !deque.IsEmpty() { 9 | t.Errorf("Deque is supposed to be empty") 10 | } 11 | } 12 | 13 | func TestDequeWorks(t *testing.T) { 14 | deque := NewDeque() 15 | // This should give us [8, 5, 1] 16 | deque.Append(5) 17 | deque.Prepend(8) 18 | deque.Append(1) 19 | 20 | if deque.Size() != 3 { 21 | t.Errorf("Deque should have 3 items in it, it has %d", deque.Size()) 22 | } 23 | 24 | first := deque.Pop() 25 | 26 | if deque.Size() != 2 { 27 | t.Errorf("Deque should have 2 items in it, it has %d", deque.Size()) 28 | } 29 | 30 | if first != 1 { 31 | t.Errorf("First value should be 1, it returned %d", first) 32 | } 33 | 34 | second := deque.PopLeft() 35 | third := deque.Pop() 36 | 37 | if !deque.IsEmpty() { 38 | t.Errorf("Deque is supposed to be empty") 39 | } 40 | 41 | if second != 8 { 42 | t.Errorf("Second value should be 8, it returned %d", second) 43 | } 44 | 45 | if third != 5 { 46 | t.Errorf("Third value should be 5, it returned %d", third) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /data-structures/graph/README.md: -------------------------------------------------------------------------------- 1 | # Graph 2 | 3 | A graph is a container which holds vertices or nodes. These vertices can be 4 | connected by different edges and when we have these edges, we can traverse 5 | a graph finding the shortest path to different vertices as well as creating 6 | lines between vertices. Graphs can be directed or undirected. The 7 | difference being that a directed graph has edges that go one way and 8 | undirected graphs can go both backwards and forwards along the edge. 9 | 10 | In the following code, I have implemented a undirected graph. As a handy 11 | way to view the graph and use it, I have provided two functions to return 12 | it: 13 | 14 | - `ToAdjacencyList()` 15 | - `ToAdjacencyMatrix()` 16 | 17 | An Adjacency list has vertices stored as records or objects, and every 18 | vertex stores a list of adjacent vertices. This data structure allows the 19 | storage of additional data on the vertices. Additional data can be stored 20 | if edges are also stored as objects, in which case each vertex stores its 21 | incident edges and each edge stores its incident vertices. An example is: 22 | 23 | ``` 24 | [1] 25 | [0 2 3] 26 | [1] 27 | [1] 28 | ``` 29 | 30 | An Adjacency Matrix is a two-dimensional matrix, in which the rows represent 31 | source vertices and columns represent destination vertices. Data on edges 32 | and vertices must be stored externally. Only the cost for one edge can be 33 | stored between each pair of vertices. An example is: 34 | 35 | 36 | ``` 37 | [0 1 0 0] 38 | [1 0 1 1] 39 | [0 1 0 0] 40 | [0 1 0 0] 41 | ``` 42 | 43 | Adjacency lists are generally preferred because they efficiently represent 44 | sparse graphs. An adjacency matrix is preferred if the graph is dense, that 45 | is the number of edges is close to the number of vertices squared or if one 46 | must be able to quickly look up if there is an edge connecting two vertices. 47 | 48 | ## Time Complexity 49 | 50 | | Operations | Adjacency List | Adjacency Matrix | 51 | | ------------- |:-----------------:|:-----------------:| 52 | | Store Graph | `O(|V|+|E|)` | `O(|V|^2)` | 53 | | Add Vertex | `O(1)` | `O(|V|^2)` | 54 | | Add Edge | `O(1)` | `O(1)` | 55 | | Remove Vertex | `O(|E|)` | `O(|V|^2)` | 56 | | Remove Edge | `O(|E|)` | `O(1)` | 57 | -------------------------------------------------------------------------------- /data-structures/graph/graph.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // Vertex represents a single node in a graph. 4 | type Vertex struct { 5 | // Value in our implementation is just an integer for readability 6 | Value int 7 | // Graph allows us to make sure the vertices are in the same graph 8 | Graph *Graph 9 | // Neighbors stores edges between two vertices 10 | Neighbors []*Vertex 11 | } 12 | 13 | // GetNeighbors is a convenient function to return the neighboring vertices 14 | func (v *Vertex) GetNeighbors() []*Vertex { 15 | return v.Neighbors 16 | } 17 | 18 | // IsAdjacent checks if the current vertex has an edge with another 19 | func (v *Vertex) IsAdjacent(other *Vertex) bool { 20 | if v.Graph != other.Graph { 21 | return false 22 | } 23 | 24 | for _, n := range v.Neighbors { 25 | if n == other { 26 | return true 27 | } 28 | } 29 | 30 | return false 31 | } 32 | 33 | // RemoveEdge removes a neighboring vertex from its list 34 | func (v *Vertex) RemoveEdge(other *Vertex) bool { 35 | if v.Graph != other.Graph { 36 | return false 37 | } 38 | 39 | if !v.IsAdjacent(other) { 40 | return false 41 | } 42 | 43 | updateNeighbors := func(cur, target *Vertex) []*Vertex { 44 | var removeIndex int 45 | for i, n := range cur.Neighbors { 46 | if n == target { 47 | removeIndex = i 48 | } 49 | } 50 | 51 | if len(cur.Neighbors) == 1 { 52 | return []*Vertex{} 53 | } 54 | return append(cur.Neighbors[:removeIndex], cur.Neighbors[removeIndex+1:]...) 55 | } 56 | 57 | v.Neighbors = updateNeighbors(v, other) 58 | other.Neighbors = updateNeighbors(other, v) 59 | return true 60 | } 61 | 62 | // Graph is a container which holds a list of vertices within it 63 | type Graph struct { 64 | Vertices []*Vertex 65 | } 66 | 67 | // NewGraph is a convenience method to instantiate a new graph object 68 | func NewGraph() *Graph { 69 | return &Graph{} 70 | } 71 | 72 | // Adjacent checks if two vertices are adjacent to each other 73 | func (g *Graph) Adjacent(first, second *Vertex) bool { 74 | if first.Graph != g || second.Graph != g { 75 | return false 76 | } 77 | return first.IsAdjacent(second) 78 | } 79 | 80 | // Edges returns an edge list showing pairs of vertices that connect 81 | func (g *Graph) Edges() [][]int { 82 | var edges [][]int 83 | 84 | for _, v := range g.Vertices { 85 | for _, n := range v.Neighbors { 86 | edges = append(edges, []int{v.Value, n.Value}) 87 | } 88 | } 89 | 90 | return edges 91 | } 92 | 93 | // AddVertex inserts a new vertex into the graph returning that new vertex 94 | func (g *Graph) AddVertex(value int) *Vertex { 95 | v := &Vertex{Value: value, Graph: g} 96 | g.Vertices = append(g.Vertices, v) 97 | return v 98 | } 99 | 100 | // RemoveVertex removes a given vertex from a graph and its neighbor relations 101 | func (g *Graph) RemoveVertex(v *Vertex) bool { 102 | var removeIndex int 103 | 104 | if v.Graph != g { 105 | return false 106 | } 107 | 108 | for i, vertex := range g.Vertices { 109 | if vertex == v { 110 | removeIndex = i 111 | for _, neighbor := range vertex.Neighbors { 112 | neighbor.RemoveEdge(v) 113 | } 114 | } 115 | } 116 | 117 | g.Vertices = append(g.Vertices[:removeIndex], g.Vertices[removeIndex+1:]...) 118 | 119 | return true 120 | } 121 | 122 | // AddEdge creates an edge between two vertices 123 | func (g *Graph) AddEdge(first, second *Vertex) { 124 | if first.Graph == second.Graph && !first.IsAdjacent(second) { 125 | second.Neighbors = append(second.Neighbors, first) 126 | first.Neighbors = append(first.Neighbors, second) 127 | } 128 | } 129 | 130 | // RemoveEdge removes an edge between two vertices if it exists 131 | func (g *Graph) RemoveEdge(first, second *Vertex) { 132 | if first.Graph == second.Graph && first.IsAdjacent(second) { 133 | first.RemoveEdge(second) 134 | } 135 | } 136 | 137 | // GetVertex searches for a vertex based on the value 138 | func (g *Graph) GetVertex(value int) *Vertex { 139 | for _, v := range g.Vertices { 140 | if v.Value == value { 141 | return v 142 | } 143 | } 144 | return nil 145 | } 146 | 147 | // ToAdjacencyList returns a graph represented as an Adjacency List 148 | func (g *Graph) ToAdjacencyList() [][]int { 149 | list := make([][]int, len(g.Vertices)) 150 | 151 | for i, v := range g.Vertices { 152 | for _, n := range v.Neighbors { 153 | list[i] = append(list[i], n.Value) 154 | } 155 | } 156 | 157 | return list 158 | } 159 | 160 | // ToAdjacencyMatrix returns a graph represented as an Adjacency Matrix 161 | func (g *Graph) ToAdjacencyMatrix() [][]int { 162 | matrix := make([][]int, len(g.Vertices)) 163 | 164 | for i, _ := range g.Vertices { 165 | row := make([]int, len(g.Vertices)) 166 | for j, _ := range g.Vertices { 167 | row[j] = 0 168 | } 169 | matrix[i] = row 170 | } 171 | 172 | for _, edge := range g.Edges() { 173 | matrix[edge[0]][edge[1]] = 1 174 | } 175 | 176 | return matrix 177 | } 178 | -------------------------------------------------------------------------------- /data-structures/graph/graph_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "testing" 4 | 5 | func TestGraphCanHaveOneVertex(t *testing.T) { 6 | graph := NewGraph() 7 | vertex := graph.AddVertex(0) 8 | 9 | if vertex.Value != 0 { 10 | t.Errorf("Vertex value should be 0, found %d", vertex.Value) 11 | } 12 | 13 | if len(graph.Vertices) != 1 { 14 | t.Errorf("Graph length should be 1, found %d", len(graph.Vertices)) 15 | } 16 | 17 | if graph.GetVertex(0) != vertex { 18 | t.Errorf("Did not get single vertex") 19 | } 20 | } 21 | 22 | func TestVertexOperations(t *testing.T) { 23 | graph := NewGraph() 24 | v1 := graph.AddVertex(0) 25 | v2 := graph.AddVertex(1) 26 | v3 := graph.AddVertex(2) 27 | v4 := graph.AddVertex(3) 28 | 29 | graph.AddEdge(v1, v2) 30 | graph.AddEdge(v2, v3) 31 | graph.AddEdge(v2, v4) 32 | 33 | // Test neighbors 34 | if len(v1.GetNeighbors()) != 1 { 35 | t.Errorf("First vertex should only have one neighbor") 36 | } 37 | 38 | if len(v2.GetNeighbors()) != 3 { 39 | t.Errorf("Second vertex should have three neighbors") 40 | } 41 | 42 | if len(v3.GetNeighbors()) != 1 { 43 | t.Errorf("Second vertex should only have one neighbor") 44 | } 45 | 46 | // Test Adjacency 47 | if v1.IsAdjacent(v2) != true { 48 | t.Errorf("Vertex 1 and vertex 2 should be adjacent") 49 | } 50 | 51 | if v1.IsAdjacent(v3) != false { 52 | t.Errorf("Vertex 1 and vertex 2 should be adjacent") 53 | } 54 | 55 | // Test can remove an edge 56 | v2.RemoveEdge(v1) 57 | 58 | if len(v1.GetNeighbors()) != 0 { 59 | t.Errorf("Vertex 1 should have no neighbors after removal") 60 | } 61 | 62 | if v2.IsAdjacent(v1) { 63 | t.Errorf("Vertex 2 should not be adjacent to vertex 1") 64 | } 65 | } 66 | 67 | func TestVertexOperationsOnDifferentGraphs(t *testing.T) { 68 | graph1 := NewGraph() 69 | graph2 := NewGraph() 70 | v1 := graph1.AddVertex(0) 71 | v2 := graph1.AddVertex(1) 72 | v3 := graph2.AddVertex(2) 73 | v4 := graph2.AddVertex(3) 74 | 75 | graph1.AddEdge(v1, v2) 76 | graph2.AddEdge(v3, v4) 77 | 78 | // Test Adjacency 79 | if v1.IsAdjacent(v3) != false { 80 | t.Errorf("Vertex 1 and vertex 3 are in different graphs and should not be adjacent") 81 | } 82 | 83 | // Test cannot remove an edge 84 | if v1.RemoveEdge(v3) != false { 85 | t.Errorf("Vertex 1 and vertex 3 are in different graphs and should not have an edge") 86 | } 87 | } 88 | 89 | func TestGraphCanFindAdjacentVertices(t *testing.T) { 90 | graph := NewGraph() 91 | v1 := graph.AddVertex(0) 92 | v2 := graph.AddVertex(1) 93 | 94 | graph.AddEdge(v1, v2) 95 | 96 | if graph.Adjacent(v1, v2) != true { 97 | t.Errorf("Vertex 1 and vertex 2 should be adjacent") 98 | } 99 | 100 | graph2 := NewGraph() 101 | v3 := graph2.AddVertex(0) 102 | v4 := graph2.AddVertex(1) 103 | 104 | graph2.AddEdge(v3, v4) 105 | 106 | if graph.Adjacent(v1, v3) != false { 107 | t.Errorf("Vertex 1 and vertex 2 are in different graphs and cannot be adjacent") 108 | } 109 | } 110 | 111 | func TestGraphCanGetEdgeList(t *testing.T) { 112 | graph := NewGraph() 113 | v1 := graph.AddVertex(0) 114 | v2 := graph.AddVertex(1) 115 | v3 := graph.AddVertex(2) 116 | v4 := graph.AddVertex(3) 117 | 118 | graph.AddEdge(v1, v2) 119 | graph.AddEdge(v2, v3) 120 | graph.AddEdge(v2, v4) 121 | 122 | edges := graph.Edges() 123 | 124 | if len(edges) != 6 { 125 | t.Errorf("There should be 6 edge pairs but found %d", len(edges)) 126 | } 127 | } 128 | 129 | func TestGraphCanRemoveVertex(t *testing.T) { 130 | graph := NewGraph() 131 | v1 := graph.AddVertex(0) 132 | v2 := graph.AddVertex(1) 133 | v3 := graph.AddVertex(2) 134 | v4 := graph.AddVertex(3) 135 | 136 | graph.AddEdge(v1, v2) 137 | graph.AddEdge(v2, v3) 138 | graph.AddEdge(v2, v4) 139 | 140 | graph.RemoveVertex(v2) 141 | 142 | if len(graph.Vertices) != 3 { 143 | t.Errorf("Graph should have 3 vertices after removal, not %d", len(graph.Vertices)) 144 | } 145 | 146 | if len(v1.GetNeighbors()) != 0 { 147 | t.Errorf("Vertex 1 should have no neighbors after removal, not %d", len(v1.GetNeighbors())) 148 | } 149 | 150 | if len(v3.GetNeighbors()) != 1 { 151 | t.Errorf("Vertex 3 should have 1 neighbor after removal, not %d", len(v3.GetNeighbors())) 152 | } 153 | 154 | if len(v4.GetNeighbors()) != 0 { 155 | t.Errorf("Vertex 4 should have no neighbors after removal, not %d", len(v4.GetNeighbors())) 156 | } 157 | 158 | if graph.GetVertex(1) != nil { 159 | t.Errorf("Should not be able to get removed vertex") 160 | } 161 | } 162 | 163 | func TestGraphCanManipulateEdges(t *testing.T) { 164 | graph := NewGraph() 165 | v1 := graph.AddVertex(0) 166 | v2 := graph.AddVertex(1) 167 | v3 := graph.AddVertex(2) 168 | v4 := graph.AddVertex(3) 169 | 170 | graph.AddEdge(v1, v2) 171 | graph.AddEdge(v2, v3) 172 | graph.AddEdge(v2, v4) 173 | 174 | graph.RemoveEdge(v1, v2) 175 | 176 | if len(v1.GetNeighbors()) != 0 { 177 | t.Errorf("Vertex 1 should have no neighbors after removal, not %d", len(v1.GetNeighbors())) 178 | } 179 | 180 | if len(v2.GetNeighbors()) != 2 { 181 | t.Errorf("Vertex 2 should have two neighbors after removal, not %d", len(v2.GetNeighbors())) 182 | } 183 | } 184 | 185 | func TestGraphCanBeRepresentation(t *testing.T) { 186 | graph := NewGraph() 187 | v1 := graph.AddVertex(0) 188 | v2 := graph.AddVertex(1) 189 | v3 := graph.AddVertex(2) 190 | v4 := graph.AddVertex(3) 191 | 192 | graph.AddEdge(v1, v2) 193 | graph.AddEdge(v2, v3) 194 | graph.AddEdge(v2, v4) 195 | 196 | // Test Adjacency List 197 | list := graph.ToAdjacencyList() 198 | targetList := [][]int{ 199 | []int{1}, 200 | []int{0, 2, 3}, 201 | []int{1}, 202 | []int{1}, 203 | } 204 | 205 | if len(list) != 4 { 206 | t.Errorf("The adjacency list should have 4 vertices") 207 | } 208 | 209 | for i, row := range list { 210 | for j, item := range row { 211 | if item != targetList[i][j] { 212 | t.Errorf("Adjacency point at [%d, %d] should be %d but found $d", i, j, targetList[i][j], item) 213 | } 214 | } 215 | } 216 | 217 | // Test Adjacency Matrix 218 | matrix := graph.ToAdjacencyMatrix() 219 | targetMatrix := [][]int{ 220 | []int{0, 1, 0, 0}, 221 | []int{1, 0, 1, 1}, 222 | []int{0, 1, 0, 0}, 223 | []int{0, 1, 0, 0}, 224 | } 225 | 226 | if len(matrix) != 4 { 227 | t.Errorf("The adjacency matrix should have 4 vertices") 228 | } 229 | 230 | for i, row := range matrix { 231 | for j, item := range row { 232 | if item != targetMatrix[i][j] { 233 | t.Errorf("Adjacency point at [%d, %d] should be %d but found $d", i, j, targetMatrix[i][j], item) 234 | } 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /data-structures/hash-table/README.md: -------------------------------------------------------------------------------- 1 | # Hash Table 2 | 3 | A hash table is a data structure which maps keys to values. It uses a hash 4 | function which finds an index based on the key and stores the value in a 5 | "bucket" or "slot". In an ideal situation, each bucket will only contain one 6 | value but this is not always the case. In the instance of a collision two keys 7 | can reside in the same bucket which can ultimately degrade the hash table 8 | performance to O(n) at worst case. 9 | 10 | When building a hash table it's important to maintain a good balance between 11 | the number of entries and number of buckets used. We can determine this "load 12 | factor" by dividing the number of entries by the number of buckets. A larger 13 | load factor points to a slower hash table. 14 | 15 | ## Hash Functions 16 | 17 | Having a good hash function is incredibly important to a hash table. Hash 18 | functions determine how to distribute values into the hash table and a poor 19 | distribution will lead to fast degradation. 20 | 21 | A common pattern in hashing will follow the formula `index = f(key, array_size)`. 22 | 23 | Our function `f()` then could be: 24 | 25 | ``` 26 | hash = hashfunc(key) 27 | index = hash % array_size 28 | ``` 29 | 30 | This concept allows the hash to be independent of the size of the underlying 31 | array. 32 | 33 | It's incredibly hard to have a perfect hash function without knowledge of the 34 | input but in the case that all keys are known ahead of time it is possible to 35 | build a "perfect hash function" which has no collisions. 36 | 37 | ## Collision Resolution 38 | 39 | Collisions are almost guaranteed to happen in a hash table with an unknown set 40 | of keys. Luckily there are a number of strategies that can be implemented in 41 | order to mitigate collisions. 42 | 43 | ### Separate Chaining 44 | 45 | Separate chaining is the process of storing matching keys in a list-type data 46 | structure. In this case finding the bucket can be done in constant time and 47 | finding the correct key is dependent on the structure used within. It should 48 | be said that the structure you use shouldn't be one that expects to store a 49 | large number of items. If your bucket sizes are that large then there is 50 | something wrong with the hash function you're using and should evaluate there 51 | first. 52 | 53 | A linked list is a common structure for storing these values. They can be used 54 | to traverse the bucket based on linked-list complexity times. While there is 55 | some overhead storing a pointer to the next node they still are a valid option 56 | for handling this chaining. 57 | 58 | Balanced binary search trees can be another good option but the needs of the 59 | system should be thought about first. While this will give the bucket an O(lg n) 60 | lookup time it may take more memory for that guarantee. 61 | 62 | ### Open Addressing 63 | 64 | Open addressing stores all entries in the bucket array itself and inserts new 65 | entries based on a probing sequence. It then uses this same probing sequence 66 | when searching for entries later. Some of the well known probing sequences 67 | include: 68 | 69 | - Linear Probing: Interval between probes is fixes (usually 1) 70 | - Quadratic probing: Interval between probes is increased by adding the 71 | successive outputs of a quadratic polynomial to the starting value given 72 | by the original hash computation 73 | - Double hashing: Interval between probes is computed by a second hash function 74 | 75 | Unfortunately this approach may require dynamic resizing since a growing load 76 | factor makes this almost unusable. On the other hand it decreases the time 77 | needed to allocate each new entry. 78 | 79 | ## Dyanamic Resizing 80 | 81 | Dynamic Resizing is the act of resizing the hash table itself as the load 82 | factor grows. In a lot of applications, a loadfactor of 2/3 is a good point to 83 | consider a resize so the hash table retains it's optimized state. Since 84 | inserts are the only time a table becomes larger the resizing would be run at 85 | this stage. Since changing the size of the underlying array will result in 86 | hashes changing, resizing also includes rehashing or rebuilding the existing 87 | hashes. 88 | 89 | In many cases the entire table can be copied to a newly allocated array and 90 | still perform well enough. In other cases such as real-time systems though 91 | this penalty can be costly and an incremental approach needs to be taken. This 92 | approach may follow a pattern: 93 | 94 | - Allocate a new hash table and keep the old table 95 | - Inserts will be placed in the new hash table 96 | - Lookups will check both tables as they exist 97 | - After an insert, a set number of entries will be copied to the new hash 98 | table 99 | - Deallocate old hash table once all entries have been copied over 100 | 101 | ## Use Cases 102 | 103 | Because most cases of the hash table have such fast lookups, hash tables are 104 | used incredibly often. Some of the common use cases for a hash table may be an 105 | associative array, database indexing, caches, and sets. 106 | 107 | ## Time Complexity 108 | 109 | | Case | Average | Worst Case | 110 | | --------- |:-------------:|:-------------:| 111 | | Space | `O(n)` | `O(n)` | 112 | | Search | `O(1)` | `O(n)` | 113 | | Insert | `O(1)` | `O(n)` | 114 | | Delete | `O(1)` | `O(n)` | 115 | -------------------------------------------------------------------------------- /data-structures/hash-table/hash_table.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | const ( 4 | // MaxLoadFactor is the ratio of entries to buckets which will force a resize 5 | MaxLoadFactor float64 = .75 6 | // DefaultTableSize is set higher to avoid many resizes 7 | DefaultTableSize uint64 = 128 8 | // SizeIncrease is the resize multiple when determining the new size of an 9 | // updated hash table 10 | SizeIncrease uint64 = 2 11 | // FNVOffset is the constant used in the FNV hash function as seen on 12 | // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function 13 | FNVOffset uint64 = 14695981039346656037 14 | // FNVPrime is the constant prime factor used in the FNV hash function 15 | FNVPrime uint64 = 1099511628211 16 | ) 17 | 18 | // HashTable is the primary data structure for storing our hash table. We keep 19 | // implicit counts of buckets and entries with buckets being singly linked 20 | // lists for separate chaining. 21 | type HashTable struct { 22 | Buckets []*List 23 | NumEntries, NumBuckets uint64 24 | } 25 | 26 | // NewHashTable creates a hash table based on the default sizing constraints. 27 | func NewHashTable() *HashTable { 28 | return &HashTable{ 29 | Buckets: make([]*List, DefaultTableSize), 30 | NumBuckets: DefaultTableSize, 31 | NumEntries: 0, 32 | } 33 | } 34 | 35 | // resize rehashes a hash table which has grown beyond its load factor 36 | // bringing it back to a more efficient state. 37 | func (t *HashTable) resize() { 38 | newBucketSize := t.NumBuckets * SizeIncrease 39 | ht := &HashTable{ 40 | Buckets: make([]*List, newBucketSize), 41 | NumBuckets: newBucketSize, 42 | NumEntries: 0, 43 | } 44 | for _, list := range t.Buckets { 45 | if list != nil { 46 | for e := range list.Iter() { 47 | ht.Put(e.key, e.val) 48 | } 49 | } 50 | } 51 | *t = *ht 52 | } 53 | 54 | // LoadFactor returns the current load of the table. 55 | func (t *HashTable) LoadFactor() float64 { 56 | return float64(t.NumEntries) / float64(t.NumBuckets) 57 | } 58 | 59 | // Hash handles hashing the key based on the FNV hash function. More can be 60 | // read about it here: 61 | // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function 62 | func (t *HashTable) Hash(s string) uint64 { 63 | var hash uint64 64 | bts := []byte(s) 65 | hash = FNVOffset 66 | for _, b := range bts { 67 | hash *= FNVPrime 68 | hash ^= uint64(b) 69 | } 70 | return hash 71 | } 72 | 73 | // Put attempts to insert a new key/value pair or update an existing key with 74 | // the new value. 75 | func (t *HashTable) Put(key string, val interface{}) { 76 | if t.LoadFactor() > MaxLoadFactor { 77 | t.resize() 78 | } 79 | index := t.Hash(key) % uint64(t.NumBuckets) 80 | if t.Buckets[index] == nil { 81 | l := NewList() 82 | l.Append(key, val) 83 | t.Buckets[index] = l 84 | t.NumEntries++ 85 | return 86 | } 87 | for n := range t.Buckets[index].Iter() { 88 | if n.key == key { 89 | n.val = val 90 | return 91 | } 92 | } 93 | t.Buckets[index].Append(key, val) 94 | t.NumEntries++ 95 | } 96 | 97 | // Get attempts to find the value based on a key. 98 | func (t *HashTable) Get(key string) interface{} { 99 | index := t.Hash(key) % uint64(t.NumBuckets) 100 | if t.Buckets[index] == nil { 101 | return nil 102 | } 103 | for n := range t.Buckets[index].Iter() { 104 | if n.key == key { 105 | return n.val 106 | } 107 | } 108 | return nil 109 | } 110 | 111 | // Del attempts to delete a key/value pair from the hash table. 112 | func (t *HashTable) Del(key string) { 113 | index := t.Hash(key) % uint64(t.NumBuckets) 114 | if t.Buckets[index] == nil { 115 | return 116 | } 117 | t.Buckets[index].Remove(key) 118 | t.NumEntries-- 119 | } 120 | -------------------------------------------------------------------------------- /data-structures/hash-table/hash_table_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestCanInitialize(t *testing.T) { 9 | ht := NewHashTable() 10 | 11 | if ht.NumEntries != 0 { 12 | t.Errorf("Initial hash table should have 0 entries, has %d", ht.NumEntries) 13 | } 14 | 15 | if ht.NumBuckets != 128 { 16 | t.Errorf("Initial hash table should have 128 buckets, has %d", ht.NumBuckets) 17 | } 18 | } 19 | 20 | func TestCanResizeItself(t *testing.T) { 21 | ht := NewHashTable() 22 | 23 | for i := 0; i < 100; i++ { 24 | key := fmt.Sprintf("key_%d", i) 25 | ht.Put(key, i) 26 | } 27 | 28 | if ht.NumBuckets != 256 { 29 | t.Errorf("Resized hash table should have 256 buckets, has %d", ht.NumBuckets) 30 | } 31 | } 32 | 33 | func TestHashFunction(t *testing.T) { 34 | ht := NewHashTable() 35 | 36 | hash := ht.Hash("testing") 37 | 38 | if hash != 18433708127455886847 { 39 | t.Errorf("The hash function doesn't seem correct. Found %d", hash) 40 | } 41 | 42 | if hash%ht.NumBuckets != 127 { 43 | t.Errorf("The table index should be 127. Found %d", hash%ht.NumBuckets) 44 | } 45 | } 46 | 47 | func TestCanPutValue(t *testing.T) { 48 | ht := NewHashTable() 49 | val := "My Value" 50 | 51 | ht.Put("key", val) 52 | 53 | if ht.NumEntries != 1 { 54 | t.Error("Value not inserted correctly") 55 | } 56 | } 57 | 58 | func TestCanGetValue(t *testing.T) { 59 | ht := NewHashTable() 60 | val := "My Value" 61 | 62 | ht.Put("key", val) 63 | 64 | if ht.Get("key") != val { 65 | t.Errorf("Tried to get value '%s' but returned %s", val, ht.Get("key")) 66 | } 67 | } 68 | 69 | func TestCanUpdateValue(t *testing.T) { 70 | ht := NewHashTable() 71 | initialVal := "My Value" 72 | newVal := "NEW Value" 73 | 74 | ht.Put("key", initialVal) 75 | ht.Put("key", newVal) 76 | 77 | if ht.Get("key") != newVal { 78 | t.Errorf("Tried to get value '%s' but returned %s", newVal, ht.Get("key")) 79 | } 80 | 81 | if ht.NumEntries != 1 { 82 | t.Error("Update should not increase the entry count") 83 | } 84 | } 85 | 86 | func TestCanGetValueAfterResize(t *testing.T) { 87 | ht := NewHashTable() 88 | 89 | for i := 0; i < 100; i++ { 90 | key := fmt.Sprintf("key_%d", i) 91 | ht.Put(key, i) 92 | } 93 | 94 | if ht.Get("key_50") != 50 { 95 | t.Errorf("key_50 should return %d after resize, returned %d", 50, ht.Get("key_50")) 96 | } 97 | } 98 | 99 | func TestCanDeleteValue(t *testing.T) { 100 | ht := NewHashTable() 101 | 102 | ht.Put("key", "testing") 103 | ht.Del("key") 104 | 105 | if ht.Get("key") != nil { 106 | t.Errorf("Deleted value should be empty, found %d", ht.Get("key")) 107 | } 108 | 109 | if ht.NumEntries != 0 { 110 | t.Error("Delete should decrease the entry count") 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /data-structures/hash-table/list.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | // Node is a linked-list node 4 | type Node struct { 5 | key string 6 | val interface{} 7 | next *Node 8 | } 9 | 10 | // List is a basic singly linked-list 11 | type List struct { 12 | head, tail *Node 13 | size int 14 | } 15 | 16 | // NewList returns an empty List 17 | func NewList() *List { 18 | return &List{size: 0} 19 | } 20 | 21 | // GetHead returns the head of the list 22 | func (l *List) GetHead() *Node { 23 | return l.head 24 | } 25 | 26 | // GetTail returns the tail of the list 27 | func (l *List) GetTail() *Node { 28 | return l.tail 29 | } 30 | 31 | // GetHead returns the next value from the node 32 | func (n *Node) Next() *Node { 33 | if n == nil { 34 | return nil 35 | } 36 | return n.next 37 | } 38 | 39 | // GetKey returns the node's key 40 | func (n *Node) GetKey() string { 41 | return n.key 42 | } 43 | 44 | // GetVal returns the node's value 45 | func (n *Node) GetVal() interface{} { 46 | return n.val 47 | } 48 | 49 | // Append adds a new value to the end of the list 50 | func (l *List) Append(key string, val interface{}) { 51 | newNode := &Node{key: key, val: val} 52 | if l.size == 0 { 53 | l.head = newNode 54 | l.tail = newNode 55 | } else { 56 | l.tail.next = newNode 57 | l.tail = newNode 58 | } 59 | l.size++ 60 | } 61 | 62 | // Remove removes a value based on its key from the list 63 | func (l *List) Remove(key string) { 64 | if l.size == 0 { 65 | return 66 | } 67 | var cur, prev *Node 68 | for cur, prev = l.head, nil; cur != nil; cur, prev = cur.next, cur { 69 | if cur.key == key { 70 | if cur == l.head { 71 | l.head = l.head.next 72 | } else { 73 | prev.next = cur.next 74 | } 75 | l.size-- 76 | return 77 | } 78 | } 79 | } 80 | 81 | // Iter returns a range-able iterator for easy traversal 82 | func (l *List) Iter() chan *Node { 83 | ch := make(chan *Node) 84 | go func() { 85 | for cur := l.GetHead(); cur != nil; cur = cur.Next() { 86 | ch <- cur 87 | } 88 | close(ch) 89 | }() 90 | return ch 91 | } 92 | -------------------------------------------------------------------------------- /data-structures/heap/README.md: -------------------------------------------------------------------------------- 1 | # Binary Heap 2 | 3 | A heap is a tree-like data structure. I'm going to focus on a binary heap for 4 | now in order to keep it simple. Binary heaps follow the contraints: 5 | 6 | - The binary tree is a complete binary tree with all levels full except for 7 | the last level which is filled from left to right. 8 | - All nodes are either greater than or equal to or less than or equal to each 9 | of its children. 10 | 11 | When talking about a binary heap, it's common to consider both a **Max Heap** and 12 | a **Min Heap**. A max heap ensures that the largest value is at the top of the 13 | heap and can be reached in O(1) time. A min heap is the opposite with the 14 | smallest value at the top of the heap. Both can be implemented the same way 15 | and only differ in the comparison of items when updating the heap. 16 | 17 | One of the nice things about heaps is that they can be handled entirely in an 18 | array reducing the memory cost of pointers. 19 | 20 | Operations on a binary heap will typically be pushing new items onto the heap 21 | or extracting the minimum (or maximum) value from the heap. In both cases we 22 | do core operations and then update the heap property to account for the new or 23 | now missing values. 24 | 25 | For insertion this means appending the new item to the end of the array and 26 | then bubbling upwards checking itself against its parent swapping values until 27 | the root is reached. In this way, it's ensured that the minimum (or maximum) 28 | value always reaches the top of the tree. 29 | 30 | For extraction, this means popping the first item from the array and bubbling 31 | downwards based on the smaller (or larger) child node. A swap can be done in 32 | this direction as well making the heap property satisfied again. 33 | 34 | One important thing to know when bubbling both up and down is how to locate 35 | the children. Since we know that the tree is complete and that each item has 36 | at most two children then we can assume the following: 37 | 38 | ``` 39 | Assuming i = current index 40 | Left Child = 2 * i 41 | Right Child = 2 * i + 1 42 | Parent = floor(i / 2) 43 | ``` 44 | 45 | With these, we can freely traverse the tree correcting the heap properties. 46 | 47 | ## Use Cases 48 | 49 | The heap can be used in a lot of different ways. Some of the more well known 50 | implementations are: 51 | 52 | - Heapsort: Very good in-place sorting algorithm 53 | - Select Algorithms: Cases where you need to find min, max, or kth element are 54 | more efficient with a heap. 55 | - Graph Algorithms: Can be used as a traversal structure within. 56 | - Priority Queue: One of the bigger use cases of a heap, a priority queue 57 | is an abstract data-type which allows for attaching priorities to queued 58 | items. 59 | - Order Statistics: Heaps can help in finding the kth smallest or largest 60 | elements in an array. 61 | 62 | ## Time Complexity 63 | 64 | | Case | Average | Worst Case | 65 | | --------- |:-------------:|:-------------:| 66 | | Space | `O(n)` | `O(n)` | 67 | | Search | `O(n)` | `O(n)` | 68 | | Insert | `O(lg n)` | `O(lg n)` | 69 | | Delete | `O(lg n)` | `O(lg n)` | 70 | -------------------------------------------------------------------------------- /data-structures/heap/heap.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | // Item is a key / value pair for people who want to set the priority and 4 | // arbitrary data. 5 | type Item struct { 6 | Key int 7 | Val interface{} 8 | } 9 | 10 | // NewItem returns a new item instance. 11 | func NewItem(key int, val interface{}) *Item { 12 | return &Item{Key: key, Val: val} 13 | } 14 | 15 | // Heap is our actual heap data structure. You can set a key / value pair with 16 | // the key being the priority. 17 | type Heap struct { 18 | items []*Item 19 | size int 20 | } 21 | 22 | // NewHeap returns a new Heap instance. 23 | func NewHeap() *Heap { 24 | var items []*Item 25 | return &Heap{items: items, size: 0} 26 | } 27 | 28 | // Cardinality returns the size of the queue. 29 | func (h *Heap) Cardinality() int { 30 | return h.size 31 | } 32 | 33 | // IsEmpty returns whether the Heap has items in it. 34 | func (h *Heap) IsEmpty() bool { 35 | return h.Cardinality() == 0 36 | } 37 | 38 | // Swap trades items at specified indices. 39 | func (h *Heap) Swap(i, j int) { 40 | h.items[i], h.items[j] = h.items[j], h.items[i] 41 | } 42 | 43 | // peek is the internal function used to view the next item in the heap. 44 | func (h *Heap) peek() interface{} { 45 | return h.items[0].Val 46 | } 47 | 48 | // Iter returns a channel to iterate through the Heap items. 49 | func (h *Heap) Iter() chan interface{} { 50 | ch := make(chan interface{}) 51 | go func() { 52 | for _, v := range h.items { 53 | ch <- v.Val 54 | } 55 | close(ch) 56 | }() 57 | return ch 58 | } 59 | 60 | // push is the internal function called by the min / max heap to add a new 61 | // item to the heap and then fix the heap properties. 62 | func (h *Heap) push(k int, v interface{}, lessFunc func(i, j int) bool) { 63 | h.items = append(h.items, NewItem(k, v)) 64 | h.size++ 65 | h.up(h.size-1, lessFunc) 66 | } 67 | 68 | // pop is the internal function called by the min / max heap to remove the 69 | // smallest item from the heap and then fix the heap properties. 70 | func (h *Heap) pop(lessFunc func(i, j int) bool) interface{} { 71 | if h.IsEmpty() { 72 | return -1 73 | } 74 | ret := h.items[0] 75 | h.items[0] = h.items[h.size-1] 76 | h.size-- 77 | h.items = h.items[1:] 78 | h.down(0, lessFunc) 79 | return ret.Val 80 | } 81 | 82 | // up moves up the heap from the back of the internal items list resorting 83 | // items as it bubbles up so the heap property is restored. 84 | func (h *Heap) up(i int, lessFunc func(i, j int) bool) { 85 | parent := (i - 1) / 2 86 | if i != parent && lessFunc(i, parent) { 87 | h.Swap(i, parent) 88 | h.up(parent, lessFunc) 89 | } 90 | } 91 | 92 | // down moves down the heap from the initial index resorting items as it 93 | // bubbles down so the heap property is restored. 94 | func (h *Heap) down(i int, lessFunc func(i, j int) bool) { 95 | left := i*2 + 1 96 | right := i*2 + 2 97 | min := i 98 | 99 | if left < h.size && lessFunc(left, i) { 100 | min = left 101 | } else if right < h.size && lessFunc(right, i) { 102 | min = right 103 | } 104 | 105 | if i != min { 106 | h.Swap(i, min) 107 | h.down(min, lessFunc) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /data-structures/heap/heap_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import "testing" 4 | 5 | func TestMinHeap(t *testing.T) { 6 | min := NewMinHeap() 7 | 8 | if !min.IsEmpty() { 9 | t.Error("Initial min heap should be empty") 10 | } 11 | 12 | min.Push(1, "First") 13 | min.Push(3, "Second") 14 | min.Push(20, "Third") 15 | min.Push(2, "Fourth") 16 | 17 | if min.Peek() != "First" { 18 | t.Errorf("Next value should be '%s' not '%s'", "First", min.Peek()) 19 | } 20 | 21 | min.Pop() 22 | min.Pop() 23 | 24 | if min.Peek() != "Second" { 25 | t.Errorf("Next value should be '%s' not '%s'", "Second", min.Peek()) 26 | } 27 | } 28 | 29 | func TestMaxHeap(t *testing.T) { 30 | max := NewMaxHeap() 31 | 32 | if !max.IsEmpty() { 33 | t.Error("Initial max heap should be empty") 34 | } 35 | 36 | max.Push(1, "First") 37 | max.Push(3, "Second") 38 | max.Push(20, "Third") 39 | max.Push(2, "Fourth") 40 | 41 | if max.Peek() != "Third" { 42 | t.Errorf("Next value should be '%s' not '%s'", "Third", max.Peek()) 43 | } 44 | 45 | max.Pop() 46 | max.Pop() 47 | 48 | if max.Peek() != "Fourth" { 49 | t.Errorf("Next value should be '%s' not '%s'", "Second", max.Peek()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /data-structures/heap/max_heap.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | // MaxHeap is a wrapper around a heap and will work the maximum values coming 4 | // off first when popping items. 5 | type MaxHeap struct { 6 | *Heap 7 | } 8 | 9 | // NewMaxHeap returns a new max heap structure. 10 | func NewMaxHeap() *MaxHeap { 11 | return &MaxHeap{NewHeap()} 12 | } 13 | 14 | // Less determines how to compare two keys when heapifying. 15 | func (h *MaxHeap) Less(i, j int) bool { 16 | return h.Heap.items[i].Key > h.Heap.items[j].Key 17 | } 18 | 19 | // Push is a wrapper around the heap's main push method which specifies the Up 20 | // function for the callback. 21 | func (h *MaxHeap) Push(k int, v interface{}) { 22 | h.Heap.push(k, v, h.Less) 23 | } 24 | 25 | // Pop is a wrapper around the heap's main pop method which specifies the Down 26 | // function for the callback. 27 | func (h *MaxHeap) Pop() interface{} { 28 | return h.Heap.pop(h.Less) 29 | } 30 | 31 | // Iter is a wrapper around the heap's Iter method for easy iteration. 32 | func (h *MaxHeap) Iter() chan interface{} { 33 | return h.Heap.Iter() 34 | } 35 | 36 | // Peek returns the next item to be popped off the heap. 37 | func (h *MaxHeap) Peek() interface{} { 38 | return h.Heap.peek() 39 | } 40 | -------------------------------------------------------------------------------- /data-structures/heap/min_heap.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | // MinHeap is a wrapper around a heap and will work the minimum values coming 4 | // off first when popping items. 5 | type MinHeap struct { 6 | *Heap 7 | } 8 | 9 | // NewMinHeap returns a new min heap structure. 10 | func NewMinHeap() *MinHeap { 11 | return &MinHeap{NewHeap()} 12 | } 13 | 14 | // Less determines how to compare two keys when heapifying. 15 | func (h *MinHeap) Less(i, j int) bool { 16 | return h.Heap.items[i].Key < h.Heap.items[j].Key 17 | } 18 | 19 | // Push is a wrapper around the heap's main push method which specifies the Up 20 | // function for the callback. 21 | func (h *MinHeap) Push(k int, v interface{}) { 22 | h.Heap.push(k, v, h.Less) 23 | } 24 | 25 | // Pop is a wrapper around the heap's main pop method which specifies the Down 26 | // function for the callback. 27 | func (h *MinHeap) Pop() interface{} { 28 | return h.Heap.pop(h.Less) 29 | } 30 | 31 | // Iter is a wrapper around the heap's Iter method for easy iteration. 32 | func (h *MinHeap) Iter() chan interface{} { 33 | return h.Heap.Iter() 34 | } 35 | 36 | // Peek returns the next item to be popped off the heap. 37 | func (h *MinHeap) Peek() interface{} { 38 | return h.Heap.peek() 39 | } 40 | -------------------------------------------------------------------------------- /data-structures/linked-list/README.md: -------------------------------------------------------------------------------- 1 | # Linked List 2 | 3 | A linked list is a collection of items, each called a node, which has a 4 | reference pointer to the next node. Together, this group of nodes represents a 5 | sequence. In the simplest form, a node stores both a value and a pointer to 6 | the next node. 7 | 8 | Linked lists are one of the simplest data structures and serves as a good base 9 | to things like stacks, queues, and associative arrays. What makes a linked 10 | list favorable is the ability to insert or delete new nodes without 11 | reorganizing every node in the list. 12 | 13 | The two types of linked lists are: 14 | 15 | - Singly linked lists 16 | - Doubly linked lists 17 | 18 | ## Singly Linked List 19 | 20 | A singly linked list is the most basic form and is described such as: 21 | 22 | ``` 23 | [ 12 ] --> [ 66 ] --> [ 45 ] --> [ X ] 24 | ``` 25 | 26 | As can be seen, each node points to the next node where the last node (tail) 27 | points to a nil reference. 28 | 29 | ## Doubly Linked List 30 | 31 | A doubly linked list is an enhancement of the singly linked list in which it 32 | holds both a reference to the next node and the previous node like so: 33 | 34 | ``` 35 | [ X ] <--> [ 12 ] <--> [ 66 ] <--> [ 45 ] <--> [ X ] 36 | ``` 37 | 38 | This gives us an even better way to operate and traverse the list while using 39 | slightly more memory storing an additional pointer value. 40 | 41 | ## Circular Linked List 42 | 43 | In the provided code, I have created a circular linked list which uses a 44 | doubly linked list as the base but instead of pointing to nil at the head and 45 | tail, they point to a **sentinel value** which is always locked to the 0 46 | index. This allows us to work more efficiently with the list itself. 47 | 48 | ## Use Cases 49 | 50 | A linked list is typically preferrable when: 51 | 52 | - You care about constant time inserts and deletes 53 | - You don't need random access to items in the list 54 | - You want to insert items at any point in the list 55 | 56 | ## Time Complexity 57 | 58 | In most cases, this implementation gives us an O(1) time span. There is no 59 | find() method currently so the only expensive method available is the 60 | ToSlice() method which is O(n) as it must visit each node. 61 | 62 | | Case | Complexity | 63 | | --------- |:-----------:| 64 | | Insert | `O(1)` | 65 | | Delete | `O(1)` | 66 | | Find | `O(n)` | 67 | | Iterate | `O(n)` | 68 | -------------------------------------------------------------------------------- /data-structures/linked-list/linkedlist.go: -------------------------------------------------------------------------------- 1 | package linkedlist 2 | 3 | // Node represents a doubly linked node. As the spec states, we keep a 4 | // reference to the next and previous nodes to avoid iteration. 5 | type Node struct { 6 | next, prev *Node 7 | Value interface{} 8 | } 9 | 10 | // Next returns the next node in the list unless there is no next node (it is 11 | // the sentinel node) and then it returns nil 12 | func (n *Node) Next() *Node { 13 | if e := n.next; e.Value != nil { 14 | return e 15 | } 16 | return nil 17 | } 18 | 19 | // Prev returns the previous node in the list unless there is no previous node 20 | // (it is the sentinel node) and then it returns nil 21 | func (n *Node) Prev() *Node { 22 | if e := n.prev; e.Value != nil { 23 | return e 24 | } 25 | return nil 26 | } 27 | 28 | // List represents a Circular Doubly Linked List. 29 | type List struct { 30 | // sentinel is the value used to make this a circular list. It allows us 31 | // to keep a 0 index node in the list which points to the head and tail 32 | // easily. 33 | sentinel Node 34 | // size allows us to get the length of our list in O(1) time. 35 | size int 36 | } 37 | 38 | // Init is a way to initialize the list or clear it out if need be. 39 | func (l *List) Init() *List { 40 | l.sentinel.next = &l.sentinel 41 | l.sentinel.prev = &l.sentinel 42 | l.size = 0 43 | return l 44 | } 45 | 46 | // New returns a new initialized List instance ready to be added to. 47 | func New() *List { 48 | return new(List).Init() 49 | } 50 | 51 | // insertAfter is a convenience function which handles placing a node after a 52 | // selected node. It also increases the list size and returns the new node 53 | // instance. 54 | func (l *List) insertAfter(newNode, node *Node) *Node { 55 | newNode.prev = node 56 | newNode.next = node.next 57 | node.next = newNode 58 | newNode.next.prev = newNode 59 | l.size++ 60 | return newNode 61 | } 62 | 63 | // insertValue is a convenience function which calls insertAfter creating a 64 | // new node in the process based on a passed in value. 65 | func (l *List) insertValue(v interface{}, afterNode *Node) *Node { 66 | return l.insertAfter(&Node{Value: v}, afterNode) 67 | } 68 | 69 | // remove is a convenience function used to remove a specified node and 70 | // decrease the list size. 71 | func (l *List) remove(node *Node) *Node { 72 | node.prev.next = node.next 73 | node.next.prev = node.prev 74 | l.size-- 75 | return node 76 | } 77 | 78 | // Size returns the length of the list. 79 | func (l *List) Size() int { 80 | return l.size 81 | } 82 | 83 | // First returns the first node in the list unless the list is empty which it 84 | // then returns nil. 85 | func (l *List) First() *Node { 86 | if l.sentinel.next == &l.sentinel { 87 | return nil 88 | } 89 | return l.sentinel.next 90 | } 91 | 92 | // Last returns the last node in the list unless the list is empty which it 93 | // then returns nil. 94 | func (l *List) Last() *Node { 95 | if l.sentinel.prev == &l.sentinel { 96 | return nil 97 | } 98 | return l.sentinel.prev 99 | } 100 | 101 | // Append adds a new value to the end of the list. 102 | func (l *List) Append(v interface{}) *Node { 103 | return l.insertValue(v, l.sentinel.prev) 104 | } 105 | 106 | // Prepend adds a new value to the beginnging of the list. 107 | func (l *List) Prepend(v interface{}) *Node { 108 | return l.insertValue(v, &l.sentinel) 109 | } 110 | 111 | // Remove a node from the list if there are values in the list. 112 | func (l *List) Remove(node *Node) interface{} { 113 | if l.size > 0 { 114 | l.remove(node) 115 | } 116 | return node.Value 117 | } 118 | 119 | // Pop removes the last value of the list returning the node. 120 | func (l *List) Pop() *Node { 121 | if l.size == 0 { 122 | return nil 123 | } 124 | return l.remove(l.sentinel.prev) 125 | } 126 | 127 | // PopLeft removes the first value of the list returning the node. 128 | func (l *List) PopLeft() *Node { 129 | if l.size == 0 { 130 | return nil 131 | } 132 | return l.remove(l.sentinel.next) 133 | } 134 | 135 | // AppendList adds another list to the end of the current list concatenating them. 136 | func (l *List) AppendList(other *List) { 137 | for i, n := other.Size(), other.First(); i > 0; i, n = i-1, n.Next() { 138 | l.insertValue(n.Value, l.sentinel.prev) 139 | } 140 | } 141 | 142 | // PrependList adds another list to the beginning of the current list 143 | // concatenating them. 144 | func (l *List) PrependList(other *List) { 145 | for i, n := other.Size(), other.Last(); i > 0; i, n = i-1, n.Prev() { 146 | l.insertValue(n.Value, &l.sentinel) 147 | } 148 | } 149 | 150 | // ToSlice converts a List to a Golang slice 151 | func (l *List) ToSlice() []interface{} { 152 | s := make([]interface{}, l.Size()) 153 | 154 | for i, n := 0, l.First(); n != nil; i, n = i+1, n.Next() { 155 | s[i] = n.Value 156 | } 157 | 158 | return s 159 | } 160 | -------------------------------------------------------------------------------- /data-structures/linked-list/linkedlist_test.go: -------------------------------------------------------------------------------- 1 | package linkedlist 2 | 3 | import "testing" 4 | 5 | func TestEmptyList(t *testing.T) { 6 | l := New() 7 | 8 | if l.Size() != 0 { 9 | t.Errorf("Initial list has length greater than 0, length is %d", l.Size()) 10 | } 11 | 12 | if l.First() != l.Last() { 13 | t.Errorf("Initial list has head != tail") 14 | } 15 | } 16 | 17 | func TestListWithOneValue(t *testing.T) { 18 | l := New() 19 | l.Append(3) 20 | 21 | if l.Size() != 1 { 22 | t.Errorf("List should be of length 1, length is %d", l.Size()) 23 | } 24 | 25 | if l.First().Value != 3 { 26 | t.Errorf("First value should be 3, first value is %d", l.First().Value) 27 | } 28 | 29 | if l.Last().Value != 3 { 30 | t.Errorf("Last value should be 3, last value is %d", l.Last().Value) 31 | } 32 | } 33 | 34 | func TestListWithMultipleValues(t *testing.T) { 35 | l := New() 36 | l.Append(3) 37 | l.Append(6) 38 | l.Prepend(9) 39 | 40 | // Test that we can set the first and last values of the list correctly 41 | if l.Size() != 3 { 42 | t.Errorf("List should be of length 3, length is %d", l.Size()) 43 | } 44 | 45 | if l.First().Value != 9 { 46 | t.Errorf("First value should be 9, first value is %d", l.First().Value) 47 | } 48 | 49 | if l.Last().Value != 6 { 50 | t.Errorf("Last value should be 6, last value is %d", l.Last().Value) 51 | } 52 | 53 | // Test that we can remove nodes and the first and last values are OK 54 | l.Remove(l.First()) 55 | l.Remove(l.Last()) 56 | 57 | if l.Size() != 1 { 58 | t.Errorf("List should be of length 1, length is %d", l.Size()) 59 | } 60 | 61 | if l.First().Value != 3 { 62 | t.Errorf("First value should be 3, first value is %d", l.First().Value) 63 | } 64 | 65 | if l.Last().Value != 3 { 66 | t.Errorf("Last value should be 3, last value is %d", l.Last().Value) 67 | } 68 | } 69 | 70 | func TestListCanBeAppendedWithList(t *testing.T) { 71 | l1 := New() 72 | l1.Append(1) 73 | l1.Append(2) 74 | l1.Append(3) 75 | 76 | l2 := New() 77 | l2.Append(4) 78 | l2.Append(5) 79 | l2.Append(6) 80 | 81 | l1.AppendList(l2) 82 | 83 | if l1.Size() != 6 { 84 | t.Errorf("List should be of length 6, length is %d", l1.Size()) 85 | } 86 | 87 | if l1.First().Value != 1 { 88 | t.Errorf("First value should be 1, first value is %d", l1.First().Value) 89 | } 90 | 91 | if l1.Last().Value != 6 { 92 | t.Errorf("Last value should be 6, last value is %d", l1.Last().Value) 93 | } 94 | } 95 | 96 | func TestListCanBePrependedWithList(t *testing.T) { 97 | l1 := New() 98 | l1.Append(1) 99 | l1.Append(2) 100 | l1.Append(3) 101 | 102 | l2 := New() 103 | l2.Append(4) 104 | l2.Append(5) 105 | l2.Append(6) 106 | 107 | l1.PrependList(l2) 108 | 109 | if l1.Size() != 6 { 110 | t.Errorf("List should be of length 6, length is %d", l1.Size()) 111 | } 112 | 113 | if l1.First().Value != 4 { 114 | t.Errorf("First value should be 4, first value is %d", l1.First().Value) 115 | } 116 | 117 | if l1.Last().Value != 3 { 118 | t.Errorf("Last value should be 3, last value is %d", l1.Last().Value) 119 | } 120 | } 121 | 122 | func TestListPopping(t *testing.T) { 123 | l := New() 124 | l.Append(1) 125 | l.Append(2) 126 | l.Append(3) 127 | 128 | first := l.PopLeft() 129 | last := l.Pop() 130 | 131 | if l.Size() != 1 { 132 | t.Errorf("List should be of length 1, length is %d", l.Size()) 133 | } 134 | 135 | if last.Value != 3 { 136 | t.Errorf("Popped value should be 3, value is %d", last.Value) 137 | } 138 | 139 | if first.Value != 1 { 140 | t.Errorf("Popped value should be 1, value is %d", first.Value) 141 | } 142 | 143 | if l.First() != l.Last() { 144 | t.Errorf("First node should now equal the last node") 145 | } 146 | } 147 | 148 | func TestListCanBeConvertedToSlice(t *testing.T) { 149 | l := New() 150 | l.Append(1) 151 | l.Append(2) 152 | l.Append(3) 153 | 154 | s := l.ToSlice() 155 | vals := []int{1, 2, 3} 156 | target := make([]interface{}, len(vals)) 157 | for i, x := range vals { 158 | target[i] = x 159 | } 160 | 161 | for i := range s { 162 | if s[i] != target[i] { 163 | t.Errorf("Slice values don't match, list is %d target is %d", s[i], target[i]) 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /data-structures/priority-queue/README.md: -------------------------------------------------------------------------------- 1 | # Priority Queue 2 | 3 | A priority queue is an abstract data structure which differs from a queue 4 | based on how the items are extracted. Instead of a FIFO or LIFO structure, PQs 5 | return the items based on a set priority. Depending on the underlying 6 | mechanism a PQ can pull the largest or smallest items off first. This kind of 7 | process enables a fast way of extracting minimum and maximum values of a 8 | container. 9 | 10 | A heap is one of the most used data structures for delivering the results and 11 | either a min or max heap can be used depending on how the priority will be 12 | set. Another common way of implementing it is with a doubly linked list which 13 | provides some more efficient time complexities on insert and delete in most 14 | cases. 15 | 16 | ## Use Cases 17 | 18 | A priority queue, like queues and stacks, can be used in a lot of ways. The 19 | difference in applications deals more with the fact that LIFO and FIFO don't 20 | apply here. It's all about an item's priority. Some use cases could be: 21 | 22 | - Bandwidth management: By using a PQ here, you can ensure the proper traffic 23 | dispersion is maintained. For instance, real-time traffic will have a higher 24 | priority. 25 | - Dijkstra's Algorithm: A PQ allows extracting a minimum vertex efficiently. 26 | - Huffman Coding: Since you need to find the two lowest-frequency trees then a 27 | PQ offers that kind of functionality. 28 | - Job Scheduling: For jobs that need to occur before others, a PQ offers a 29 | sane way to manage job hierarchy. 30 | 31 | ## Time Complexity 32 | 33 | Since the PQ is using a Max Heap as the underlying mechanism for operations, 34 | the time complexity is that of a binary max heap. 35 | 36 | | Case | Average | Worst Case | 37 | | --------- |:-------------:|:-------------:| 38 | | Space | `O(n)` | `O(n)` | 39 | | Search | `O(n)` | `O(n)` | 40 | | Insert | `O(lg n)` | `O(lg n)` | 41 | | Delete | `O(lg n)` | `O(lg n)` | 42 | -------------------------------------------------------------------------------- /data-structures/priority-queue/pq.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import heap "../heap" 4 | 5 | // PriorityQueue uses a Max Heap to handle all operations. 6 | type PriorityQueue struct { 7 | items *heap.MaxHeap 8 | } 9 | 10 | // NewPriorityQueue creates a new instance of a priority queue. 11 | func NewPriorityQueue() *PriorityQueue { 12 | return &PriorityQueue{items: heap.NewMaxHeap()} 13 | } 14 | 15 | // Enqueue pushes a new value onto the back of the priority queue. 16 | func (q *PriorityQueue) Enqueue(pr int, val interface{}) { 17 | q.items.Push(pr, val) 18 | } 19 | 20 | // Peek returns the next value without popping it off. 21 | func (q *PriorityQueue) Peek() interface{} { 22 | return q.items.Peek() 23 | } 24 | 25 | // Dequeue gets the largest value from the heap and return it. 26 | func (q *PriorityQueue) Dequeue() interface{} { 27 | return q.items.Pop() 28 | } 29 | 30 | // IsEmpty checks if there are any items currently in the priority queue. 31 | func (q *PriorityQueue) IsEmpty() bool { 32 | return q.items.IsEmpty() 33 | } 34 | 35 | // Size checks the length of the priority queue. 36 | func (q *PriorityQueue) Size() int { 37 | return q.items.Cardinality() 38 | } 39 | -------------------------------------------------------------------------------- /data-structures/priority-queue/pq_test.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import "testing" 4 | 5 | func TestPriorityQueue(t *testing.T) { 6 | pq := NewPriorityQueue() 7 | 8 | if !pq.IsEmpty() { 9 | t.Error("Initial PQ should be empty") 10 | } 11 | 12 | pq.Enqueue(5, "First") 13 | pq.Enqueue(3, "Second") 14 | pq.Enqueue(10, "Third") 15 | 16 | if pq.Size() != 3 { 17 | t.Error("PQ should have 3 items in it") 18 | } 19 | 20 | if pq.Peek() != "Third" { 21 | t.Errorf("PQ's first item should be '%s' but found '%s'", "Third", pq.Peek()) 22 | } 23 | 24 | first := pq.Dequeue() 25 | if first != "Third" { 26 | t.Errorf("PQ's first popped item should be '%s' but found '%s'", "Third", first) 27 | } 28 | 29 | second := pq.Dequeue() 30 | if second != "First" { 31 | t.Errorf("PQ's second popped item should be '%s' but found '%s'", "First", second) 32 | } 33 | 34 | third := pq.Dequeue() 35 | if third != "Second" { 36 | t.Errorf("PQ's third popped item should be '%s' but found '%s'", "Second", third) 37 | } 38 | 39 | if !pq.IsEmpty() { 40 | t.Error("PQ should be empty") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /data-structures/queue/README.md: -------------------------------------------------------------------------------- 1 | # Queue 2 | 3 | A queue is a container which allows items to be pushed in and removed on a 4 | first in first out principle (FIFO). In practice, we push new items onto 5 | the back of the queue and pop items off the front of the queue. 6 | 7 | In this implementation, I'm using a circular doubly linked list as the 8 | container. 9 | 10 | ## Use Cases 11 | 12 | In the real world, we could use a queue to simulate things such as the line of 13 | cars at a traffic light, a printer buffer, packets waiting at a router. In 14 | general terms, they can be used anywhere where you want to ensure that items 15 | are executed in order they were created. 16 | 17 | ## Time Complexity 18 | 19 | | Case | Complexity | 20 | | --------- |:-----------:| 21 | | All | `O(1)` | 22 | -------------------------------------------------------------------------------- /data-structures/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import list "../linked-list" 4 | 5 | // Queue holds the items. 6 | type Queue struct { 7 | Items *list.List 8 | } 9 | 10 | // NewQueue returns an empty queue. 11 | func NewQueue() *Queue { 12 | return &Queue{Items: list.New()} 13 | } 14 | 15 | // Enqueue pushes a new value onto the back of the queue. 16 | func (q *Queue) Enqueue(val interface{}) { 17 | q.Items.Prepend(val) 18 | } 19 | 20 | // Dequeue pops the last value of the queue off removing it from the 21 | // underlying list and returning the value. 22 | func (q *Queue) Dequeue() interface{} { 23 | return q.Items.Pop().Value 24 | } 25 | 26 | // IsEmpty checks if there are any items currently in the queue. 27 | func (q *Queue) IsEmpty() bool { 28 | if q.Size() > 0 { 29 | return false 30 | } 31 | return true 32 | } 33 | 34 | // Size checks the length of the queue 35 | func (q *Queue) Size() int { 36 | return q.Items.Size() 37 | } 38 | -------------------------------------------------------------------------------- /data-structures/queue/queue_test.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import "testing" 4 | 5 | func TestInitialQueueIsEmpty(t *testing.T) { 6 | queue := NewQueue() 7 | 8 | if !queue.IsEmpty() { 9 | t.Errorf("Queue is supposed to be empty") 10 | } 11 | } 12 | 13 | func TestQueueWorks(t *testing.T) { 14 | queue := NewQueue() 15 | queue.Enqueue(5) 16 | queue.Enqueue(8) 17 | queue.Enqueue(1) 18 | 19 | if queue.Size() != 3 { 20 | t.Errorf("Queue should have 3 items in it, it has %d", queue.Size()) 21 | } 22 | 23 | first := queue.Dequeue() 24 | 25 | if queue.Size() != 2 { 26 | t.Errorf("Queue should have 2 items in it, it has %d", queue.Size()) 27 | } 28 | 29 | if first != 5 { 30 | t.Errorf("First value should be 5, it returned %d", first) 31 | } 32 | 33 | second := queue.Dequeue() 34 | third := queue.Dequeue() 35 | 36 | if !queue.IsEmpty() { 37 | t.Errorf("Queue is supposed to be empty") 38 | } 39 | 40 | if second != 8 { 41 | t.Errorf("Second value should be 8, it returned %d", second) 42 | } 43 | 44 | if third != 1 { 45 | t.Errorf("Third value should be 1, it returned %d", third) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /data-structures/red-black-tree/rbt_test.go: -------------------------------------------------------------------------------- 1 | package rbt 2 | 3 | import "testing" 4 | 5 | func TestInitialRBTIsEmpty(t *testing.T) { 6 | rbt := NewRBT() 7 | 8 | if !rbt.IsEmpty() { 9 | t.Error("RBT should be empty") 10 | } 11 | } 12 | 13 | func TestRBTWithRootHasTwoChildren(t *testing.T) { 14 | rbt := NewRBT() 15 | rbt.Insert(1) 16 | 17 | if rbt.IsEmpty() { 18 | t.Error("RBT should NOT be empty") 19 | } 20 | 21 | if rbt.Root().Left == nil || rbt.Root().Right == nil { 22 | t.Error("Node should always have two children") 23 | } 24 | } 25 | 26 | func TestRBTNodeCanDetermineColor(t *testing.T) { 27 | rbt := NewRBT() 28 | rbt.Insert(1) 29 | 30 | if rbt.Root().IsRed() { 31 | t.Error("Node should not be red") 32 | } 33 | 34 | if !rbt.Root().IsBlack() { 35 | t.Error("Node should be black") 36 | } 37 | } 38 | 39 | func TestRBTNodeCanGetParent(t *testing.T) { 40 | rbt := NewRBT() 41 | rbt.Insert(1) 42 | rbt.Insert(2) 43 | 44 | if rbt.Root().Right.Parent.Val != 1 { 45 | t.Errorf("Parent node should have a value of %d", 1) 46 | } 47 | } 48 | 49 | func TestRBTNodeCanGetGrandparent(t *testing.T) { 50 | rbt := NewRBT() 51 | rbt.Insert(1) 52 | rbt.Insert(2) 53 | rbt.Insert(3) 54 | rbt.Insert(4) 55 | 56 | if rbt.Find(4).grandparent().Val != 2 { 57 | t.Errorf("Grandparent node should have a value of %d", 2) 58 | } 59 | } 60 | 61 | func TestRBTNodeCanGetUncle(t *testing.T) { 62 | rbt := NewRBT() 63 | rbt.Insert(1) 64 | rbt.Insert(2) 65 | rbt.Insert(3) 66 | rbt.Insert(4) 67 | 68 | if rbt.Find(4).uncle().Val != 1 { 69 | t.Errorf("Uncle node should have a value of %d", 1) 70 | } 71 | } 72 | 73 | func TestRBTNodeCanGetSibling(t *testing.T) { 74 | rbt := NewRBT() 75 | rbt.Insert(1) 76 | rbt.Insert(2) 77 | rbt.Insert(3) 78 | rbt.Insert(4) 79 | 80 | if rbt.Find(3).sibling().Val != 1 { 81 | t.Errorf("Sibling node should have a value of %d", 1) 82 | } 83 | } 84 | 85 | func TestRBTCanGetRoot(t *testing.T) { 86 | rbt := NewRBT() 87 | rbt.Insert(1) 88 | 89 | if rbt.Root().Val != 1 { 90 | t.Errorf("Root node should have value of %d", 1) 91 | } 92 | } 93 | 94 | func TestRBTCanFindNodes(t *testing.T) { 95 | rbt := NewRBT() 96 | 97 | notFound := rbt.Find(10) 98 | 99 | if notFound != nil { 100 | t.Error("Should not find node in empty tree") 101 | } 102 | 103 | rbt.Insert(1) 104 | rbt.Insert(2) 105 | rbt.Insert(3) 106 | rbt.Insert(4) 107 | 108 | root := rbt.Find(2) 109 | 110 | if root.Val != 2 { 111 | t.Errorf("Found node should have value of %d, instead found %d", 2, root.Val) 112 | } 113 | 114 | left := rbt.Find(1) 115 | 116 | if left.Val != 1 { 117 | t.Errorf("Found node should have value of %d, instead found %d", 1, root.Val) 118 | } 119 | 120 | right := rbt.Find(4) 121 | 122 | if right.Val != 4 { 123 | t.Errorf("Found node should have value of %d, instead found %d", 4, root.Val) 124 | } 125 | } 126 | 127 | func TestRBTCanIterate(t *testing.T) { 128 | rbt := NewRBT() 129 | rbt.Insert(1) 130 | rbt.Insert(2) 131 | rbt.Insert(3) 132 | rbt.Insert(4) 133 | 134 | expected := []int{1, 2, 3, 4} 135 | 136 | i := 0 137 | for e := range rbt.Iter(InOrder) { 138 | if e != expected[i] { 139 | t.Errorf("Iteration should yield %d, found %d", expected[i], e) 140 | } 141 | i++ 142 | } 143 | } 144 | 145 | func TestRBTCannotInsertDuplicateNode(t *testing.T) { 146 | rbt := NewRBT() 147 | rbt.Insert(1) 148 | res := rbt.Insert(1) 149 | 150 | if res { 151 | t.Error("Should not be allowed to insert duplicate node") 152 | } 153 | } 154 | 155 | func TestRBTInsertBalanceCase2(t *testing.T) { 156 | rbt := NewRBT() 157 | rbt.Insert(1) 158 | rbt.Insert(2) 159 | 160 | if rbt.Find(2).IsBlack() || rbt.Find(1).IsRed() { 161 | t.Error("Insert balance case 2 should not alter colors") 162 | } 163 | } 164 | 165 | func TestRBTInsertBalanceCase3(t *testing.T) { 166 | rbt := NewRBT() 167 | rbt.Insert(1) 168 | rbt.Insert(2) 169 | rbt.Insert(0) 170 | rbt.Insert(3) 171 | 172 | node := rbt.Find(3) 173 | 174 | if node.Parent.IsRed() { 175 | t.Error("Parent should be black after balance") 176 | } 177 | 178 | if node.uncle().IsRed() { 179 | t.Error("Uncle should be black after balance") 180 | } 181 | } 182 | 183 | func TestRBTInsertBalanceCase4(t *testing.T) { 184 | rbt := NewRBT() 185 | rbt.Insert(7) 186 | rbt.Insert(10) 187 | rbt.Insert(4) 188 | rbt.Insert(5) 189 | rbt.Insert(6) 190 | 191 | node := rbt.Find(6) 192 | 193 | if node.sibling().Val != 4 { 194 | t.Errorf("Sibling value should be 4, is %d", node.sibling().Val) 195 | } 196 | 197 | if node.Parent.Val != 5 { 198 | t.Errorf("Parent value should be 5, is %d", node.Parent.Val) 199 | } 200 | } 201 | 202 | func TestRBTCannotDeleteNodeNotInTree(t *testing.T) { 203 | rbt := NewRBT() 204 | rbt.Insert(1) 205 | res := rbt.Delete(2) 206 | 207 | if res { 208 | t.Error("Should not be allowed to delete node not in tree") 209 | } 210 | } 211 | 212 | func TestRBTCannotDeleteInEmptyTree(t *testing.T) { 213 | rbt := NewRBT() 214 | res := rbt.Delete(2) 215 | 216 | if res { 217 | t.Error("Should not be allowed to delete node in empty tree") 218 | } 219 | } 220 | 221 | func TestRBTCanDeleteNodeWithTwoChildren(t *testing.T) { 222 | rbt := NewRBT() 223 | rbt.Insert(1) 224 | rbt.Insert(2) 225 | rbt.Insert(3) 226 | res := rbt.Delete(2) 227 | 228 | if !res { 229 | t.Error("Should be allowed to delete node with two children") 230 | } 231 | 232 | if rbt.Root().Val != 3 { 233 | t.Errorf("New root node should be %d, found %d", 3, rbt.Root().Val) 234 | } 235 | 236 | if rbt.Root().IsRed() { 237 | t.Error("New root node should be black") 238 | } 239 | 240 | if rbt.Root().Right != LEAF { 241 | t.Error("New right node should be a LEAF") 242 | } 243 | 244 | if rbt.Root().Left.Val != 1 || !rbt.Root().Left.IsRed() { 245 | t.Error("New left node should be 1 and red") 246 | } 247 | } 248 | 249 | func TestRBTCanDeleteNodeWithOneChild(t *testing.T) { 250 | rbt := NewRBT() 251 | rbt.Insert(1) 252 | rbt.Insert(2) 253 | rbt.Insert(3) 254 | rbt.Insert(4) 255 | rbt.Insert(5) 256 | rbt.Insert(6) 257 | res := rbt.Delete(5) 258 | 259 | if !res { 260 | t.Error("Should be allowed to delete node with one child") 261 | } 262 | } 263 | 264 | func TestRBTDeleteBalanceCase1(t *testing.T) { 265 | } 266 | 267 | func TestRBTDeleteBalanceCase2(t *testing.T) { 268 | } 269 | 270 | func TestRBTDeleteBalanceCase3(t *testing.T) { 271 | } 272 | 273 | func TestRBTDeleteBalanceCase4(t *testing.T) { 274 | } 275 | 276 | func TestRBTDeleteBalanceCase5(t *testing.T) { 277 | } 278 | 279 | func TestRBTDeleteBalanceCase6(t *testing.T) { 280 | } 281 | -------------------------------------------------------------------------------- /data-structures/red-black-tree/traverse.go: -------------------------------------------------------------------------------- 1 | package rbt 2 | 3 | // Traversable is an interface to define a method for traversing through a BST. 4 | type Traversable interface { 5 | Traverse(*Node, chan int) 6 | } 7 | 8 | // InOrderTraversable is an empty struct used to handle the methods of the 9 | // Traversable interface. 10 | type InOrderTraversable struct{} 11 | 12 | // InOrder is a valid type used to specify the iteration method. 13 | var InOrder InOrderTraversable 14 | 15 | // Traverse satisfies the Traversable interface sending values back on the 16 | // channel. This one if for in-order traversing. 17 | func (bst InOrderTraversable) Traverse(node *Node, ch chan int) { 18 | if node != LEAF { 19 | bst.Traverse(node.Left, ch) 20 | ch <- node.Val 21 | bst.Traverse(node.Right, ch) 22 | } 23 | } 24 | 25 | // PreOrderTraversable is an empty struct used to handle the methods of the 26 | // Traversable interface. 27 | type PreOrderTraversable struct{} 28 | 29 | // PreOrder is a valid type used to specify the iteration method. 30 | var PreOrder PreOrderTraversable 31 | 32 | // Traverse satisfies the Traversable interface sending values back on the 33 | // channel. This one if for pre-order traversing. 34 | func (bst PreOrderTraversable) Traverse(node *Node, ch chan int) { 35 | if node != LEAF { 36 | ch <- node.Val 37 | bst.Traverse(node.Left, ch) 38 | bst.Traverse(node.Right, ch) 39 | } 40 | } 41 | 42 | // PostOrderTraversable is an empty struct used to handle the methods of the 43 | // Traversable interface. 44 | type PostOrderTraversable struct{} 45 | 46 | // PostOrder is a valid type used to specify the iteration method. 47 | var PostOrder PostOrderTraversable 48 | 49 | // Traverse satisfies the Traversable interface sending values back on the 50 | // channel. This one if for post-order traversing. 51 | func (bst PostOrderTraversable) Traverse(node *Node, ch chan int) { 52 | if node != LEAF { 53 | bst.Traverse(node.Left, ch) 54 | bst.Traverse(node.Right, ch) 55 | ch <- node.Val 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /data-structures/set/README.md: -------------------------------------------------------------------------------- 1 | # Set 2 | 3 | A set is an abstract data type that stores unorder unique values. Sets differ 4 | from other list-type containers because most operations of a set check for 5 | existence rather than for actual values. Still, it can be used in place of an 6 | array or another container if order doesn't matter and duplicates don't need 7 | to be stored. 8 | 9 | The typical two types of sets are **static sets** and **dynamic sets**. As you 10 | can imagine the core difference is that statis sets only allow search and 11 | traversal while a dynamic set can be added to and subtracted from. 12 | 13 | ## Implementation 14 | 15 | Due to the set properties many sets are implemented using a hash table where 16 | the keys are the values of the set. In my implementation here, I decided to 17 | use a hash table for this to make it very easy to work with. Other structures 18 | such as standard arrays and trees can be used if the tradeoffs are better for 19 | your use case. For instance a balanced binary search tree will guarantee o(lg n) 20 | complexity for most cases where a hash table may degrade to o(n) at worst 21 | case. 22 | 23 | A binary search tree may be overkill though because the ordering of a set 24 | typically doesn't matter which is why a hash table can be a good candidate. 25 | 26 | ## Operations 27 | 28 | Sets are based on math's finite sets so operations are very similar. Some of 29 | the most common operations are: 30 | 31 | - Union: Return a new set which is a combination of two sets. 32 | - Intersection: Return a new set which contains the values that exist in two 33 | sets. 34 | - Difference: Return a new set which contains the values that don't exist in 35 | another set. This will change based on the set asking for the difference. 36 | 37 | Some other common operations are: 38 | 39 | - IsSubset: Check if another set is the subset of your set. 40 | - IsSuperset: Check if your set is the subset of another. 41 | 42 | ## Use Cases 43 | 44 | A set can be used in places where membership is important rather than the real 45 | contents of a container. For instance, if you simply want to see how two 46 | containers compare then set operations can provide clarity. If you care about 47 | unique data and don't care about order, a set can be a lighter way to store 48 | items as well. 49 | 50 | ## Time Complexity 51 | 52 | | Case | Average | Worst Case | 53 | | --------- |:-------------:|:-------------:| 54 | | Space | `O(n)` | `O(n)` | 55 | | Search | `O(1)` | `O(n)` | 56 | | Insert | `O(1)` | `O(n)` | 57 | | Delete | `O(1)` | `O(n)` | 58 | -------------------------------------------------------------------------------- /data-structures/set/set.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | hashtable "../hash-table" 8 | ) 9 | 10 | // Set is a wrapper around a hash table containing the actual items. To make 11 | // it easy, items must be strings in this example so it conforms to my simple 12 | // hash table structure. 13 | type Set struct { 14 | items *hashtable.HashTable 15 | } 16 | 17 | // NewSet creates an empty Set instance. 18 | func NewSet() *Set { 19 | return &Set{items: hashtable.NewHashTable()} 20 | } 21 | 22 | // Values returns the values from the hash table. In reality it iterates the 23 | // hash table for the keys. 24 | func (s *Set) Values() []string { 25 | var ret []string 26 | 27 | if s.IsEmpty() { 28 | return ret 29 | } 30 | 31 | for _, l := range s.items.Buckets { 32 | if l != nil { 33 | for cur := l.GetHead(); cur != nil; cur = cur.Next() { 34 | ret = append(ret, cur.GetKey()) 35 | } 36 | } 37 | } 38 | return ret 39 | } 40 | 41 | // String fullfills the Stringer interface allowing easy set printing. 42 | func (s *Set) String() string { 43 | return fmt.Sprintf("[%s]", strings.Join(s.Values(), ",")) 44 | } 45 | 46 | // Cardinality returns the number of items in the set. 47 | func (s *Set) Cardinality() uint64 { 48 | return s.items.NumEntries 49 | } 50 | 51 | // IsEmpty returns an answer based on the current cardinality. 52 | func (s *Set) IsEmpty() bool { 53 | if s.Cardinality() == 0 { 54 | return true 55 | } 56 | return false 57 | } 58 | 59 | // Add inserts a new item into the set. 60 | func (s *Set) Add(key string) { 61 | s.items.Put(key, true) 62 | } 63 | 64 | // Remove attempts to delete an item from the set. 65 | func (s *Set) Remove(key string) { 66 | s.items.Del(key) 67 | } 68 | 69 | // Contains checks if an items exists within the set. 70 | func (s *Set) Contains(key string) bool { 71 | if s.items.Get(key) != nil { 72 | return true 73 | } 74 | return false 75 | } 76 | 77 | // Visit is a method allowing a function to be applied to each item in the set. 78 | func (s *Set) Visit(fn func(string)) { 79 | for _, v := range s.Values() { 80 | fn(v) 81 | } 82 | } 83 | 84 | // Subset builds a subset based on a conditional function. 85 | func (s *Set) Subset(condition func(string) bool) *Set { 86 | newSet := NewSet() 87 | s.Visit(func(v string) { 88 | if condition(v) { 89 | newSet.Add(v) 90 | } 91 | }) 92 | return newSet 93 | } 94 | 95 | // Union returns a new set containing all of the values from both sets. 96 | func (s *Set) Union(other *Set) *Set { 97 | newSet := NewSet() 98 | s.Visit(func(v string) { newSet.Add(v) }) 99 | other.Visit(func(v string) { newSet.Add(v) }) 100 | return newSet 101 | } 102 | 103 | // Intersection returns a new set containing the values that overlap between 104 | // both sets. 105 | func (s *Set) Intersection(other *Set) *Set { 106 | if s.Cardinality() > other.Cardinality() { 107 | return s.Subset(func(v string) bool { return other.Contains(v) }) 108 | } else { 109 | return other.Subset(func(v string) bool { return s.Contains(v) }) 110 | } 111 | } 112 | 113 | // Difference returns a new set containing its items that the other set does 114 | // not contain. 115 | func (s *Set) Difference(other *Set) *Set { 116 | return s.Subset(func(v string) bool { return !other.Contains(v) }) 117 | } 118 | -------------------------------------------------------------------------------- /data-structures/set/set_test.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestEmptySet(t *testing.T) { 9 | s := NewSet() 10 | 11 | if s.Cardinality() != 0 { 12 | t.Error("Empty set should contain no items") 13 | } 14 | 15 | if !s.IsEmpty() { 16 | t.Error("Empty set should be empty") 17 | } 18 | } 19 | 20 | func TestCanGetValues(t *testing.T) { 21 | s := NewSet() 22 | s.Add("one") 23 | 24 | if s.Cardinality() != 1 { 25 | t.Error("Set should have one item") 26 | } 27 | 28 | if s.IsEmpty() { 29 | t.Error("Set should not be empty") 30 | } 31 | 32 | if s.Values()[0] != "one" { 33 | t.Error("Values were not correct") 34 | } 35 | } 36 | 37 | func TestCanPrintSet(t *testing.T) { 38 | st := "[two,one]" 39 | 40 | s := NewSet() 41 | s.Add("one") 42 | s.Add("two") 43 | 44 | if s.String() != st { 45 | t.Errorf("Printed string does not match, should be %s but found %s", st, s.String()) 46 | } 47 | } 48 | 49 | func TestCanAddItem(t *testing.T) { 50 | s := NewSet() 51 | s.Add("one") 52 | 53 | if !s.Contains("one") { 54 | t.Error("Didn't find 'one' in set") 55 | } 56 | } 57 | 58 | func TestCanDeleteItem(t *testing.T) { 59 | s := NewSet() 60 | s.Add("one") 61 | s.Remove("one") 62 | 63 | if s.Contains("one") { 64 | t.Error("Found 'one' in set after deleting it") 65 | } 66 | } 67 | 68 | func TestCanVisitEachItem(t *testing.T) { 69 | s := NewSet() 70 | s.Add("one") 71 | s.Add("two") 72 | o := NewSet() 73 | 74 | s.Visit(func(v string) { 75 | o.Add(strings.ToUpper(v)) 76 | }) 77 | 78 | if !o.Contains("ONE") { 79 | t.Error("Could not find uppercased 'one' in set") 80 | } 81 | 82 | if !o.Contains("TWO") { 83 | t.Error("Could not find uppercased 'two' in set") 84 | } 85 | } 86 | 87 | func TestCanGetSubset(t *testing.T) { 88 | s := NewSet() 89 | s.Add("one") 90 | s.Add("two") 91 | sub := s.Subset(func(v string) bool { return v != "one" }) 92 | 93 | if sub.Contains("one") { 94 | t.Error("Subset should not contain 'one'") 95 | } 96 | } 97 | 98 | func TestCanGetUnion(t *testing.T) { 99 | s := NewSet() 100 | s.Add("one") 101 | s.Add("two") 102 | 103 | o := NewSet() 104 | o.Add("one") 105 | o.Add("three") 106 | 107 | u := s.Union(o) 108 | 109 | if u.Cardinality() != 3 { 110 | t.Error("Union should have three members") 111 | } 112 | } 113 | 114 | func TestCanGetIntersection(t *testing.T) { 115 | s := NewSet() 116 | s.Add("one") 117 | s.Add("two") 118 | 119 | o := NewSet() 120 | o.Add("one") 121 | o.Add("three") 122 | 123 | u := s.Intersection(o) 124 | 125 | if u.Cardinality() != 1 { 126 | t.Error("Intersection should have one member") 127 | } 128 | 129 | if u.Values()[0] != "one" { 130 | t.Error("Intersection value should be 'one'") 131 | } 132 | } 133 | 134 | func TestCanGetDifference(t *testing.T) { 135 | s := NewSet() 136 | s.Add("one") 137 | s.Add("two") 138 | 139 | o := NewSet() 140 | o.Add("one") 141 | o.Add("three") 142 | 143 | if s.Difference(o).Values()[0] != "two" { 144 | t.Errorf("Difference of first to second should be 'two', got %s", s.Difference(o).Values()[0]) 145 | } 146 | 147 | if o.Difference(s).Values()[0] != "three" { 148 | t.Errorf("Difference of second to first should be 'three', got %s", o.Difference(s).Values()[0]) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /data-structures/stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | A stack is a container which allows items to be pushed in and removed on a 4 | last in first out principle (LIFO). In practice, we push new items onto 5 | the back of the stack and pop items off the back of the stack. 6 | 7 | In this implementation, I'm using a circular doubly linked list as the 8 | container. 9 | 10 | ## Use Cases 11 | 12 | Stacks are used in a number of concepts. One of the first real world cases is 13 | in writing reverse polish notation. Another good place to see a stack is in 14 | browser history where if you go back to the last page, it would be the most 15 | recent page pushed onto the stack. In general though, applications which could 16 | recall the most recent value are good candidates for a stack. 17 | 18 | ## Time Complexity 19 | 20 | | Case | Complexity | 21 | | --------- |:-----------:| 22 | | All | `O(1)` | 23 | -------------------------------------------------------------------------------- /data-structures/stack/stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import list "../linked-list" 4 | 5 | // Stack holds the items. 6 | type Stack struct { 7 | Items *list.List 8 | } 9 | 10 | // NewStack returns an empty stack. 11 | func NewStack() *Stack { 12 | return &Stack{Items: list.New()} 13 | } 14 | 15 | // Push places a new value on the end of the stack 16 | func (q *Stack) Push(val interface{}) { 17 | q.Items.Append(val) 18 | } 19 | 20 | // Pop removes the value at the end of the stack 21 | func (q *Stack) Pop() interface{} { 22 | return q.Items.Pop().Value 23 | } 24 | 25 | // IsEmpty checks if there are any items currently in the stack. 26 | func (q *Stack) IsEmpty() bool { 27 | if q.Size() > 0 { 28 | return false 29 | } 30 | return true 31 | } 32 | 33 | // Size checks the length of the stack 34 | func (q *Stack) Size() int { 35 | return q.Items.Size() 36 | } 37 | -------------------------------------------------------------------------------- /data-structures/stack/stack_test.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | import "testing" 4 | 5 | func TestInitialStackIsEmpty(t *testing.T) { 6 | stack := NewStack() 7 | 8 | if !stack.IsEmpty() { 9 | t.Errorf("Stack is supposed to be empty") 10 | } 11 | } 12 | 13 | func TestStackWorks(t *testing.T) { 14 | stack := NewStack() 15 | stack.Push(5) 16 | stack.Push(8) 17 | stack.Push(1) 18 | 19 | if stack.Size() != 3 { 20 | t.Errorf("Stack should have 3 items in it, it has %d", stack.Size()) 21 | } 22 | 23 | first := stack.Pop() 24 | 25 | if stack.Size() != 2 { 26 | t.Errorf("Stack should have 2 items in it, it has %d", stack.Size()) 27 | } 28 | 29 | if first != 1 { 30 | t.Errorf("First value should be 1, it returned %d", first) 31 | } 32 | 33 | second := stack.Pop() 34 | third := stack.Pop() 35 | 36 | if !stack.IsEmpty() { 37 | t.Errorf("Stack is supposed to be empty") 38 | } 39 | 40 | if second != 8 { 41 | t.Errorf("Second value should be 8, it returned %d", second) 42 | } 43 | 44 | if third != 5 { 45 | t.Errorf("Third value should be 5, it returned %d", third) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /data-structures/suffix-array/README.md: -------------------------------------------------------------------------------- 1 | # Suffix Array 2 | 3 | A suffix array is basically just a sorted array of all the possible suffixes 4 | of a given string. We can think of a suffix as a "substring" in this case. A 5 | suffix array will contain indices to each suffix found in a string after they 6 | have been sorted. In a lot of implementations, it's common to use a sentinel 7 | character which is not in the alphabet of the string in question. 8 | 9 | Let's look at an example to understand how this works using the word `banana`. 10 | 11 | Using a sentinel value of `$`, we can see the string to index becomes `banana$`. 12 | We index these items as follows: 13 | 14 | | i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 15 | |:-----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 16 | | S[i] | b | a | n | a | n | a | $ | 17 | 18 | With this, we can then start to break down the suffixes of this string such 19 | that: 20 | 21 | | Suffix | i | 22 | |:---------:|:-:| 23 | | banana$ | 1 | 24 | | anana$ | 2 | 25 | | nana$ | 3 | 26 | | ana$ | 4 | 27 | | na$ | 5 | 28 | | a$ | 6 | 29 | | $ | 7 | 30 | 31 | Finally, we can now sort these suffixes in ascending order to give us: 32 | 33 | | Suffix | i | 34 | |:---------:|:-:| 35 | | $ | 7 | 36 | | a$ | 6 | 37 | | ana$ | 4 | 38 | | anana$ | 2 | 39 | | banana$ | 1 | 40 | | na$ | 5 | 41 | | nana$ | 3 | 42 | 43 | Our final suffix array with the corresponding indices to the suffixes (A) would be: 44 | 45 | | i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 46 | |:-----:|:-:|:-:|:-:|:-:|:-:|:-:|:-:| 47 | | A[i] | 7 | 6 | 4 | 2 | 1 | 5 | 3 | 48 | 49 | ## Suffix Array Construction 50 | 51 | A proper suffix array can be constructed many ways. The way that I chose to 52 | follow has a time complexity of `O(n log^2 n)`. The process is similiar to 53 | above but continuously updates the order based on sets of two characters. An 54 | example is best to understand this process: 55 | 56 | | Sort Index | Suffix Index | Suffix | 57 | |:-------------:|:-------------:|:-----------------:| 58 | | 0 | 10 | **i** | 59 | | 1 | 7 | **ip**pi | 60 | | 2 | 1 | **is**sissippi | 61 | | 2 | 4 | **is**sippi | 62 | | 3 | 0 | **mi**ssissippi | 63 | | 4 | 9 | **pi** | 64 | | 5 | 8 | **ppi** | 65 | | 6 | 3 | **si**ssippi | 66 | | 6 | 6 | **si**ppi | 67 | | 7 | 2 | **ss**issippi | 68 | | 7 | 5 | **ss**ippi | 69 | 70 | The table above shows the suffixes of the string `mississippi` sorted by the 71 | first two characters in the suffix. For suffixes that have the same first two 72 | characters, we assign the same sort index. With this, we can now create a 73 | tuple to help us sort suffixes with the same index. We do this as such: 74 | 75 | | Sort Index | Suffix Index | Suffix | Suffix Index (After first 2 chars) | Sort Index Tuple | 76 | |:-------------:|:-------------:|:-----------------:|:-------------------------------------:|:-----------------:| 77 | | 0 | 10 | **i** | -1 | (0, -1) | 78 | | 1 | 7 | **ip**pi | 9 | (1, 4) | 79 | | 2 | 1 | **is**sissippi | 3 | (2, 6) | 80 | | 2 | 4 | **is**sippi | 6 | (2, 6) | 81 | | 3 | 0 | **mi**ssissippi | 2 | (3, 7) | 82 | | 4 | 9 | **pi** | -1 | (4, -1) | 83 | | 5 | 8 | **ppi** | 10 | (5, 0) | 84 | | 6 | 3 | **si**ssippi | 5 | (6, 7) | 85 | | 6 | 6 | **si**ppi | 8 | (6, 5) | 86 | | 7 | 2 | **ss**issippi | 4 | (7, 2) | 87 | | 7 | 5 | **ss**ippi | 7 | (7, 1) | 88 | 89 | Based on the first sort, we look at the suffix that follows the first two 90 | characters. As an example, let's look at the suffix `ippi`. The remaining 91 | suffix after the first two characters is `pi`. We can see that this correlates 92 | to the suffix at index 9 which has a sort index of 4. We create a tuple with 93 | the initial suffixes sort index (`1`) and the remaining sort index (`4`) 94 | giving us a value that we can use to sort things. As you can probably see, if 95 | there is no remainder we assign a -1 as the remaing sort index. 96 | 97 | When we do the sort on this tuple, our comparison function works something 98 | like this: 99 | 100 | ``` 101 | Using the first two tuples (0, -1) and (1, 4): 102 | 103 | if 0 == 1: 104 | return -1 < 4 105 | else: 106 | return 0 < 1 107 | 108 | For this, we would take the else route and since the result is true, they are 109 | already in the correct order. 110 | 111 | As an example of an order that would change, we can look at (6, 7) and (6, 5) 112 | which correlate to the suffix indexes 3 and 6 respectively. 113 | 114 | if 6 == 6: 115 | return 7 < 5 116 | else: 117 | return 6 < 6 118 | 119 | Since we are within the initial if statement and return false, they switch order. 120 | ``` 121 | 122 | So now when we sort the suffixes based on these results, we have all the 123 | suffixes sorted by the first 4 characters. Our new result would look like: 124 | 125 | | Sort Index | Suffix Index | Suffix | 126 | |:-------------:|:-------------:|:-----------------:| 127 | | 0 | 10 | **i** | 128 | | 1 | 7 | **ippi** | 129 | | 2 | 1 | **issi**ssippi | 130 | | 2 | 4 | **issi**ppi | 131 | | 3 | 0 | **missi**ssippi | 132 | | 4 | 9 | **pi** | 133 | | 5 | 8 | **ppi** | 134 | | 6 | 6 | **sipp**i | 135 | | 7 | 3 | **siss**ippi | 136 | | 8 | 2 | **ssis**sippi | 137 | | 9 | 5 | **ssip**pi | 138 | 139 | On the next iteration, we move the "cursor" forward and can construct the same 140 | set of tuples for the first 8 characters. This process continues until we 141 | reach the end of the suffix length available. 142 | 143 | In my algorithm, followed [this paper](http://web.stanford.edu/class/cs97si/suffix-array.pdf). 144 | 145 | ## Use Cases 146 | 147 | In a lot of ways, this is used in contest-type problems dealing with finding 148 | substrings. On a very practical level, it can be used to do full text 149 | indexing, data compression algorithsm, and bioinformatics. In a more common 150 | form, you will see these types of suffix arrays presented as suffix trees (a 151 | form of trie) but the array version is a condensed way to store suffix 152 | positions without using memory to save references to nodes. 153 | 154 | Things to look for when deciding if a suffix array is right for the problem 155 | are: 156 | 157 | - Finding the longest common prefix of a string 158 | - Finding if a substring exists in a string 159 | - Searching through text 160 | 161 | ## Time Complexity 162 | 163 | | Case | Complexity | 164 | | --------- |:-----------------:| 165 | | Construct | `O(n log^2 n)` | 166 | | Search | `O(m log n)` | 167 | -------------------------------------------------------------------------------- /data-structures/suffix-array/suffix_array.go: -------------------------------------------------------------------------------- 1 | package suffix_array 2 | 3 | import "sort" 4 | 5 | // Suffix represents a single suffix item which holds a reference to the 6 | // initial string index and the last range of sort indices. 7 | type Suffix struct { 8 | nr []int 9 | idx int 10 | } 11 | 12 | // SuffixResult is the group of Suffix objects 13 | type SuffixResult []*Suffix 14 | 15 | // ToSuffixArray gets the sorted suffix array result. 16 | func (s SuffixResult) ToSuffixArray() []int { 17 | var suffixArray []int 18 | for _, e := range s { 19 | suffixArray = append(suffixArray, e.idx) 20 | } 21 | return suffixArray 22 | } 23 | 24 | // Search finds a given pattern within a string using a suffix array. It 25 | // returns two integers, the start index of the suffix array for the match and 26 | // the end index for the suffix array for the match. 27 | func (s SuffixResult) Search(str, pat string) (int, int) { 28 | suffixArray := s.ToSuffixArray() 29 | n := len(suffixArray) 30 | start := 0 31 | end := n 32 | 33 | for start < end { 34 | mid := (start + end) / 2 35 | if pat > str[suffixArray[mid]:n] { 36 | start = mid + 1 37 | } else { 38 | end = mid 39 | } 40 | } 41 | 42 | suffixStart := start 43 | end = n 44 | 45 | for start < end { 46 | mid := (start + end) / 2 47 | if pat < str[suffixArray[mid]:n] { 48 | end = mid 49 | } else { 50 | start = mid + 1 51 | } 52 | } 53 | return suffixStart, end 54 | } 55 | 56 | // BySuffix is a convenience type to handle sorting by the suffixes. 57 | type BySuffix []*Suffix 58 | 59 | // Len satisfies the sort interface. 60 | func (s BySuffix) Len() int { 61 | return len(s) 62 | } 63 | 64 | // Swap satisfies the sort interface. 65 | func (s BySuffix) Swap(i, j int) { 66 | s[i], s[j] = s[j], s[i] 67 | } 68 | 69 | // Less satisfies the sort interface. In this case, we check to see if the two 70 | // sort indices are equal. If they are then we check which of the second 71 | // indices is greater. If they aren't equal then we find out which of the 72 | // first indices is greater. 73 | func (s BySuffix) Less(i, j int) bool { 74 | if s[i].nr[0] == s[j].nr[0] { 75 | return s[i].nr[1] < s[j].nr[1] 76 | } 77 | 78 | return s[i].nr[0] < s[j].nr[0] 79 | } 80 | 81 | // ConstructSuffixArray builds the initial suffix array returning the subsequent suffix array 82 | // along with the sort index matrix which can be used to find the LCP. 83 | func ConstructSuffixArray(s string) (SuffixResult, [][]int) { 84 | var sortIndex [][]int 85 | 86 | n := len(s) 87 | suffixes := make(SuffixResult, n) 88 | 89 | // Build the initial sort index row containing integers based on distance from `a` 90 | sortIndex = append(sortIndex, make([]int, n)) 91 | for i, char := range s { 92 | sortIndex[0][i] = int(char - 'a') 93 | } 94 | 95 | // Here we are sorting suffixes in intervals of 2 characters. In this way, 96 | // we first look at the suffixes as the first 2 characters, first 4, first 97 | // 8, etc. This allows us to progressively make sorting decisions. 98 | for step, count := 1, 1; count < n; step, count = step+1, count*2 { 99 | // Build the array of suffixes where the range is based on the 100 | // previous batches sort index results. Since we want to store the 101 | // current sort index and the previous sort index (nr) then we check 102 | // that we can. If not, we denote it with then -1. 103 | for i := 0; i < n; i++ { 104 | nr := make([]int, 2) 105 | nr[0] = sortIndex[step-1][i] 106 | if i+count < n { 107 | nr[1] = sortIndex[step-1][i+count] 108 | } else { 109 | nr[1] = -1 110 | } 111 | suffixes[i] = &Suffix{nr: nr, idx: i} 112 | } 113 | 114 | // Sort the suffixes to their order based on the sort indexes collected above. 115 | sort.Sort(BySuffix(suffixes)) 116 | 117 | // Build the next row of sort indexes based on the previous row. 118 | sortIndex = append(sortIndex, make([]int, n)) 119 | for i := 0; i < n; i++ { 120 | // This step really checks to see if the current suffix and the 121 | // one before it are the same substring as one another. If they 122 | // are, then we want to prior suffix's index to move into the next 123 | // round. 124 | if i > 0 && suffixes[i].nr[0] == suffixes[i-1].nr[0] && suffixes[i].nr[1] == suffixes[i-1].nr[1] { 125 | sortIndex[step][suffixes[i].idx] = sortIndex[step][suffixes[i-1].idx] 126 | } else { 127 | sortIndex[step][suffixes[i].idx] = i 128 | } 129 | } 130 | } 131 | 132 | return suffixes, sortIndex 133 | } 134 | -------------------------------------------------------------------------------- /data-structures/suffix-array/suffix_array_test.go: -------------------------------------------------------------------------------- 1 | package suffix_array 2 | 3 | import "testing" 4 | 5 | func TestSuffixArrayConstruction(t *testing.T) { 6 | str := "banana" 7 | sa, _ := ConstructSuffixArray(str) 8 | 9 | if len(sa) != 6 { 10 | t.Errorf("Suffix array was not contructed properly") 11 | } 12 | 13 | res := []int{5, 3, 1, 0, 4, 2} 14 | for i, a := range sa.ToSuffixArray() { 15 | if a != res[i] { 16 | t.Errorf("Suffix array was not sorted properly", a, "is out of place") 17 | } 18 | } 19 | } 20 | 21 | func TestSuffixArraySearch(t *testing.T) { 22 | str := "banana" 23 | sa, _ := ConstructSuffixArray(str) 24 | s, _ := sa.Search(str, "na") 25 | 26 | if s != 4 { 27 | t.Errorf("Suffix Array search did not find `na` at index 4") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /design-patterns/adapter/README.md: -------------------------------------------------------------------------------- 1 | # Adpater Design Pattern 2 | 3 | Adapter pattern works as a bridge between two incompatible interfaces. This 4 | type of design pattern comes under structural pattern as this pattern combines 5 | the capability of two independent interfaces. 6 | 7 | This pattern involves a single class which is responsible to join 8 | functionalities of independent or incompatible interfaces. A real life example 9 | could be a case of card reader which acts as an adapter between memory card and 10 | a laptop. You plugin the memory card into card reader and card reader into the 11 | laptop so that memory card can be read via laptop. 12 | -------------------------------------------------------------------------------- /design-patterns/adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | type MediaPlayer interface { 10 | Play(audioType, filename string) (string, error) 11 | } 12 | 13 | type AdvancedMediaPlayer interface { 14 | PlayVLC(filename string) string 15 | PlayMP3(filename string) string 16 | } 17 | 18 | type VLCPlayer struct{} 19 | 20 | func (vlc *VLCPlayer) PlayVLC(filename string) string { 21 | return fmt.Sprintf("Playing VLC file '%s'", filename) 22 | } 23 | 24 | func (vlc *VLCPlayer) PlayMP3(filename string) string { 25 | return "" 26 | } 27 | 28 | type MP3Player struct{} 29 | 30 | func (mp3 *MP3Player) PlayVLC(filename string) string { 31 | return "" 32 | } 33 | 34 | func (mp3 *MP3Player) PlayMP3(filename string) string { 35 | return fmt.Sprintf("Playing MP3 file '%s'", filename) 36 | } 37 | 38 | type MediaAdapter struct { 39 | player AdvancedMediaPlayer 40 | } 41 | 42 | func NewMediaAdapter(audioType string) (*MediaAdapter, error) { 43 | var player AdvancedMediaPlayer 44 | if strings.ToLower(audioType) == "vlc" { 45 | player = &VLCPlayer{} 46 | } else if strings.ToLower(audioType) == "mp3" { 47 | player = &MP3Player{} 48 | } else { 49 | return nil, errors.New("Unsupported media type") 50 | } 51 | return &MediaAdapter{player: player}, nil 52 | } 53 | 54 | func (a *MediaAdapter) Play(audioType, filename string) (string, error) { 55 | if strings.ToLower(audioType) == "vlc" { 56 | return a.player.PlayVLC(filename), nil 57 | } else if strings.ToLower(audioType) == "mp3" { 58 | return a.player.PlayMP3(filename), nil 59 | } else { 60 | return "", errors.New("Unsupported media type") 61 | } 62 | } 63 | 64 | type AudioPlayer struct{} 65 | 66 | func NewAudioPlayer() *AudioPlayer { 67 | return &AudioPlayer{} 68 | } 69 | 70 | func (a *AudioPlayer) Play(audioType, filename string) (string, error) { 71 | adapter, err := NewMediaAdapter(audioType) 72 | if err != nil { 73 | return "", err 74 | } 75 | return adapter.Play(audioType, filename) 76 | } 77 | -------------------------------------------------------------------------------- /design-patterns/adapter/adapter_test.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import "testing" 4 | 5 | func TestAdapter(t *testing.T) { 6 | player := NewAudioPlayer() 7 | 8 | _, mp3err := player.Play("mp3", "some_file.mp3") 9 | _, vlcerr := player.Play("vlc", "some_file.vlc") 10 | _, mp4err := player.Play("mp4", "some_file.mp4") 11 | 12 | if mp3err != nil { 13 | t.Errorf("MP3 tracks should be playable") 14 | } 15 | 16 | if vlcerr != nil { 17 | t.Errorf("VLC tracks should be playable") 18 | } 19 | 20 | if mp4err == nil { 21 | t.Errorf("MP4 tracks should NOT be playable") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /design-patterns/decorator/README.md: -------------------------------------------------------------------------------- 1 | # Decorator Design Pattern 2 | 3 | The decorator pattern is a design pattern that allows behavior to be added to an 4 | individual object, either statically or dynamically, without affecting the 5 | behavior of other objects from the same class. The decorator pattern is 6 | often useful for adhering to the Single Responsibility Principle, as it allows 7 | functionality to be divided between classes with unique areas of concern. 8 | -------------------------------------------------------------------------------- /design-patterns/decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | type IceCream interface { 4 | GetToppings() []string 5 | AddTopping(string) 6 | } 7 | 8 | type VanillaIceCream struct { 9 | Toppings []string 10 | } 11 | 12 | func NewVanillaIceCream() IceCream { 13 | var toppings []string 14 | return &VanillaIceCream{Toppings: toppings} 15 | } 16 | 17 | func (i *VanillaIceCream) GetToppings() []string { 18 | return i.Toppings 19 | } 20 | 21 | func (i *VanillaIceCream) AddTopping(topping string) { 22 | i.Toppings = append(i.Toppings, topping) 23 | } 24 | 25 | type Sprinkles struct { 26 | icecream IceCream 27 | } 28 | 29 | func AddSprinkles(icecream IceCream) IceCream { 30 | icecream.AddTopping("Sprinkles") 31 | return &Sprinkles{icecream: icecream} 32 | } 33 | 34 | func (s Sprinkles) GetToppings() []string { 35 | return s.icecream.GetToppings() 36 | } 37 | 38 | func (s Sprinkles) AddTopping(topping string) { 39 | s.icecream.AddTopping(topping) 40 | } 41 | 42 | type ChocolateSauce struct { 43 | icecream IceCream 44 | } 45 | 46 | func AddChocolateSauce(icecream IceCream) IceCream { 47 | icecream.AddTopping("Chocolate Sauce") 48 | return &ChocolateSauce{icecream: icecream} 49 | } 50 | 51 | func (s ChocolateSauce) GetToppings() []string { 52 | return s.icecream.GetToppings() 53 | } 54 | 55 | func (s ChocolateSauce) AddTopping(topping string) { 56 | s.icecream.AddTopping(topping) 57 | } 58 | -------------------------------------------------------------------------------- /design-patterns/decorator/decorator_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import "testing" 4 | 5 | func TestDecorator(t *testing.T) { 6 | var toppings []string 7 | icecream := NewVanillaIceCream() 8 | toppings = icecream.GetToppings() 9 | 10 | if len(toppings) != 0 { 11 | t.Error("Plain vanilla ice cream should have no toppings") 12 | } 13 | 14 | icecream = AddSprinkles(icecream) 15 | toppings = icecream.GetToppings() 16 | 17 | if len(toppings) != 1 { 18 | t.Error("Vanilla ice cream should have one topping") 19 | } 20 | 21 | if toppings[0] != "Sprinkles" { 22 | t.Error("Sprinkles should be in the toppings list") 23 | } 24 | 25 | icecream = AddChocolateSauce(icecream) 26 | toppings = icecream.GetToppings() 27 | 28 | if len(toppings) != 2 { 29 | t.Error("Vanilla ice cream should have two toppings") 30 | } 31 | 32 | if toppings[1] != "Chocolate Sauce" { 33 | t.Error("Chocolate Sauce should be in the toppings list") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /design-patterns/factory/README.md: -------------------------------------------------------------------------------- 1 | # Factory Design Pattern 2 | 3 | The factory design pattern is a way to decouple code through the use of a 4 | common interface and similiar objects. It typically consists of: 5 | 6 | - A factory function that can generate sub-classes 7 | - A common interface which sub-classes conform to 8 | 9 | With these we can create objects without exposing the creation logic to the 10 | client and refer to newly created objects using the common interface. 11 | -------------------------------------------------------------------------------- /design-patterns/factory/factory.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | type Appliance interface { 4 | Start() 5 | Stop() 6 | GetName() string 7 | } 8 | 9 | type ApplianceFactory struct{} 10 | 11 | type Blender struct { 12 | name string 13 | active bool 14 | } 15 | 16 | func (b *Blender) Start() { 17 | b.active = true 18 | } 19 | 20 | func (b *Blender) Stop() { 21 | b.active = false 22 | } 23 | 24 | func (b *Blender) GetName() string { 25 | return b.name 26 | } 27 | 28 | type Microwave struct { 29 | name string 30 | active bool 31 | } 32 | 33 | func (m *Microwave) Start() { 34 | m.active = true 35 | } 36 | 37 | func (m *Microwave) Stop() { 38 | m.active = false 39 | } 40 | 41 | func (m *Microwave) GetName() string { 42 | return m.name 43 | } 44 | 45 | func NewApplianceFactory() *ApplianceFactory { 46 | return &ApplianceFactory{} 47 | } 48 | 49 | func (f *ApplianceFactory) MakeBlender() *Blender { 50 | return &Blender{name: "Blender", active: false} 51 | } 52 | 53 | func (f *ApplianceFactory) MakeMicrowave() *Microwave { 54 | return &Microwave{name: "Microwave", active: false} 55 | } 56 | -------------------------------------------------------------------------------- /design-patterns/factory/factory_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import "testing" 4 | 5 | func TestFactory(t *testing.T) { 6 | var appliances []Appliance 7 | 8 | factory := NewApplianceFactory() 9 | blender := factory.MakeBlender() 10 | microwave := factory.MakeMicrowave() 11 | 12 | appliances = append(appliances, blender) 13 | appliances = append(appliances, microwave) 14 | 15 | namesShouldEqual := []string{"Blender", "Microwave"} 16 | 17 | for i, appliance := range appliances { 18 | if appliance.GetName() != namesShouldEqual[i] { 19 | t.Errorf("Appliance names do not match") 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /design-patterns/iterator/README.md: -------------------------------------------------------------------------------- 1 | # Iterator Design Pattern 2 | 3 | the iterator pattern is a design pattern in which an iterator is used to 4 | traverse a container and access the container's elements. The iterator pattern 5 | decouples algorithms from containers; in some cases, algorithms are necessarily 6 | container-specific and thus cannot be decoupled. 7 | -------------------------------------------------------------------------------- /design-patterns/iterator/iterator.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | type Iterator interface { 4 | HasNext() bool 5 | Next() interface{} 6 | } 7 | 8 | type Recipe struct { 9 | name string 10 | ingredients []string 11 | index int 12 | } 13 | 14 | func NewRecipe(name string, ingredients []string) *Recipe { 15 | return &Recipe{name: name, ingredients: ingredients, index: 0} 16 | } 17 | 18 | func (r *Recipe) HasNext() bool { 19 | if r.index < len(r.ingredients) { 20 | return true 21 | } 22 | return false 23 | } 24 | 25 | func (r *Recipe) Next() interface{} { 26 | if r.HasNext() { 27 | curIdx := r.index 28 | r.index++ 29 | return r.ingredients[curIdx] 30 | } 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /design-patterns/iterator/iterator_test.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | import "testing" 4 | 5 | func TestIterator(t *testing.T) { 6 | ingredients := []string{"eggs", "flour", "butter"} 7 | recipe := NewRecipe("Cake", ingredients) 8 | 9 | for i := 0; recipe.HasNext(); i++ { 10 | nextIngredient := recipe.Next() 11 | if nextIngredient != ingredients[i] { 12 | t.Errorf("Expected '%s' found '%s'", ingredients[i], nextIngredient.(string)) 13 | } 14 | } 15 | 16 | if recipe.Next() != nil { 17 | t.Errorf("Iterator should be terminated") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /design-patterns/mvc/README.md: -------------------------------------------------------------------------------- 1 | # MVC Design Pattern 2 | 3 | MVC Pattern stands for Model-View-Controller Pattern. This pattern is used to 4 | separate application's concerns. 5 | 6 | - Model: Model represents an object carrying data. It can also have logic to 7 | update controller if its data changes. 8 | - View: View represents the visualization of the data that model contains. 9 | - Controller: Controller acts on both model and view. It controls the data flow 10 | into model object and updates the view whenever data changes. It keeps view 11 | and model separate. 12 | -------------------------------------------------------------------------------- /design-patterns/mvc/mvc.go: -------------------------------------------------------------------------------- 1 | package mvc 2 | 3 | import "fmt" 4 | 5 | type Student struct { 6 | name string 7 | } 8 | 9 | func NewStudent(name string) *Student { 10 | return &Student{name: name} 11 | } 12 | 13 | func (s *Student) GetName() string { 14 | return s.name 15 | } 16 | 17 | func (s *Student) SetName(name string) { 18 | s.name = name 19 | } 20 | 21 | type StudentView struct{} 22 | 23 | func NewStudentView() *StudentView { 24 | return &StudentView{} 25 | } 26 | 27 | func (v *StudentView) Output(student *Student) string { 28 | return fmt.Sprintf("Student name is %s", student.name) 29 | } 30 | 31 | type StudentController struct { 32 | model *Student 33 | view *StudentView 34 | } 35 | 36 | func NewStudentController(model *Student, view *StudentView) *StudentController { 37 | return &StudentController{model: model, view: view} 38 | } 39 | 40 | func (c *StudentController) SetStudentName(name string) { 41 | c.model.SetName(name) 42 | } 43 | 44 | func (c *StudentController) GetStudent() *Student { 45 | return c.model 46 | } 47 | 48 | func (c *StudentController) View() string { 49 | return c.view.Output(c.GetStudent()) 50 | } 51 | -------------------------------------------------------------------------------- /design-patterns/mvc/mvc_test.go: -------------------------------------------------------------------------------- 1 | package mvc 2 | 3 | import "testing" 4 | 5 | func TestMVC(t *testing.T) { 6 | model := NewStudent("Dan") 7 | view := NewStudentView() 8 | controller := NewStudentController(model, view) 9 | 10 | if controller.View() != "Student name is Dan" { 11 | t.Errorf("Initial view state is incorrect") 12 | } 13 | 14 | controller.SetStudentName("Jesse") 15 | 16 | if controller.View() != "Student name is Jesse" { 17 | t.Errorf("Updated view state is incorrect") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /design-patterns/observer/README.md: -------------------------------------------------------------------------------- 1 | # Observer Design Pattern 2 | 3 | The observer pattern is a software design pattern in which an object, called 4 | the subject, maintains a list of its dependents, called observers, and notifies 5 | them automatically of any state changes, usually by calling one of their 6 | methods. It is mainly used to implement distributed event handling systems. The 7 | Observer pattern is also a key part in the familiar model–view–controller (MVC) 8 | architectural pattern. 9 | -------------------------------------------------------------------------------- /design-patterns/observer/observer.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import "fmt" 4 | 5 | type Observer interface { 6 | Update() 7 | } 8 | 9 | type Subject struct { 10 | observers []Observer 11 | state int 12 | } 13 | 14 | func NewSubject() *Subject { 15 | var observers []Observer 16 | return &Subject{observers: observers} 17 | } 18 | 19 | func (s *Subject) GetState() int { 20 | return s.state 21 | } 22 | 23 | func (s *Subject) SetState(state int) { 24 | s.state = state 25 | s.NotifyAll() 26 | } 27 | 28 | func (s *Subject) Attach(observer Observer) { 29 | s.observers = append(s.observers, observer) 30 | } 31 | 32 | func (s *Subject) NotifyAll() { 33 | for _, observer := range s.observers { 34 | observer.Update() 35 | } 36 | } 37 | 38 | type BinaryObserver struct { 39 | subject *Subject 40 | } 41 | 42 | func NewBinaryObserver(subject *Subject) { 43 | subject.Attach(&BinaryObserver{subject: subject}) 44 | } 45 | 46 | func (o *BinaryObserver) Update() { 47 | fmt.Printf("%b", o.subject.GetState()) 48 | fmt.Println() 49 | } 50 | 51 | type HexObserver struct { 52 | subject *Subject 53 | } 54 | 55 | func NewHexObserver(subject *Subject) { 56 | subject.Attach(&HexObserver{subject: subject}) 57 | } 58 | 59 | func (o *HexObserver) Update() { 60 | fmt.Printf("%x", o.subject.GetState()) 61 | fmt.Println() 62 | } 63 | -------------------------------------------------------------------------------- /design-patterns/observer/observer_test.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | func TestObserver(t *testing.T) { 12 | // This is a hack to capture stdout 13 | old := os.Stdout 14 | r, w, _ := os.Pipe() 15 | os.Stdout = w 16 | 17 | // Make our subject and setup observers 18 | subject := NewSubject() 19 | NewBinaryObserver(subject) 20 | NewHexObserver(subject) 21 | 22 | // Set the state 23 | subject.SetState(27) 24 | 25 | // More hack to capture stdout 26 | outC := make(chan string) 27 | // copy the output in a separate goroutine so printing can't block indefinitely 28 | go func() { 29 | var buf bytes.Buffer 30 | io.Copy(&buf, r) 31 | outC <- buf.String() 32 | }() 33 | 34 | // back to normal state 35 | w.Close() 36 | os.Stdout = old 37 | out := <-outC 38 | 39 | if !strings.Contains(out, "11011") { 40 | t.Errorf("The binary observer did not work") 41 | } 42 | 43 | if !strings.Contains(out, "1b") { 44 | t.Errorf("The hex observer did not work") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /design-patterns/singleton/README.md: -------------------------------------------------------------------------------- 1 | # Singleton Design Pattern 2 | 3 | The singleton design pattern is creational and provides an interface to create 4 | one master object and no other instances of itself. This is useful when exactly 5 | one object is needed to coordinate actions across the system. 6 | -------------------------------------------------------------------------------- /design-patterns/singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "sync" 4 | 5 | type SingleObject struct{} 6 | 7 | var instance *SingleObject 8 | var once sync.Once 9 | 10 | func GetInstance() *SingleObject { 11 | once.Do(func() { 12 | instance = &SingleObject{} 13 | }) 14 | return instance 15 | } 16 | 17 | func (o SingleObject) printMessage() string { 18 | return "Hello, world!" 19 | } 20 | -------------------------------------------------------------------------------- /design-patterns/singleton/singleton_test.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "testing" 4 | 5 | func TestSingleton(t *testing.T) { 6 | obj := GetInstance() 7 | obj2 := GetInstance() 8 | 9 | if obj != obj2 { 10 | t.Errorf("The singleton instances should be the same object") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /interview-questions/strings/is_substring.go: -------------------------------------------------------------------------------- 1 | package interview 2 | 3 | import "strings" 4 | 5 | func IsSubstring(s, t string) bool { 6 | l := len(s) 7 | if l == len(t) && l > 0 { 8 | s1s1 := s + s 9 | return strings.Contains(s1s1, t) 10 | } 11 | return false 12 | } 13 | -------------------------------------------------------------------------------- /interview-questions/strings/is_substring_test.go: -------------------------------------------------------------------------------- 1 | package interview 2 | 3 | import "testing" 4 | 5 | func TestIsSubstring(t *testing.T) { 6 | x := "waterbottle" 7 | y := "erbottlewat" 8 | 9 | if !IsSubstring(x, y) { 10 | t.Errorf("'%s' should be a rotation of '%s'", y, x) 11 | } 12 | 13 | x = "waterbottl" 14 | y = "erbottlewat" 15 | 16 | if IsSubstring(x, y) { 17 | t.Errorf("'%s' should NOT be a rotation of '%s'", y, x) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /interview-questions/strings/run_length_encoding.go: -------------------------------------------------------------------------------- 1 | package interview 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | ) 7 | 8 | // Encode takes a string and compresses sequences into numeric 9 | // representations such as converting `aabcccccc` to `a2bc6`. 10 | func Encode(s string) string { 11 | if len(s) == 0 { 12 | return s 13 | } 14 | 15 | var buffer bytes.Buffer 16 | last, count := s[0], 1 17 | 18 | for i := 1; i < len(s); i++ { 19 | if s[i] == last { 20 | count++ 21 | } else { 22 | buffer.WriteString(string(last)) 23 | if count > 1 { 24 | buffer.WriteString(strconv.Itoa(count)) 25 | } 26 | last = s[i] 27 | count = 1 28 | } 29 | } 30 | 31 | buffer.WriteString(string(last)) 32 | if count > 1 { 33 | buffer.WriteString(strconv.Itoa(count)) 34 | } 35 | return buffer.String() 36 | } 37 | 38 | // Decode takes an encoded string and expands the numeric representations into 39 | // repeated characters again. 40 | func Decode(s string) string { 41 | if len(s) == 0 { 42 | return s 43 | } 44 | 45 | var buffer bytes.Buffer 46 | last := s[0] 47 | 48 | for i := 1; i < len(s); i++ { 49 | if val, err := strconv.Atoi(string(s[i])); err == nil { 50 | for i := 1; i < val; i++ { 51 | buffer.WriteString(string(last)) 52 | } 53 | } else { 54 | buffer.WriteString(string(last)) 55 | last = s[i] 56 | } 57 | } 58 | 59 | buffer.WriteString(string(last)) 60 | 61 | return buffer.String() 62 | } 63 | -------------------------------------------------------------------------------- /interview-questions/strings/run_length_encoding_test.go: -------------------------------------------------------------------------------- 1 | package interview 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestEncode(t *testing.T) { 9 | s1 := "abc" 10 | s1ShouldBe := "abc" 11 | encoded1 := Encode(s1) 12 | 13 | s2 := "abccc" 14 | s2ShouldBe := "abc3" 15 | encoded2 := Encode(s2) 16 | 17 | s3 := "aaaabbcccdeee" 18 | s3ShouldBe := "a4b2c3de3" 19 | encoded3 := Encode(s3) 20 | 21 | if s1 != encoded1 { 22 | t.Errorf("Length of '%s' should be the same as the encoded string", s1) 23 | } 24 | 25 | if s1ShouldBe != encoded1 { 26 | t.Errorf("string '%s' is not the same as '%s'", s1, s1ShouldBe) 27 | } 28 | 29 | if len(s2) == len(encoded2) { 30 | t.Errorf("Length of encoded string '%s' should not be the same as initial string", s2) 31 | } 32 | 33 | if s2ShouldBe != encoded2 { 34 | t.Errorf("string '%s' is not the same as '%s'", s2, s2ShouldBe) 35 | } 36 | 37 | if len(s3) == len(encoded3) { 38 | t.Errorf("Length of encoded string '%s' should not be the same as initial string", s3) 39 | } 40 | 41 | if s3ShouldBe != encoded3 { 42 | t.Errorf("string '%s' is not the same as '%s'", s3, s3ShouldBe) 43 | } 44 | } 45 | 46 | func TestDecode(t *testing.T) { 47 | s1 := "abc" 48 | encoded1 := Encode(s1) 49 | decoded1 := Decode(encoded1) 50 | 51 | s2 := "abccc" 52 | encoded2 := Encode(s2) 53 | decoded2 := Decode(encoded2) 54 | 55 | s3 := "aaaabbcccdeee" 56 | encoded3 := Encode(s3) 57 | decoded3 := Decode(encoded3) 58 | 59 | if strings.Compare(s1, decoded1) != 0 { 60 | t.Errorf("String '%s' should be the same as the decoded string but is '%s'", s1, decoded1) 61 | } 62 | 63 | if strings.Compare(s2, decoded2) != 0 { 64 | t.Errorf("String '%s' should be the same as the decoded string but is '%s'", s2, decoded2) 65 | } 66 | 67 | if strings.Compare(s3, decoded3) != 0 { 68 | t.Errorf("String '%s' should be the same as the decoded string but is '%s'", s3, decoded3) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /notes/asymptotic_notation.md: -------------------------------------------------------------------------------- 1 | # Asymptotic Notation 2 | 3 | When dealing with algorithms, it's important to understand the complexity and 4 | running time of them in order to make decisions about efficiency. In most 5 | cases, there are three different parts of Asymptotic Notation including: 6 | big-Θ(Theta), big-O, and big-Ω(Omega). 7 | 8 | ## Big-Θ 9 | 10 | With big-Θ (big theta) we can easily deduce a problem by reasoning that the 11 | constant factors become negligible at a large point. So for instance, in the 12 | equation `6n^2 + 100n + 300` we can see that `n^2` is going to be the highest 13 | term in the sequence and at some point the other numbers will not affect the 14 | equation in a significant way. So based on that, we can drop the `100n + 300` 15 | and we're left with just `6n^2`. Even here, the exponent will make the 16 | multiplier insignificant at some point so we can drop this as well and we're 17 | left with just `n^2`. 18 | 19 | We can then say that this equation (algorithm) is Θ(n^2). 20 | 21 | When we say this, we mean that for a large bound of n, we can see that our 22 | complexity will be between an upper bound of `k2 * n^2` and a lower bound of 23 | `k1 * n^2`. This takes into account compiler time, the language, the compiler 24 | being used, etc and gives us a fair estimation of what we can expect. 25 | 26 | Some samples or common big-theta notation and their types are: 27 | 28 | **Constant** 29 | 30 | - `Θ(1)` 31 | 32 | **Linear** 33 | 34 | - `Θ(lg n)` 35 | - `Θ(n)` 36 | - `Θ(n lg n)` 37 | 38 | **Polynomial** 39 | 40 | - `Θ(n^2)` 41 | - `Θ(n^2 lg n)` 42 | - `Θ(n^3)` 43 | 44 | **Exponential** 45 | 46 | - `Θ(2^n)` 47 | 48 | ## Big-O 49 | 50 | Where Big-Theta gives us a value for an algorithm both above and below, Big-O 51 | notation only gives us an estimation based on the upper bound value. In the 52 | computer science world, we use Big-O to describe the best case scenario at 53 | times. For instance, with Binary Search we can see that saying it runs 54 | `Θ(lg n)` is not always correct since it may be more efficient than that. For 55 | instance, if we guess the value on the first iteration, then we are looking at 56 | performance as `Θ(1)`. For this reason, we can use Big-O notation to note the 57 | worse case scenario with probability that it can run better. 58 | 59 | So we can say best that binary search is `O(lg n)`. 60 | 61 | ## Big-Ω 62 | 63 | In contrast to Big-O, we can say that Big-Omega gives us the least amount of 64 | time for an algorithm to run. This means we have just a lower bound and no 65 | upper bound. With this, we can make assertions that binary search is in fact 66 | `Ω(1)` meaning that binary search is at least constant. 67 | 68 | These types of assumptions are not precise but still give us a good way to 69 | define an algorithm. 70 | -------------------------------------------------------------------------------- /notes/powers_of_two.md: -------------------------------------------------------------------------------- 1 | # Powers of Two Table 2 | 3 | The following table shows the powers of two as a quick reference for people. I 4 | originally saw it in the book [Cracking the Coding Interview](https://www.amazon.com/Cracking-Coding-Interview-Programming-Questions/dp/098478280X) 5 | and figured it should stay here. 6 | 7 | | Power of 2 | Exact Value | Approximate Value | Approx Bytes | 8 | |:----------:|:------------------:|:-----------------:|:------------:| 9 | | 7 | 128 | | | 10 | | 8 | 256 | | | 11 | | 10 | 1024 | 1 Thousand | 1K | 12 | | 16 | 65,536 | | 64K | 13 | | 20 | 1,048,576 | 1 Million | 1MB | 14 | | 30 | 1,073,741,824 | 1 Billion | 1GB | 15 | | 32 | 4,294,967,296 | | 4GB | 16 | | 40 | 1,099,511,627,776 | 1 Trillion | 1TB | 17 | --------------------------------------------------------------------------------