├── .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 |
--------------------------------------------------------------------------------