├── .gitattributes ├── .gitignore ├── algorithm-implementation └── heapsort │ ├── main.go │ └── readme.md ├── arrays ├── arrayPermutation │ ├── main.go │ └── readme.md ├── doTwoNumbersInArrayEqualK │ └── readme.md ├── findSubarrayWithMaximumSum │ └── main.go ├── firstDuplicateMinimumIdx │ ├── main.go │ └── readme.md ├── generateAllSubarrays │ ├── main.go │ └── readme.md ├── groupAnagrams │ ├── main.go │ └── readme.md ├── moveZeroes │ ├── main.go │ └── readme.md ├── multiplyArraySubtractIndex │ ├── main.go │ └── readme.md └── separateThreeValues │ ├── main.go │ └── readme.md ├── backtracking ├── generate-all-binary-strings │ ├── main.go │ └── readme.md ├── knightsTour │ ├── main.go │ └── readme.md ├── permutations │ ├── main.go │ └── readme.md ├── permutations2 │ ├── main.go │ └── readme.md ├── ratMaze │ ├── main.go │ └── readme.md ├── subsets │ ├── main.go │ └── readme.md ├── subsets2 │ ├── betterSolution │ │ └── betterSolution.go │ ├── hacky │ │ └── hacky.go │ └── readme.md └── wordSearch │ ├── main.go │ └── readme.md ├── binarySearch └── buildArray │ └── readme.md ├── binaryTree ├── 2ndHighestNodeBST │ ├── main.go │ └── readme.md ├── closestLeafToNode │ ├── main.go │ └── readme.md ├── countNodesRecursive │ ├── main.go │ └── readme.md ├── courseListingsOrder │ └── readme.md ├── findDeepestNode │ ├── main.go │ └── readme.md ├── isBinaryTreeSymmetric │ ├── main.go │ └── readme.md ├── lowestCommonAncestor │ └── main.go ├── printBinaryTree │ └── readme.md ├── returnRootToLeaves │ ├── main.go │ └── readme.md └── reverseBinaryTree │ ├── main.go │ └── readme.md ├── dataStructures ├── lfuCache │ ├── main.go │ └── readme.md ├── lruCache │ ├── cache.go │ └── readme.md └── minStack │ ├── main.go │ └── readme.md ├── dynamicProgramming ├── bestTimeBuySellStock │ ├── main.go │ └── readme.md ├── coinChange │ ├── main.go │ └── readme.md ├── dungeonGame │ ├── main.go │ └── readme.md ├── goldMineProblem │ ├── main.go │ └── readme.md ├── knapsack01 │ ├── main.go │ └── readme.md ├── longestIncreasingSubsequence │ ├── main.go │ └── readme.md ├── maxSumOfThreeSubarrays │ ├── main.go │ └── readme.md ├── minCostClimbingStairs │ └── readme.md ├── minimumStepsToOne │ ├── main.go │ └── readme.md ├── nStaircase │ ├── main.go │ └── readme.md ├── paintHouses │ ├── main.go │ └── readme.md └── robHousesStreet │ ├── main.go │ └── readme.md ├── graphs ├── boggleWordFind │ └── main.go ├── connectedComponentsUnconnectedGraph │ └── readme.md ├── data │ └── dag │ │ ├── sharedDag.json │ │ ├── sharedNoCycleDag.json │ │ └── source.md ├── isDag │ ├── main.go │ └── readme.md ├── isInGraph │ ├── List.go │ ├── isInGraph.go │ ├── isInGraph_Test.go │ └── readme.md ├── iterativeDeepeningDepthFirstSearch │ └── main.go ├── readme.md ├── topologicalSort │ ├── main.go │ ├── nodeDag.json │ └── readme.md └── utils │ └── utils.go ├── greedy └── jumpGame │ ├── main.go │ └── readme.md ├── hashMap └── addTwoNumbers │ ├── main.go │ └── readme.md ├── increasing-triplet-subsequence ├── main.go └── readme.md ├── lengthLongestSubstring ├── main.go ├── main_test.go └── readme.md ├── linkedlist ├── ConstantReverseLL │ ├── main.go │ └── readme.md ├── deleteLLNode │ ├── main.go │ └── readme.md ├── insertLLNode │ ├── main.go │ └── readme.md └── removeDuplicates │ └── main.go ├── readme.md ├── strings ├── defangingIpAddress │ ├── main.go │ └── readme.md ├── findFirstMaxOccurrence │ ├── main.go │ └── readme.md ├── findLastNonDuplicate │ ├── main.go │ └── readme.md ├── firstNonDuplicate │ ├── main.go │ └── readme.md ├── palindromeIterative │ ├── main.go │ └── readme.md ├── palindromeRecursive │ ├── main.go │ └── readme.md ├── reverseStringRecursively │ └── readme.md ├── shiftStrings │ ├── main.go │ └── readme.md └── uniqueEmailAddresses │ ├── main.go │ └── readme.md └── twoPointers └── anagram-diff ├── anagram.go ├── anagram_test.go └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | readme.md merge=ours 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.un* 3 | -------------------------------------------------------------------------------- /algorithm-implementation/heapsort/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func swap(nums []int, i int, j int) []int { 8 | nums[i] ^= nums[j] 9 | nums[j] ^= nums[i] 10 | nums[i] ^= nums[j] 11 | 12 | return nums 13 | } 14 | 15 | func maxHeapify(nums []int, i int) []int { 16 | left := (i * 2) + 1 17 | right := (i * 2) + 2 18 | largest := i 19 | 20 | if left < len(nums) && nums[left] > nums[largest] { 21 | largest = left 22 | } 23 | 24 | if right < len(nums) && nums[right] > nums[largest] { 25 | largest = right 26 | } 27 | 28 | if largest != i { 29 | nums = swap(nums, largest, i) 30 | nums = maxHeapify(nums, largest) 31 | } 32 | 33 | return nums 34 | } 35 | 36 | func buildMaxHeap(nums []int) []int { 37 | for i := len(nums) / 2; i >= 0; i-- { 38 | nums = maxHeapify(nums, i) 39 | } 40 | 41 | return nums 42 | } 43 | 44 | func heapSort(nums []int) []int { 45 | heapSize := len(nums) - 1 46 | nums = buildMaxHeap(nums) 47 | for i := heapSize; i >= 1; i-- { 48 | swap(nums, 0, i) 49 | buildMaxHeap(nums[0:i]) 50 | } 51 | 52 | return nums 53 | } 54 | 55 | func main() { 56 | fmt.Println(heapSort([]int{1, 4, 3, 5, 2, 6, 5, 7, 6, 8})) 57 | fmt.Println(heapSort([]int{6, 5, 4, 3, 2, 1})) 58 | } 59 | -------------------------------------------------------------------------------- /algorithm-implementation/heapsort/readme.md: -------------------------------------------------------------------------------- 1 | # heapsort 2 | 3 | ## Problem Definition 4 | Code out heapsort using heap functions you define. 5 | 6 | ## Questions to Ask 7 | - GOLANG SPECIFIC - Should I make heapsort just take int slices that I define? 8 | - Should I start the marking of the heap from 0 or 1? (In theory, we always start from 1) 9 | -------------------------------------------------------------------------------- /arrays/arrayPermutation/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func createEmptyStrSlice(len int) (returnArr []string) { 6 | for i := 0; i < len; i++ { 7 | returnArr = append(returnArr, "") 8 | } 9 | return 10 | } 11 | 12 | func returnPermutationNewArray(arr []string, permArr []int) []string { 13 | rArr := createEmptyStrSlice(len(arr)) 14 | for i := 0; i < len(permArr); i++ { 15 | rArr[permArr[i]] = arr[i] 16 | } 17 | 18 | return rArr 19 | } 20 | 21 | func returnPermutationInPlace(arr []int, permArr []int) []int { 22 | for i := 0; i < len(permArr); i++ { 23 | permArr[i] = arr[permArr[i]] 24 | } 25 | 26 | return permArr 27 | } 28 | 29 | func main() { 30 | fmt.Println(returnPermutationNewArray([]string{"a", "b", "c", "d"}, []int{2, 1, 3, 0})) 31 | fmt.Println(returnPermutationInPlace([]int{3, 4, 5, 6}, []int{2, 1, 3, 0})) 32 | } 33 | -------------------------------------------------------------------------------- /arrays/arrayPermutation/readme.md: -------------------------------------------------------------------------------- 1 | # arrayPermutation 2 | 3 | ## Definition 4 | A permutation can be specified by an array P, where P[i] represents the location of the element at i in the permutation. For example, [2, 1, 0] represents the permutation where elements at the index 0 and 2 are swapped. 5 | 6 | Given an array and a permutation, apply the permutation to the array. For example, given the array ["a", "b", "c"] and the permutation [2, 1, 0], return ["c", "b", "a"]. 7 | 8 | ## Solutions 9 | 10 | ### Creating a New Array 11 | The first solution creates a new array of the same length as the original array and assigns the values in the original array at the indices specified by the permutation array. In [Strong-Typed Languages](https://en.wikipedia.org/wiki/Strong_and_weak_typing) like Golang, we need to use a separate array of the type that needs to be returned to arrange the input and the output. 12 | 13 | This implementation runs in O(n) speed since it needs to loop through the permutation array in its entirety in order to set the indices correctly. We also technically incur another O(n) cost to create the new array, but O(n) + O(n) reduces to O(2n), so we still consider it O(n) complexity. 14 | 15 | The space complexity of this solution is also O(n), as we create a separate array to hold all of the values. 16 | 17 | ### Modifying the Permutation Array 18 | In the second solution to the problem, we use the permutation array itself and assign the appropriate values at the permutation index of the first array into the permutation array. 19 | 20 | ## Questions to ask 21 | 1. Will the two input arrays always be the same length? 22 | 1. How should we handle two empty arrays? 23 | 1. Mention the static / dynamic type limitation and talk about the time complexities due to that. -------------------------------------------------------------------------------- /arrays/doTwoNumbersInArrayEqualK/readme.md: -------------------------------------------------------------------------------- 1 | # do two numbers in an array equal k 2 | 3 | ## Problem Definition 4 | Given a list of numbers and a number k, return whether any two numbers from the list add up to k. 5 | 6 | Example: 7 | Input [10, 15, 3, 7], k of 17 8 | 9 | Output is true 10 | 11 | Example: 12 | Input: [1, 3, 5, 8], k of 22 13 | 14 | Output is false. 15 | 16 | Example: 17 | Input: [2, 4, 6, 8], k of 14 18 | 19 | Output is true 20 | 21 | 22 | -------------------------------------------------------------------------------- /arrays/findSubarrayWithMaximumSum/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func max(a, b int) int { 6 | if a > b { 7 | return a 8 | } 9 | 10 | return b 11 | } 12 | 13 | func maximumSumInefficient(arr []int) int { 14 | globalMax := arr[0] 15 | for i := 0; i < len(arr); i++ { 16 | localMax := arr[i] 17 | for j := i + 1; j < len(arr); j++ { 18 | localMax = max(localMax+arr[j], arr[j]) 19 | globalMax = max(localMax, globalMax) 20 | } 21 | } 22 | 23 | return globalMax 24 | } 25 | 26 | func maxSum(arr []int) int { 27 | currentMax, maxEndingHere := arr[0], arr[0] 28 | for idx := 1; idx < len(arr); idx++ { 29 | val := arr[idx] 30 | maxEndingHere = max(maxEndingHere+val, val) 31 | currentMax = max(maxEndingHere, currentMax) 32 | } 33 | 34 | return currentMax 35 | } 36 | 37 | func maximumSumWithIndices(arr []int) (int, []int) { 38 | arrIdxs := []int{0, 0} 39 | currentMax, maxEndingHere := 0, 0 40 | for idx := 0; idx < len(arr); idx++ { 41 | val := arr[idx] 42 | if maxEndingHere+val < val { 43 | arrIdxs[0] = idx 44 | maxEndingHere = val 45 | } else { 46 | maxEndingHere = maxEndingHere + val 47 | } 48 | if maxEndingHere < currentMax { 49 | arrIdxs[1] = idx - 1 50 | } else { 51 | currentMax = maxEndingHere 52 | arrIdxs[1] = idx 53 | } 54 | } 55 | 56 | return currentMax, arrIdxs 57 | } 58 | 59 | func main() { 60 | fmt.Println(max(-3, 1)) 61 | fmt.Println(maxSum([]int{1, 3, 5, 7, 9})) 62 | fmt.Println(maxSum([]int{1, -3, 2, 1, -1})) 63 | fmt.Println(maxSum([]int{1, -3, 2, 1, -1})) 64 | fmt.Println(maxSum([]int{-2, 1, -3, 4, -1, 2, 1, -5, 4})) 65 | fmt.Println(maxSum([]int{-1})) 66 | fmt.Println(maximumSumInefficient([]int{1, -3, 2, 1, -1})) 67 | fmt.Println(maximumSumInefficient([]int{-1})) 68 | fmt.Println(maximumSumWithIndices([]int{1, -3, 2, 1, -1})) 69 | fmt.Println(maximumSumWithIndices([]int{1, -3, 2, 1, -1, -9, 9, -9})) 70 | } 71 | -------------------------------------------------------------------------------- /arrays/firstDuplicateMinimumIdx/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type CountHashMapper struct { 9 | Occurrences, SecondIdx int 10 | } 11 | 12 | func findFirstDuplicate(ints []int) int { 13 | // build a duplicate mechanism 14 | countHash := make(map[int]*CountHashMapper) 15 | for idx, val := range ints { 16 | if _, ok := countHash[val]; ok == false { 17 | countHash[val] = &CountHashMapper{ 18 | Occurrences: 1, 19 | SecondIdx: -1, 20 | } 21 | } else { 22 | (*countHash[val]).Occurrences += 1 23 | if countHash[val].Occurrences == 2 { 24 | (*countHash[val]).SecondIdx = idx 25 | } 26 | } 27 | } 28 | 29 | returnVal, duplicateIdx := -1, math.MaxInt32 30 | 31 | for k, v := range countHash { 32 | if v.Occurrences > 1 { 33 | if v.SecondIdx < duplicateIdx { 34 | returnVal = k 35 | } 36 | } 37 | } 38 | 39 | return returnVal 40 | } 41 | 42 | func findFirstDuplicateOptimized(ints []int) int { 43 | // build a duplicate mechanism 44 | countHash := make(map[int]*CountHashMapper) 45 | 46 | returnVal, duplicateIdx := -1, math.MaxInt32 47 | for idx, val := range ints { 48 | if _, ok := countHash[val]; ok == false { 49 | countHash[val] = &CountHashMapper{ 50 | Occurrences: 1, 51 | } 52 | } else { 53 | (*countHash[val]).Occurrences += 1 54 | if countHash[val].Occurrences == 2 { 55 | if idx < duplicateIdx { 56 | duplicateIdx = idx 57 | returnVal = val 58 | } 59 | } 60 | } 61 | } 62 | 63 | return returnVal 64 | } 65 | 66 | func findFirstDuplicateAbsolute(ints []int) int { 67 | returnVal := -1 68 | for _, val := range ints { 69 | absValInt := int(math.Abs(float64(val))) 70 | if ints[absValInt] < 0 { 71 | returnVal = absValInt 72 | break 73 | } else { 74 | ints[absValInt] = ints[absValInt] * -1 75 | } 76 | } 77 | 78 | return returnVal 79 | } 80 | 81 | func main() { 82 | // test cases 83 | fmt.Println(` 84 | findFirstDuplicateUnoptimized 85 | `) 86 | fmt.Println(findFirstDuplicate([]int{1, 2, 3, 4}) == -1) 87 | fmt.Println(findFirstDuplicate([]int{1, 2, 3, 4, 1}) == 1) 88 | fmt.Println(findFirstDuplicate([]int{1, 2, 3, 4, 2, 1}) == 2) 89 | fmt.Println(findFirstDuplicate([]int{6, 8, 1, 4, 2, 8, 6}) == 8) 90 | 91 | fmt.Println(` 92 | findFirstDuplicateOptimized 93 | `) 94 | fmt.Println(findFirstDuplicateOptimized([]int{1, 2, 3, 4}) == -1) 95 | fmt.Println(findFirstDuplicateOptimized([]int{1, 2, 3, 4, 1}) == 1) 96 | fmt.Println(findFirstDuplicateOptimized([]int{1, 2, 3, 4, 2, 1}) == 2) 97 | fmt.Println(findFirstDuplicateOptimized([]int{6, 8, 1, 4, 2, 8, 6}) == 8) 98 | 99 | fmt.Println(` 100 | findFirstDuplicateAbsolute 101 | `) 102 | fmt.Println(findFirstDuplicateAbsolute([]int{0, 1, 2, 3}) == -1) 103 | fmt.Println(findFirstDuplicateAbsolute([]int{1, 2, 3, 4, 1}) == 1) 104 | fmt.Println(findFirstDuplicateAbsolute([]int{1, 2, 3, 4, 2, 1}) == 2) 105 | fmt.Println(findFirstDuplicateAbsolute([]int{6, 8, 1, 4, 2, 8, 6, 2, 1}) == 8) 106 | } 107 | -------------------------------------------------------------------------------- /arrays/firstDuplicateMinimumIdx/readme.md: -------------------------------------------------------------------------------- 1 | # First Duplicate Minimum Index 2 | 3 | ## Problem Statement 4 | Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. 5 | In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of all other numbers. If there are no elements with duplicates, return -1. 6 | 7 | Examples: 8 | For the array [1, 2, 3, 3, 1, 2, 3], the algorithm should output 3. There are three duplicates, and the index of the second duplicate of the number 3 occurs before the second duplicate of the other numbers. 9 | For the array [1, 2, 3, 1] with only one duplicate, return 1. 10 | For the array [1, 2, 3] with no duplicates, return -1. 11 | 12 | ## Solution 13 | This solution is similar to many other duplicate-based questions in that a standard solution can be derived by using a hash. The twist of needing to record the index of the second occurrence of a number turns the hash into a nested hash. 14 | The map that we construct looks like the following. 15 | **Note: The following is not JSON format** 16 | ``` 17 | { 18 | int: { 19 | Occurrences: int, 20 | SecondIdx: int, 21 | }, 22 | int: { 23 | Occurrences: int, 24 | SecondIdx: int, 25 | }, 26 | } 27 | ``` 28 | 29 | We loop through the array once, recording all occurences of each int in the map. If an int shows multiple times in the array, we increment the Occurrences and record the second index position. 30 | After we have built the map, we loop through the keys of the map, record the minimum duplicate idx and return either the value with the minimum duplicateIdx or -1 if there are no duplicates. 31 | 32 | ## Complexity 33 | The Time complexity of the solution is O(2n) in the worst case of having no duplicates. In this case, we would loop through all the values of the array to build the hash, then loop through all values of the array again when we 34 | loop through the keys of the map. This is reducible to O(n) ultimately. 35 | 36 | The space complexity is also O(2n) since for each value in the array in the worst case we store a map with two pointers. This is also reducible to O(n) and ends up with a O(n) space complexity for this solution. 37 | 38 | ## Optimizations 39 | An optimization we could make for this algorithm would help to reduce the time complexity in the worst-case scenario. If we add a boolean value in the function of hasDuplicates that we flip when a duplicate is found, we can choose not to 40 | loop through the array if that boolean is false. This would keep the time complexity of O(n) but marginally increase the amount of time it takes due to not needing to loop through the hashmap a second time. 41 | 42 | Another optimization is recorded in the firstDuplicateOptimized function. In the first solution I wrote to the firstDuplicateMinimumIndex problem, I utilized a solution I learned from a previous duplicate problem where I needed to return the first occurrence of a duplicate 43 | character in a string (or array). In that particular question, you must loop through the array (or string) once first in order to discover which characters are duplicates then loop through again in sequence to find the first occurrence of a duplicate. 44 | In the current question, although the sequence is important to the solution, we are tasked to find the second occurrence of the element instead of the first. We can find this in one loop through the array (or string) with only recording the occurrences of a particular element in the array. 45 | This saves us space complexity and time complexity. 46 | So the map ends up looking like this: 47 | ``` 48 | { 49 | int: int, 50 | int: int, 51 | } 52 | ``` 53 | 54 | In the optimized solution, our algorithm can solve the problem in O(n) time complexity and O(n) space complexity right off the bat. 55 | Save those frames friends! 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /arrays/generateAllSubarrays/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func bruteForcePrintAllSubarrays(arr []int) { 6 | for i := 0; i < len(arr); i++ { 7 | for j := i + 1; j < len(arr); j++ { 8 | for k := i; k < j; k++ { 9 | fmt.Printf("%d", arr[k]) 10 | } 11 | fmt.Println() 12 | } 13 | } 14 | } 15 | 16 | func verboseOptimizedPrintAllSubarrays(arr []int) { 17 | var result [][]int 18 | for i := 0; i < len(arr); i++ { 19 | var subArrContainer []int 20 | for j := i; j < len(arr); j++ { 21 | subArrContainer = append(subArrContainer, arr[j]) 22 | result = append(result, subArrContainer) 23 | } 24 | } 25 | for i := 0; i < len(result); i++ { 26 | fmt.Println(result[i]) 27 | } 28 | } 29 | 30 | func bestPrintAllSubarrays(arr []int) { 31 | for i := 0; i < len(arr); i++ { 32 | var subArrContainer []int 33 | for j := i; j < len(arr); j++ { 34 | subArrContainer = append(subArrContainer, arr[j]) 35 | fmt.Println(subArrContainer) 36 | } 37 | } 38 | } 39 | 40 | func main() { 41 | // bruteForcePrintAllSubarrays([]int{1, 3, 5, 7, 9}) 42 | // verboseOptimizedPrintAllSubarrays([]int{1, 3, 5, 7, 9}) 43 | bestPrintAllSubarrays([]int{1, 3, 5, 7, 9}) 44 | } 45 | -------------------------------------------------------------------------------- /arrays/generateAllSubarrays/readme.md: -------------------------------------------------------------------------------- 1 | # Print all Subarrays 2 | 3 | ## Problem Statement 4 | Given an array, print out all subarrays in the statement. 5 | 6 | Input of the function will be the an array of ints 7 | The function will print out all subarrays. 8 | 9 | ## Unoptimized Solution 10 | In the unoptimized solution, we nest 3 for loops to print out the correct values. 11 | The first for loop index (i) is the start of each sub-array index. It runs from 0 to the length of the array. 12 | The second for loop (j) is the end of each sub-array index. It runs from i to the length of the array. 13 | The third for loop (k) runs through each index between i and k and focuses on one element to print at a time. 14 | You can think of these two indices as a bookends on a book shelf that partition off a selection of books you can choose from. 15 | The third index would be like the focus of your eyes or your hand running through each book in the book-ended selection. 16 | 17 | If we use the array [1, 3, 5, 7], the i index will start from 0. For each iteration of the first nested loop j, the range of values that are included in the sub-array increase. 18 | When i is 0 and j is 0, the subarray can only be in the range 0, 0. K only runs once and prints out the value of that subarray ([1]). 19 | When i is 0 and j is 1, the subarray can be in the range 0, 1. K will run twice and print out the value of that subarray ([1, 3]). 20 | This continues all the way to the end of the array. 21 | Then, when we get to the end of the array, i increments to 1 and j resets itself. 22 | So the first iteration of the loop will be i at 1 and j at i (1). So in the first iteration, the subarray can only be in the range 1, 1. K will run only once and print out the value of that subarray([3]). 23 | In the second iteration, i will be at 1 and j at 2. The subarray is in the range 1, 2, and k will run twice and print out the value of the subarray ([3, 5]) 24 | 25 | This continues until the end of the array. 26 | 27 | Since their are 3 nested for loops, the time complexity of this algorithm is O(n^3). The space complexity is O(1). We can make it faster than this. 28 | 29 | ## Optimized solution 30 | In the optimized solution, we use a pattern found from a [different problem](https://web.stanford.edu/class/cs9/sample_probs/SubarraySums.pdf) to limit the amount of loops we use. 31 | Instead of using one loop to set the beginning index (i), one loop to set the end index (j), and the third to actually focus on each element (k), we take an approach that mixes the j and k loops together. 32 | In the optimized solution, we use two data structures to help us keep track of all values we touched and all subarrays we have seen. The first data structure is a result array that stores arrays of ints. This represents an array that will store ALL subarrays. This result array of arrays is instantiated before any looping occurs. 33 | The second data structure we use is an array of ints called a subArrContainer. This data structure holds an increasing amount of values that represents the a specific subarray at each tick of the j loop. 34 | 35 | Whereas in the unoptimized solution, at i = 0 and j = 1 the the i and j pointer only acts as bookends to reign in the range of values the k pointer can run through. In the optimized solution, when i = 0 and j = 1, before the loop even begins, the subArrContainer already holds the value that comprises the subarray at i = 0 and and j = 0 ([1]). 36 | After the loop logic executes at i = 0 j = 1, the subArrContainer contains the values of subarray at i = 0 j = 0 ([1]) AND the values that are in the subarray at i = 0, j = 1 ([1, 3]). 37 | Since the subArrContainer contains a valid subarray in each iteration of the j loop, we append the container each time we add a new value to the subArrContainer. 38 | 39 | The time complexity of this solution is O(n^2 + n). The n is the final loop through all of the result subarrays. It can be simplified to O(n^2). 40 | The space complexity should be O(max size of subarray + max size of subarray - 1 + max size of subarray - 2...) for each starting index of a subarray. This is because we will store an array of arrays that contain all subarrays from the longest length down to 1, then from the longest length - 1 down to 1, then from the longest length - 2 down to 1. 41 | I am almost certain there are better math terms to use than what I just used. If you know them, please either make a pr or add an issue. 42 | 43 | ## Best solution 44 | After looking at my "optimized" solution, I realized there was a better one. You can edit out the logic complexity of using the results array and looping through that array by just printing after each subArray container append. 45 | This works because the subArrContainer contains the subArray from index i to the current iteration of index j at this point. You can just print out that subArray each time you append the value to the container and call it a day. 46 | This results in time complexity O(n^2) and space complexity of O(length of subarray). 47 | -------------------------------------------------------------------------------- /arrays/groupAnagrams/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | type sortRunes []rune 9 | 10 | func (s sortRunes) Len() int { 11 | return len(s) 12 | } 13 | 14 | func (s sortRunes) Less (i, j int) bool { 15 | return s[i] < s[j] 16 | } 17 | 18 | func (s sortRunes) Swap (i, j int) { 19 | s[i], s[j] = s[j], s[i] 20 | } 21 | 22 | func SortString(s string) string { 23 | r := []rune(s) 24 | sort.Sort(sortRunes(r)) 25 | return string(r) 26 | } 27 | 28 | func groupAnagrams (ss []string) [][]string { 29 | m := map[string][]string{} 30 | for _, s := range ss { 31 | sorted := SortString(s) 32 | m[sorted] = append(m[sorted], s) 33 | } 34 | 35 | var res [][]string 36 | 37 | for _, strSl := range m { 38 | res = append(res, strSl) 39 | } 40 | 41 | return res 42 | } 43 | 44 | func main() { 45 | strs := []string{"eat", "tea", "tan", "ate", "nat", "bat"} 46 | 47 | fmt.Println(groupAnagrams(strs)) 48 | fmt.Println(groupAnagrams([]string{""})) 49 | fmt.Println(groupAnagrams([]string{"a"})) 50 | // should return 3 groups because the words are not anagrams of one another. 51 | fmt.Println(groupAnagrams([]string{"a", "an", "ant"})) 52 | } -------------------------------------------------------------------------------- /arrays/groupAnagrams/readme.md: -------------------------------------------------------------------------------- 1 | # Group Anagrams 2 | 3 | ## Problem Definition 4 | Given an array of strings as input, group all of the anagrams together into their own array. Return an array of all of the anagram arrays 5 | grouped this way. 6 | 7 | Input: myStrings = ["bat", "ant", "tan", "eat", "nat", "ate", "tea"] 8 | Output: [["bat"], ["ant", "tan", "nat"], ["eat", "ate", "tea"]] 9 | 10 | Note: A word is an anagram of another if it uses the exact same characters as the other word. 11 | 12 | So "ant" and "tan", are anagrams, but "bat" and "eat" are not because of the "b" and "e". 13 | 14 | Also, "at" is not an anagram of "ate" because they don't have the exact same characters. 15 | 16 | ## Explanation 17 | My first intuition was to use a map of string to array of strings (or slice of strings in golang map[string][]string) to store the values, but this isn't the actual brute force way to solve the problem. 18 | 19 | The brute force way can loop through the characters in each string and compare the amount of characters it has in the string to each other string we have seen before. 20 | 21 | So if we run it on the example above it could look like the following: 22 | 23 | ``` 24 | Input: myStrings = ["bat", "ant", "tan", "eat", "nat", "ate", "tea"] 25 | Output: [["bat"], ["ant", "tan", "nat"], ["eat", "ate", "tea"]] 26 | 27 | The answer starts out as an empty array [] 28 | 29 | As we loop through "bat", we see that there are no groups in the answer, so we can append "bat". 30 | 31 | The answer will be [["bat"]] after we see "bat". 32 | 33 | 34 | For "ant", we start looping through the strings in all of our groups to see if there is a match. 35 | 36 | We will first encounter "bat", and we can loop through the characters of "ant" or "bat" to see if they match one another. 37 | 38 | They do not, so we will append "ant" to it's own array in the result. 39 | 40 | We continue doing this until the problem is complete. 41 | 42 | The time complexity of the following will be O(n^2) since we could compare all of the characters in all of the strings for each string in the worst case scenario. 43 | 44 | The space complexity should be O(n * l) where n is the length of the input array and l is the length of each string. 45 | ``` 46 | 47 | There are methods we can use to make the character comparisons in the two strings quicker, but it is much more useful to just use a different data structure to groups of strings instead. 48 | 49 | ## Optimizations 50 | 51 | In a more optimal solution, we use a hash table to store the sorted string characters, sort each string as we loop through and either add it to the hash table when it matches a previous key or create a new entry when it is a new key. 52 | 53 | The approach using this method is as follows: 54 | 55 | ``` 56 | Input: myStrings = ["bat", "ant", "tan", "eat", "nat", "ate", "tea"] 57 | Output: [["bat"], ["ant", "tan", "nat"], ["eat", "ate", "tea"]] 58 | 59 | 1. Loop through each string in the input. 60 | 61 | 2. For each string, sort the string {lexographically} and save the sorted result. 62 | 63 | 3. See if the sorted string is in the hash table. 64 | -- 1. If so, append the unsorted string to this group. 65 | -- 2. If not, create a new group with the unsorted string as its first entry. 66 | 67 | 4. Finally, loop through the hash table and append each group to the result array. 68 | ``` 69 | 70 | The time complexity of the solution will depend on which sort you use to implement the character sorting in the strings. 71 | 72 | Using a comparison based sort (like the one I used in the solution) will have a O(n log n) time complexity and O(1) space complexity if you use an in-place sort like Quicksort. 73 | 74 | The benefits of this type of sort is that it will not have huge performance hits when you change the set of characters used in the input. 75 | 76 | If the character set that could be in the set of strings are lower case letters or some other bounded character sit, you can sort a string using [Counting Sort](https://en.wikipedia.org/wiki/Counting_sort) in O(n). 77 | 78 | Our algorithm will group anagrams in O(N * L) in this circumstance where the N is the number of strings and the L is the length of each string. 79 | 80 | ## Complexities 81 | Brute Force comparing arrays to arrays: O(N^2) time and O(N * L) space. 82 | 83 | Optimization using comparison based sort: O(N * l log l) time and O(N * L) space. 84 | 85 | Optimization using counting sort: O(N * L) time and O(N * length of character set) space. -------------------------------------------------------------------------------- /arrays/moveZeroes/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // [1, 2, 0, 3, 0, 5] 6 | // [1, 2, 3, 5, 0, 0] 7 | 8 | func moveZeroesToEnd(arr []int) []int { 9 | i := 0 10 | for j := 0; j < len(arr); j++ { 11 | if arr[j] != 0 { 12 | arr[i] = arr[j] 13 | i++ 14 | } 15 | } 16 | for ; i < len(arr); i++ { 17 | arr[i] = 0 18 | } 19 | 20 | return arr 21 | } 22 | 23 | // [1, 2, 0, 3, 0, 5] 24 | // [0, 0, 1, 2, 3, 5] 25 | 26 | func moveZeroesToBeginning(arr []int) []int { 27 | i := len(arr) - 1 28 | for j := len(arr) - 1; j >= 0; j-- { 29 | if arr[j] != 0 { 30 | arr[i] = arr[j] 31 | i-- 32 | } 33 | } 34 | for ; i >= 0; i-- { 35 | arr[i] = 0 36 | } 37 | 38 | return arr 39 | } 40 | 41 | func main() { 42 | fmt.Println("vim-go") 43 | fmt.Println(moveZeroesToEnd([]int{1, 2, 0, 3, 0, 5})) 44 | fmt.Println(moveZeroesToBeginning([]int{1, 2, 0, 3, 0, 5})) 45 | } 46 | -------------------------------------------------------------------------------- /arrays/moveZeroes/readme.md: -------------------------------------------------------------------------------- 1 | # Move Zeroes 2 | 3 | ## Problem Definition 4 | Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements. 5 | 6 | For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0]. 7 | 8 | Note: You must do this in-place without making a copy of the array. Minimize the total number of operations. 9 | 10 | ## Explanation 11 | I have previously done a similar problem where we move all 0s to the end of the array in any order by having one pointer start at the beginning and the other start from the end (len(arr) - 1) of the array and switch them when there are 0s, so I thought this problem might be similar. 12 | A better way though is to loop through the array in order, keeping one pointer to progress through the loop and the other to hold a spot for when you encounter non-0 values to swap. 13 | 14 | In the test array we use [1, 2, 0, 3, 0, 5], when we enounter the first element one, we will be swapping the value of and 0, which will still be 1, incrementing the index of the place-holder pointer by one, and continuing the loop. 15 | 16 | We continue to increment both pointers in this way until we hit the 3 index (arr[2]) of the array. Since this is our first 0, we will not be swapping the j (at index 2 at this point) with i (at index 1). This iteration of the loop will finish, then the j pointer will increment again. 17 | 18 | This next index is the first time the swap will actually happen successfully. Since the value at index j (at index 3 now) is not a 0, we will be swapping it with pointer i at the previous index (2). 19 | 20 | The result of this operation will be [1, 2, 3, 0, 0, 5]. 21 | 22 | The loop will continue until the j pointer reaches the final index of the array, 5. At this point, we will again swap the j and i pointers leaving the array at [1, 2, 3, 5, 0, 5] (the extra five being there due to the final swap between the i pointer and the j pointer). 23 | 24 | At this point in time, we have swapped all of the non-zero integers into their correct places, but we haven't finished moving the zeroes to the end of the array. The j index is already at the length of the array, so we run another for loop (a while loop in other languages) to run until the i index 25 | reaches the length of the array. 26 | 27 | In each iteration of this loop, we set the i index in the array to 0. 28 | 29 | We can move zeroes to the beginning by looping through the array in reverse and running the same logic. 30 | 31 | ## Complexity 32 | The space complexity of the following operation is O(1) and the time complexity is O(2 * length of array) because we could potentially need to loop over the entire array twice if the array only has 0 values in the worst case. 33 | O(2n) reduces to O(n). 34 | -------------------------------------------------------------------------------- /arrays/multiplyArraySubtractIndex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func multiplyArraySubtractIndex(input []int) []int { 8 | total := 1 9 | 10 | for i := 0; i < len(input); i++ { 11 | if input[i] > 0 { 12 | total *= input[i] 13 | } 14 | } 15 | 16 | for i := len(input) - 1; i >= 0; i-- { 17 | if input[i] > 0 { 18 | input[i] = total / input[i] 19 | } else { 20 | input[i] = total 21 | } 22 | } 23 | 24 | return input 25 | } 26 | 27 | func main() { 28 | fmt.Println(multiplyArraySubtractIndex([]int{1, 2, 3, 4, 5})) 29 | fmt.Println(multiplyArraySubtractIndex([]int{3, 2, 1, 0})) 30 | } 31 | -------------------------------------------------------------------------------- /arrays/multiplyArraySubtractIndex/readme.md: -------------------------------------------------------------------------------- 1 | # multiplyArraySubtractIndex 2 | 3 | ## Definition 4 | Given an array of integers, return a new array such that each element at index i of the new array is the product of all the numbers in the original array except the one at i. 5 | 6 | For example, if our input was [1, 2, 3, 4, 5], the expected output would be [120, 60, 40, 30, 24]. 7 | 8 | If our input was [3, 2, 1], the expected output would be [2, 3, 6]. 9 | 10 | ## Follow up 11 | Follow-up: what if you can't use division? 12 | 13 | Potential Solution to the follow up: 14 | It seems that we might be able to multiply by 0.1 instead of dividing for integers under 10. 15 | If a value at a particular index is over 10, we chould perhaps define a function that calculates how many decimal places 16 | it has and which "multiplication factor" to multiply by. 17 | 18 | I started writing the solution to this problem and realized that handling float multiplication to a specific precision is not trivial in golang. 19 | So I won't be working out that solution for now. 20 | 21 | 22 | ## Complexity 23 | The time complexity of the following algorithm should be O(2 * length of input array) because we loop through the input twice, once to get the total, and another time to calculate the values at each index. 24 | The space complexity is O(1) because we keep the same input array. 25 | 26 | ## Gotchas 27 | - What happens when we multiply by 0? 28 | 29 | ## Questions to Ask 30 | - How do you want us to handle 0? 31 | - How do you want us to handle negative numbers? 32 | - How do you want us to handle negative decimals or fractions? 33 | 34 | -------------------------------------------------------------------------------- /arrays/separateThreeValues/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func sortTripleArrayFirst(arr []string) []string { 6 | if len(arr) == 0 { 7 | return arr 8 | } 9 | r := 0 10 | b := len(arr) - 1 11 | for i := 0; i < len(arr); i++ { 12 | if arr[i] == "R" { 13 | arr[i], arr[r] = arr[r], arr[i] 14 | r++ 15 | } 16 | 17 | } 18 | 19 | for i := len(arr) - 1; i >= 0; i-- { 20 | fmt.Println(b) 21 | fmt.Println(i) 22 | if arr[i] == "B" { 23 | arr[i], arr[b] = arr[b], arr[i] 24 | b-- 25 | } 26 | } 27 | 28 | return arr 29 | } 30 | 31 | func sortTripleArraySecond(arr []string) []string { 32 | if len(arr) == 0 { 33 | return arr 34 | } 35 | i, j := 0, 0 36 | n := len(arr) - 1 37 | 38 | for j <= n { 39 | if arr[j] == "R" { 40 | arr[i], arr[j] = arr[j], arr[i] 41 | i++ 42 | j-- 43 | } else if arr[j] == "B" { 44 | arr[n], arr[j] = arr[j], arr[n] 45 | n-- 46 | } else { 47 | j++ 48 | } 49 | } 50 | 51 | return arr 52 | } 53 | 54 | func main() { 55 | fmt.Println("vim-go") 56 | fmt.Println(sortTripleArrayFirst([]string{"G", "B", "R", "R", "B", "R", "G"})) 57 | fmt.Println(sortTripleArraySecond([]string{"G", "B", "R", "R", "B", "R", "G"})) 58 | } 59 | -------------------------------------------------------------------------------- /arrays/separateThreeValues/readme.md: -------------------------------------------------------------------------------- 1 | # separate-three-values 2 | 3 | ## Problem Definition 4 | Given an array of strictly the characters 'R', 'G', and 'B', segregate the values of the array so that all the Rs come first, the Gs come second, and the Bs come last. You can only swap elements of the array. 5 | 6 | Do this in linear time and in-place. 7 | 8 | For example, given the array ['G', 'B', 'R', 'R', 'B', 'R', 'G'], it should become ['R', 'R', 'R', 'G', 'G', 'B', 'B']. 9 | 10 | ## Questions to ask 11 | - When you say In-place, do you mean space complexity of O(1)? 12 | - Will there be any variable values? Are we sure they are 'R', 'G', and 'B'? 13 | 14 | ## First Solution 15 | In the first solution, we loop over the array twice, once forwards and once backwards, while swapping either the R or B to keep the G in the middle of the array. 16 | Although this maintains O(N) complexity, it is technically O(2N). So it does reduce down, but we can do better. 17 | 18 | ## Dutch national flag solution 19 | After consulting the oracle, I discovered that Dijkstra had solved this WAAAYYYY before my time in something called the [Dutch national flag problem](https://en.wikipedia.org/wiki/Dutch_national_flag_problem). 20 | Basically, we can swap out the > or < mid conditionals in the pseudocode with B and R respectively. 21 | This solves the problem in O(N) regardless of the input. 22 | -------------------------------------------------------------------------------- /backtracking/generate-all-binary-strings/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func create1dArr(n int) []int { 6 | var arr []int 7 | for i := 0; i < n; i++ { 8 | arr = append(arr, 0) 9 | } 10 | 11 | return arr 12 | } 13 | 14 | func printArray(arr []int, n int) { 15 | for i := 0; i < n; i++ { 16 | fmt.Printf("%d ", arr[i]) 17 | } 18 | fmt.Printf("\n") 19 | } 20 | 21 | func generateAllBinStrings(n int, arr []int, i int) { 22 | if i == n { 23 | printArray(arr, n) 24 | return 25 | } 26 | 27 | arr[i] = 0 28 | generateAllBinStrings(n, arr, i+1) 29 | 30 | arr[i] = 1 31 | generateAllBinStrings(n, arr, i+1) 32 | } 33 | 34 | func returnAllBinStrings(n int) { 35 | arr := create1dArr(n) 36 | generateAllBinStrings(n, arr, 0) 37 | } 38 | 39 | func main() { 40 | returnAllBinStrings(4) 41 | returnAllBinStrings(5) 42 | returnAllBinStrings(6) 43 | } 44 | -------------------------------------------------------------------------------- /backtracking/generate-all-binary-strings/readme.md: -------------------------------------------------------------------------------- 1 | # Generate All Binary Strings 2 | 3 | ## Problem Statement 4 | Given a positive integer number N. The task is to generate all the binary strings of N bits. These binary strings should be in ascending order. 5 | -------------------------------------------------------------------------------- /backtracking/knightsTour/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | var xMoves = []int{2, 1, -1, -2, -2, -1, 1, 2} 9 | var yMoves = []int{1, 2, 2, 1, -1, -2, -2, -1} 10 | 11 | func isSafe(x, y int, sol [][]int) bool { 12 | return x >= 0 && x < len(sol) && y >= 0 && y < len(sol[0]) && sol[x][y] == -1 13 | } 14 | 15 | func recurFunc(x, y, moveCount int, sol [][]int, xMoves, yMoves []int, iter *int) bool { 16 | if moveCount == len(sol)*len(sol[0]) { 17 | return true 18 | } 19 | *iter = *iter + 1 20 | 21 | for k := 0; k < 8; k++ { 22 | nextX := x + xMoves[k] 23 | nextY := y + yMoves[k] 24 | // backtracking occurs here 25 | if isSafe(nextX, nextY, sol) { 26 | sol[nextX][nextY] = moveCount 27 | if recurFunc(nextX, nextY, moveCount+1, sol, xMoves, yMoves, iter) { 28 | return true 29 | } else { 30 | sol[nextX][nextY] = -1 31 | } 32 | } 33 | } 34 | 35 | return false 36 | 37 | } 38 | 39 | func findKnightsTour(boardSize int) ([][]int, error) { 40 | // first we initialize each location on the board to -1 to not visited 41 | chessboard := make([][]int, boardSize) 42 | for x := 0; x < boardSize; x++ { 43 | for y := 0; y < boardSize; y++ { 44 | chessboard[y] = append(chessboard[y], -1) 45 | } 46 | } 47 | 48 | chessboard[0][0] = 0 49 | iter := 0 50 | 51 | if !recurFunc(0, 0, 1, chessboard, xMoves, yMoves, &iter) { 52 | fmt.Printf("ran through %d iterations\n", iter) 53 | return nil, errors.New("The solution doesn't exist. Failure") 54 | } else { 55 | fmt.Printf("ran through %d iterations\n", iter) 56 | return chessboard, nil 57 | } 58 | 59 | fmt.Printf("ran through %d iterations\n", iter) 60 | 61 | return chessboard, nil 62 | } 63 | 64 | func main() { 65 | fmt.Println(findKnightsTour(2)) 66 | fmt.Println(findKnightsTour(3)) 67 | fmt.Println(findKnightsTour(6)) 68 | fmt.Println(findKnightsTour(7)) 69 | } 70 | -------------------------------------------------------------------------------- /backtracking/knightsTour/readme.md: -------------------------------------------------------------------------------- 1 | # Knight's Tour 2 | > Classic backtracking problem. It's even number one on [geeks for geeks](https://www.geeksforgeeks.org/the-knights-tour-problem-backtracking-1/) 3 | 4 | ## Problem Statement 5 | From Wikipedia: 6 | A knight's tour is a sequence of moves of a knight on a chessboard such that the knight visits every square exactly once. If the knight ends on a square that is one knight's move from the beginning square (so that it could tour the board again immediately, following the same path), the tour is closed; otherwise, it is open.[1][2] 7 | 8 | The knight's tour problem is the mathematical problem of finding a knight's tour. Creating a program to find a knight's tour is a common problem given to computer science students.[3] Variations of the knight's tour problem involve chessboards of different sizes than the usual 8 × 8, as well as irregular (non-rectangular) boards. 9 | 10 | ## Implementation 11 | Backtracking algorithms have stumped me since I first encountered them a couple of years ago. Yes, the basic pattern I can understand. 12 | 13 | We can see the basic backtracking part of the function in the recurFunc on line 25 of the main.go file in this folder. 14 | At each step of the algorithm, we calculate the next move the knight could possibly do. 15 | 16 | The recursive function in backtracking algorithms tend to follow this pattern: 17 | 1. if the condition for the solution is found, return true 18 | 2. Check if a potential move is safe 19 | 3. if so, make the move. 20 | 4. call the recursive function with the same move. 21 | 5. if the result of the recursive function is true, return true 22 | 6. if not, reset the index to be unvisited 23 | 7. return false. For the move chosen higher up in the recursive stack, all of its branches in the tree did not result in a solution. So we must rewind the stack and try a different move from a higher level. 24 | 25 | Step 6 is important here. This step allows us to calculate solutions that **fail further down the call stack** . Using this technique, we don't need to throw out the first couple steps of the algorithm that we have proved to be correct. Resetting the index to not used also enables the algorithm to unstick itself at specific locations. 26 | There could be situations where choosing a sequence of moves backs the knight into a corner where it can not make any safe moves to get out. In this situation, the backtracking algorithm will "let the knight leave" by unwinding the stack and marking the index as -1. Ultimately, the index that is unmarked will need to be visited 27 | but it will be in a different sequence. 28 | 29 | ## Gotchas 30 | I feel that the gotchas with backtracking algorithms are problem-specific and algorithm-specific. If you haven't solved a backtracking algorithm before, you will need to have the basic structure memorized to a point where you can reproduce it for this problem. You will also need to piece together what constraints make up the isSafe function. 31 | In this situation it is comprised by the following: 32 | 1. The move is on the board (not less than 0 and not greater than the length of the board) 33 | 2. The space on the board hasn't been filled yet. (== -1) 34 | 35 | ## Time Complexity 36 | I searched around for time complexity of the algorithm for a long time and it should be O(n!). 37 | The reason for this is that for each time we recurse, one of the remaining vertices is selected, so the total number of vertices that could be selected is decreased by one. However, at each recursion of the function, the knight might need to try out ALL of the spaces in function calls further down the recursive stack in order to find the correct next step. He might also get stuck at this step and need to unwind the call stack up again. 38 | This means that at each step N of the algorithm the knight might need to try all of the spaces N. 39 | So Time Complexity of (N) = N * the next recursed function call, which is Time complexity of (N - 1). 40 | So N* * N-1 * N-2... will result in O(N!). 41 | -------------------------------------------------------------------------------- /backtracking/permutations/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func contains(s []int, e int) bool { 6 | for _, a := range s { 7 | if a == e { 8 | return true 9 | } 10 | } 11 | return false 12 | } 13 | 14 | func permute(nums []int) [][]int { 15 | allPermutations := [][]int{} 16 | backtrack(nums, &allPermutations, []int{}) 17 | 18 | return allPermutations 19 | } 20 | 21 | func backtrack(nums []int, permutations *[][]int, subset []int) { 22 | newSubset := make([]int, len(subset)) 23 | copy(newSubset[0:len(subset)], subset) 24 | if len(newSubset) == len(nums) { 25 | *permutations = append(*permutations, newSubset) 26 | } else { 27 | for i := 0; i < len(nums); i++ { 28 | if contains(subset, nums[i]) { 29 | continue 30 | } 31 | subset = append(subset, nums[i]) 32 | backtrack(nums, permutations, subset) 33 | subset = subset[:len(subset)-1] 34 | } 35 | } 36 | } 37 | 38 | func main() { 39 | fmt.Println(findPermutations([]int{1, 2, 3})) 40 | } 41 | -------------------------------------------------------------------------------- /backtracking/permutations/readme.md: -------------------------------------------------------------------------------- 1 | # Permutations 2 | 3 | ## Problem Definition 4 | Given a collection of distinct integers, return all possible permutations. 5 | 6 | Example: 7 | 8 | Input: [1,2,3] 9 | Output: 10 | [ 11 | [1,2,3], 12 | [1,3,2], 13 | [2,1,3], 14 | [2,3,1], 15 | [3,1,2], 16 | [3,2,1] 17 | ] 18 | -------------------------------------------------------------------------------- /backtracking/permutations2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func permuteUnique(nums []int) [][]int { 8 | permutations := [][]int{} 9 | backtrack(nums, &nums, &permutations) 10 | return permutations 11 | } 12 | 13 | func backtrack(nums []int, raw *[]int, permutations *[][]int) { 14 | if len(nums) <= 1 { 15 | fmt.Println("raw: ", *raw) 16 | *permutations = append(*permutations, duplicate(*raw)) 17 | return 18 | } 19 | 20 | m := make(map[int]int) 21 | for i := 0; i < len(nums); i++ { 22 | if _, ok := m[nums[i]]; ok { 23 | continue 24 | } 25 | fmt.Println("nums: ", nums) 26 | fmt.Println("nums[0]: ", nums[0]) 27 | fmt.Println("nums[i]: ", nums[i]) 28 | 29 | fmt.Println("raw before swap: ", raw) 30 | nums[0], nums[i] = nums[i], nums[0] 31 | fmt.Println("raw after swap: ", raw) 32 | backtrack(nums[1:], raw, permutations) 33 | nums[0], nums[i] = nums[i], nums[0] 34 | m[nums[i]] = i 35 | } 36 | } 37 | 38 | func duplicate(a []int) []int { 39 | ret := make([]int, len(a)) 40 | copy(ret, a) 41 | return ret 42 | } 43 | 44 | func main() { 45 | fmt.Println(permuteUnique([]int{1, 2, 2, 1})) 46 | } 47 | -------------------------------------------------------------------------------- /backtracking/permutations2/readme.md: -------------------------------------------------------------------------------- 1 | # permutations2 2 | 3 | ## Problem Definition 4 | Given a collection of numbers that might contain duplicates, return all possible unique permutations. 5 | 6 | Example: 7 | 8 | Input: [1,1,2] 9 | Output: 10 | [ 11 | [1,1,2], 12 | [1,2,1], 13 | [2,1,1] 14 | ] 15 | 16 | ## Potential Questions to ask 17 | - Can the input be an empty array / slice? Should I handle that? 18 | - Can the input be in any order 19 | -------------------------------------------------------------------------------- /backtracking/ratMaze/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func buildMatrix(n int) (maze [][]int) { 6 | for i := 0; i < n; i++ { 7 | maze = append(maze, []int{}) 8 | for j := 0; j < n; j++ { 9 | maze[i] = append(maze[i], 0) 10 | } 11 | } 12 | 13 | return 14 | } 15 | 16 | func isSafe(maze [][]int, x int, y int) bool { 17 | return x >= 0 && y >= 0 && x < len(maze) && y < len(maze) && maze[x][y] != 0 18 | } 19 | 20 | func findPath(maze [][]int, x int, y int, solution *[][]int) bool { 21 | l := len(maze) 22 | 23 | if x == l-1 && y == l-1 { 24 | (*solution)[x][y] = 1 25 | return true 26 | } 27 | 28 | if isSafe(maze, x, y) == true { 29 | (*solution)[x][y] = 1 30 | if findPath(maze, x+1, y, solution) { 31 | return true 32 | } 33 | 34 | if findPath(maze, x, y+1, solution) { 35 | return true 36 | } 37 | 38 | (*solution)[x][y] = 0 39 | return false 40 | } 41 | 42 | return false 43 | } 44 | 45 | func findPathWithBackMotion(maze *[][]int, x int, y int, solution *[][]int) bool { 46 | l := len(*maze) 47 | 48 | if x == l-1 && y == l-1 { 49 | (*solution)[x][y] = 1 50 | return true 51 | } 52 | 53 | (*maze)[x][y] = 0 54 | (*solution)[x][y] = 1 55 | 56 | if isSafe((*maze), x+1, y) && findPathWithBackMotion(maze, x+1, y, solution) { 57 | return true 58 | } 59 | 60 | if isSafe(*maze, x, y+1) && findPathWithBackMotion(maze, x, y+1, solution) { 61 | return true 62 | } 63 | 64 | if isSafe(*maze, x-1, y) && findPathWithBackMotion(maze, x-1, y, solution) { 65 | return true 66 | } 67 | 68 | if isSafe(*maze, x, y-1) && findPathWithBackMotion(maze, x, y-1, solution) { 69 | return true 70 | } 71 | 72 | (*solution)[x][y] = 0 73 | return false 74 | } 75 | 76 | func ratInAMaze(maze [][]int) [][]int { 77 | solution := buildMatrix(len(maze)) 78 | 79 | if findPath(maze, 0, 0, &solution) == true { 80 | return solution 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func ratInAMazeBack(maze [][]int) [][]int { 87 | solution := buildMatrix(len(maze)) 88 | if findPathWithBackMotion(&maze, 0, 0, &solution) == true { 89 | return solution 90 | } 91 | 92 | return nil 93 | } 94 | 95 | func main() { 96 | maze := [][]int{[]int{1, 0, 0, 0}, 97 | []int{1, 1, 1, 1}, 98 | []int{0, 0, 1, 0}, 99 | []int{0, 1, 1, 1}} 100 | fmt.Println(maze) 101 | 102 | fmt.Println(ratInAMaze(maze)) 103 | 104 | maze2 := [][]int{ 105 | []int{1, 1, 0, 0}, 106 | []int{0, 1, 0, 0}, 107 | []int{1, 1, 0, 0}, 108 | []int{1, 1, 1, 1}, 109 | } 110 | 111 | fmt.Println(ratInAMazeBack(maze2)) 112 | } 113 | -------------------------------------------------------------------------------- /backtracking/ratMaze/readme.md: -------------------------------------------------------------------------------- 1 | # rat in a maze 2 | 3 | ## Problem Definition 4 | 5 | A Maze is given as Nx*N binary matrix of blocks where source block is the upper left most block i.e., maze[0][0] and destination block is lower rightmost block i.e., maze[N-1][N-1]. A rat starts from source and has to reach the destination. The rat can move only in two directions: forward and down. 6 | In the maze matrix, 0 means the block is a dead end and 1 means the block can be used in the path from source to destination. 7 | 8 | Start from point [0 0] of the maze and find your way through to [n - 1, n - 1] 9 | 10 | The rat can move in two directions: horizontally and vertically in any position that is not blocked. 11 | 12 | ``` 13 | Input: 14 | [[1 0 0 0], [1 1 1 1], [0 0 1 0], [0 1 1 1]] 15 | 16 | Output: 17 | [[1 0 0 0], [1 1 1 0], [0 0 1 0], [0 0 1 1]] 18 | ``` 19 | -------------------------------------------------------------------------------- /backtracking/subsets/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func subsets(nums []int) [][]int { 6 | powerset := [][]int{} 7 | backtrack(nums, 0, []int{}, &powerset) 8 | return powerset 9 | } 10 | 11 | func backtrack(nums []int, start int, subset []int, powerset *[][]int) { 12 | newSubset := make([]int, len(subset)+1) 13 | copy(newSubset[0:len(subset)], subset) 14 | *powerset = append(*powerset, newSubset[:len(newSubset)-1]) 15 | 16 | for i := start; i < len(nums); i++ { 17 | subset = append(subset, nums[i]) 18 | backtrack(nums, i+1, subset, powerset) 19 | 20 | subset = subset[:len(subset)-1] 21 | } 22 | } 23 | 24 | func main() { 25 | fmt.Println(subsets([]int{1, 2, 3})) 26 | } 27 | -------------------------------------------------------------------------------- /backtracking/subsets/readme.md: -------------------------------------------------------------------------------- 1 | # subsets 2 | 3 | ## Problem Definition 4 | Given a set of distinct integers, nums, return all possible subsets (the power set). 5 | 6 | Note: The solution set must not contain duplicate subsets. 7 | 8 | Example: 9 | 10 | Input: nums = [1,2,3] 11 | Output: 12 | [ 13 | [3], 14 | [1], 15 | [2], 16 | [1,2,3], 17 | [1,3], 18 | [2,3], 19 | [1,2], 20 | [] 21 | ] 22 | 23 | ## Gotchas 24 | - Golang sometimes pollutes slice memory when append a slice to a slice of slices 25 | -- I.E. I found that although my subset slice was appending and removing fine in the backtracking algorithm, without creating a new copy of the slice, 26 | I would overwrite parts of previously written slices in the powerset. 27 | - You MUST pass in a pointer of the variable whose memory will be altered 28 | 29 | ## Questions to Ask 30 | - Do we add the blank "[]" array as a subset 31 | - How do we handle empty nums arrays 32 | 33 | ## Main Idea 34 | In backtracking, you use a depth-first search-like approach to find out all of the different subsets. 35 | As the algorithm is called in a decreasing sized for-loop, elements are added and removed from the subset before and after visit. 36 | So for the first index of the array, 1 in the test, we will first add the empty array as a subset, add the value at the first index to the array and recursively 37 | call the recursive backtrack call which starts the function again. 38 | The next function will add the [1] subset to the results and start the for loop from the second index of the nums array. 39 | This will continue until we add up all the subsets from the first for loop calls to the master array, then the functions on the call stack will begin to return. 40 | After we have added [1, 2, 3], the call stack will return to the previous function, remove the 3 from the array, and return. 41 | This will continue until the call stack returns to the first function call, when the for loop will then start calling functions from the second index of the array, 2, and start the process over again. 42 | -------------------------------------------------------------------------------- /backtracking/subsets2/betterSolution/betterSolution.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func findSubsets(inputArr []int) [][]int { 9 | sort.Ints(inputArr) 10 | subsets := [][]int{} 11 | backtrack(inputArr, &subsets, 0, []int{}) 12 | return subsets 13 | } 14 | 15 | func backtrack(inputArr []int, subsets *[][]int, index int, tempArr []int) { 16 | newTemp := make([]int, len(tempArr)) 17 | copy(newTemp[0:len(tempArr)], tempArr) 18 | *subsets = append(*subsets, newTemp) 19 | 20 | for i := index; i < len(inputArr); i++ { 21 | if i > index && inputArr[i] == inputArr[i-1] { 22 | continue 23 | } 24 | 25 | tempArr = append(tempArr, inputArr[i]) 26 | backtrack(inputArr, subsets, i+1, tempArr) 27 | tempArr = tempArr[:len(tempArr)-1] 28 | } 29 | } 30 | 31 | func main() { 32 | fmt.Println(findSubsets([]int{1, 2, 2})) 33 | fmt.Println(findSubsets([]int{1, 2, 3})) 34 | fmt.Println(findSubsets([]int{4, 4, 4, 1, 4})) 35 | } 36 | -------------------------------------------------------------------------------- /backtracking/subsets2/hacky/hacky.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func findSubsets(inputArr []int) [][]int { 9 | sort.Ints(inputArr) 10 | subsets := [][]int{} 11 | backtrack(inputArr, &subsets, 0, []int{}, make(map[int]bool)) 12 | return subsets 13 | } 14 | 15 | func backtrack(inputArr []int, subsets *[][]int, index int, tempArr []int, containMap map[int]bool) { 16 | newTemp := make([]int, len(tempArr)) 17 | copy(newTemp, tempArr) 18 | var tempSum int 19 | for i := 0; i < len(newTemp); i++ { 20 | tempSum += newTemp[i] 21 | } 22 | 23 | if _, ok := containMap[tempSum]; ok == false { 24 | *subsets = append(*subsets, newTemp) 25 | containMap[tempSum] = true 26 | } 27 | 28 | for i := index; i < len(inputArr); i++ { 29 | tempArr = append(tempArr, inputArr[i]) 30 | backtrack(inputArr, subsets, i+1, tempArr, containMap) 31 | tempArr = tempArr[1:] 32 | } 33 | } 34 | 35 | func main() { 36 | fmt.Println(findSubsets([]int{1, 2, 2})) 37 | } 38 | -------------------------------------------------------------------------------- /backtracking/subsets2/readme.md: -------------------------------------------------------------------------------- 1 | # subsets2 2 | 3 | ## Problem Definition 4 | Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set). 5 | 6 | Note: The solution set must not contain duplicate subsets. 7 | 8 | Example: 9 | 10 | Input: [1,2,2] 11 | Output: 12 | [ 13 | [2], 14 | [1], 15 | [1,2,2], 16 | [2,2], 17 | [1,2], 18 | [] 19 | ] 20 | 21 | ## Gotchas 22 | - How will you store if you have seen a subset before? (potentially different for different languages) 23 | 24 | ## Time Complexity 25 | - In my first hacky complexity, since I need to add all of the numbers in each temp array up, the time complexity should be at least (n^2) power 26 | with a space complexity of O(N - 1) since potentially you could have a duplicate stored for each subset (probably except for the empty array) 27 | -------------------------------------------------------------------------------- /backtracking/wordSearch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var board = [][]byte{ 6 | []byte{"A"[0], "B"[0], "C"[0], "E"[0]}, 7 | []byte{"S"[0], "F"[0], "C"[0], "S"[0]}, 8 | []byte{"A"[0], "D"[0], "E"[0], "E"[0]}, 9 | } 10 | 11 | func inBounds(val int, length int) bool { 12 | return val >= 0 && val < length 13 | } 14 | 15 | func backtrack(board [][]byte, word string, i int, j int, result *bool) { 16 | if len(word) > 1 { 17 | if inBounds(i, len(board)) && inBounds(j-1, len(board[0])) && board[i][j-1] == word[1] { 18 | backtrack(board, word[1:], i, j-1, result) 19 | } 20 | 21 | if inBounds(i, len(board)) && inBounds(j+1, len(board[0])) && board[i][j+1] == word[1] { 22 | backtrack(board, word[1:], i, j+1, result) 23 | } 24 | 25 | if inBounds(i-1, len(board)) && inBounds(j, len(board[0])) && board[i-1][j] == word[1] { 26 | backtrack(board, word[1:], i-1, j, result) 27 | } 28 | 29 | if inBounds(i+1, len(board)) && inBounds(j, len(board[0])) && board[i+1][j] == word[1] { 30 | backtrack(board, word[1:], i+1, j, result) 31 | } 32 | } else { 33 | *result = true 34 | } 35 | } 36 | 37 | func exists(board [][]byte, word string) bool { 38 | result := false 39 | for i := 0; i < len(board); i++ { 40 | for j := 0; j < len(board[0]); j++ { 41 | if board[i][j] == word[0] { 42 | backtrack(board, word, i, j, &result) 43 | } 44 | } 45 | } 46 | 47 | return result 48 | } 49 | 50 | func main() { 51 | fmt.Println(exists(board, "ABCED")) 52 | fmt.Println(exists(board, "ABA")) 53 | fmt.Println(exists(board, "ABCE")) 54 | fmt.Println(exists(board, "ASFC")) 55 | fmt.Println(exists(board, "ASFF")) 56 | } 57 | -------------------------------------------------------------------------------- /backtracking/wordSearch/readme.md: -------------------------------------------------------------------------------- 1 | # wordSearch 2 | 3 | ## Problem Definition 4 | Given a 2D board and a word, find if the word exists in the grid. 5 | 6 | The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. 7 | 8 | Example: 9 | 10 | board = 11 | [ 12 | ['A','B','C','E'], 13 | ['S','F','C','S'], 14 | ['A','D','E','E'] 15 | ] 16 | 17 | Given word = "ABCCED", return true. 18 | Given word = "SEE", return true. 19 | Given word = "ABCB", return false. 20 | 21 | ## Questions to Ask 22 | - I'd like to modify the array we will be using, do you think it's fine if I use a "-" 23 | - Will the inner arrays of the 2d array all be the same length? 24 | - Which direction can you find the string in, any? 25 | -------------------------------------------------------------------------------- /binarySearch/buildArray/readme.md: -------------------------------------------------------------------------------- 1 | # build decreasing array 2 | -------------------------------------------------------------------------------- /binaryTree/2ndHighestNodeBST/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Val int 7 | Left, Right *BNode 8 | } 9 | 10 | func spop(stack *[]BNode) *BNode { 11 | if len(*stack) == 0 { 12 | return nil 13 | } 14 | 15 | slength := len(*stack) - 1 16 | 17 | sVal := *stack 18 | // get value of last index from arr 19 | nodeToReturn := sVal[slength] 20 | 21 | // pop value off of array 22 | sVal = append(sVal[:slength], sVal[slength+1:]...) 23 | *stack = sVal 24 | 25 | return &nodeToReturn 26 | } 27 | 28 | func spush(stack *[]BNode, node BNode) { 29 | if node == (BNode{}) { 30 | return 31 | } 32 | 33 | *stack = append(*stack, node) 34 | } 35 | 36 | func newNode(key int) *BNode { 37 | node := BNode{Val: key} 38 | return &node 39 | } 40 | 41 | func bstInsert(root *BNode, key int) *BNode { 42 | if root == nil { 43 | return newNode(key) 44 | } 45 | 46 | if key < root.Val { 47 | root.Left = bstInsert(root.Left, key) 48 | } else { 49 | root.Right = bstInsert(root.Right, key) 50 | } 51 | 52 | return root 53 | } 54 | 55 | func findKBiggestNode(root *BNode, k int) *BNode { 56 | if root == nil { 57 | return nil 58 | } 59 | 60 | stack := []BNode{*root} 61 | currentNode := root 62 | for currentNode != nil || len(stack) > 0 { 63 | for currentNode.Right != nil { 64 | spush(&stack, *currentNode.Right) 65 | *currentNode = *currentNode.Right 66 | } 67 | 68 | currentNode = spop(&stack) 69 | k-- 70 | if k == 0 { 71 | return currentNode 72 | } else if currentNode.Left != nil { 73 | spush(&stack, *currentNode.Left) 74 | *currentNode = *currentNode.Left 75 | } 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func createBst(arr []int) *BNode { 82 | var root *BNode 83 | for _, k := range arr { 84 | root = bstInsert(root, k) 85 | } 86 | 87 | return root 88 | } 89 | 90 | func main() { 91 | 92 | bst3 := createBst([]int{15, 10, 20, 8, 5, 2}) 93 | 94 | fmt.Println(findKBiggestNode(bst3, 2)) 95 | } 96 | -------------------------------------------------------------------------------- /binaryTree/2ndHighestNodeBST/readme.md: -------------------------------------------------------------------------------- 1 | # secondLargestNode 2 | 3 | ## Problem Definition 4 | Given the root to a binary search tree, find the second largest node in the tree. 5 | -------------------------------------------------------------------------------- /binaryTree/closestLeafToNode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type BNode struct { 4 | Val int 5 | Left, Right *BNode 6 | } 7 | 8 | func newNode(val int) *BNode { 9 | return &BNode{Val: val} 10 | } 11 | 12 | func bstInsert(root *BNode, val int) *BNode { 13 | if root == nil { 14 | return newNode(val) 15 | } 16 | 17 | if val < root.Val { 18 | root.Left = bstInsert(root.Left, val) 19 | } else { 20 | root.Right = bstInsert(root.Right, val) 21 | } 22 | 23 | return root 24 | } 25 | 26 | func createTree(keys []int) *BNode { 27 | var root *BNode 28 | for _, k := range keys { 29 | root = bstInsert(root, k) 30 | } 31 | 32 | return root 33 | } 34 | 35 | func findLeafDown(root *BNode, lev int, minDist *int) { 36 | if root == nil { 37 | return 38 | } 39 | 40 | // If the root wasn't null, and we have a value, set the minDist to be the current level value 41 | if root.Left == nil && root.Right == nil { 42 | if lev < *minDist { 43 | *minDist = lev 44 | return 45 | } 46 | } 47 | 48 | findLeafDown(root.Left, lev+1, minDist) 49 | findLeafDown(root.Right, lev+1, minDist) 50 | } 51 | 52 | func main() { 53 | btree := createTree([]int{6, 1, 3, 5, 20, 7, 9, 13}) 54 | } 55 | -------------------------------------------------------------------------------- /binaryTree/closestLeafToNode/readme.md: -------------------------------------------------------------------------------- 1 | # closestLeafToNode 2 | 3 | ## Problem Definition 4 | 5 | Closest leaf to a given node in Binary Tree 6 | 7 | Given a Binary Tree and a node x in it, find distance of the closest leaf to x in Binary Tree. If given node itself is a leaf, then distance is 0. 8 | 9 | Examples: 10 | 11 | Input: Root of below tree 12 | And x = pointer to node 13 13 | 10 14 | / \ 15 | 12 13 16 | / 17 | 14 18 | Output 1 19 | Distance 1. Closest leaf is 14. 20 | 21 | 22 | Input: Root of below tree 23 | And x = pointer to node 13 24 | 10 25 | / \ 26 | 12 13 27 | / \ 28 | 14 15 29 | / \ / \ 30 | 21 22 23 24 31 | /\ /\ /\ /\ 32 | 1 2 3 4 5 6 7 8 33 | 34 | Output 2 35 | Closest leaf is 12 through 10. 36 | 37 | Questions to ask: 38 | 1. Is the binary tree a bst? Does it have order? 39 | -------------------------------------------------------------------------------- /binaryTree/countNodesRecursive/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Val int 7 | Left, Right *BNode 8 | } 9 | 10 | func newNode(key int) *BNode { 11 | node := BNode{Val: key} 12 | return &node 13 | } 14 | 15 | func bstInsert(root *BNode, key int) *BNode { 16 | if root == nil { 17 | return newNode(key) 18 | } 19 | 20 | if key < root.Val { 21 | root.Left = bstInsert(root.Left, key) 22 | } else if key > root.Val { 23 | root.Right = bstInsert(root.Right, key) 24 | } 25 | 26 | return root 27 | } 28 | 29 | func createBstFromArr(arr []int) *BNode { 30 | var root *BNode 31 | for _, k := range arr { 32 | root = bstInsert(root, k) 33 | } 34 | 35 | return root 36 | } 37 | 38 | func CountNodesRecursive(count int, root *BNode) int { 39 | count = 1 40 | if root.Left != nil { 41 | count += CountNodesRecursive(count, root.Left) 42 | } 43 | 44 | if root.Right != nil { 45 | count += CountNodesRecursive(count, root.Right) 46 | } 47 | 48 | return count 49 | } 50 | 51 | func main() { 52 | bst := createBstFromArr([]int{1, 2, 3, 4, 10, 13, 19, 15}) 53 | count := CountNodesRecursive(0, bst) 54 | 55 | fmt.Println(count) 56 | } 57 | -------------------------------------------------------------------------------- /binaryTree/countNodesRecursive/readme.md: -------------------------------------------------------------------------------- 1 | # Count Nodes in a binary tree recursively 2 | 3 | ## Problem Statement 4 | The recursive structure of a binary tree makes it easy to count nodes recursively. 5 | 6 | There are 3 things we can count: 7 | 8 | 1. The total number of nodes 9 | 2. The number of leaf nodes 10 | 3. The number of internal nodes 11 | 12 | ### Counting all nodes 13 | The number of nodes in a binary tree is the number of nodes in the root’s left subtree, plus the number of nodes in its right subtree, plus one (for the root itself). 14 | 15 | ## Solution 16 | The solution for counting the nodes in the bst revolves around just walking the tree. Using the recursive algorithm uses the call stack behind the scenes. 17 | We first set the count to equal one for each call on the stack, then we add this number to all of the calls that will be added to the stack with each recursive call. 18 | The result is the total number of nodes that are contained within the tree. 19 | 20 | ## Time Complexity 21 | The time complexity of the algorithm is O(n) where n is the number of each node contained within the binary search tree. 22 | 23 | -------------------------------------------------------------------------------- /binaryTree/courseListingsOrder/readme.md: -------------------------------------------------------------------------------- 1 | # Return sorted ordering of courses in hashmap 2 | 3 | ## Problem Statement 4 | We're given a hashmap associating each courseId key with a list of courseIds values, which represents that the prerequisites of courseId are courseIds. Return a sorted ordering of courses such that we can finish all courses. 5 | Return null if there is no such ordering. 6 | 7 | For example, given {'CSC300': ['CSC100', 'CSC200'], 'CSC200': ['CSC100'], 'CSC100': []}, should return ['CSC100', 'CSC200', 'CSCS300'] 8 | -------------------------------------------------------------------------------- /binaryTree/findDeepestNode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Val int 7 | Left, Right *BNode 8 | } 9 | 10 | func newNode(val int) *BNode { 11 | return &BNode{Val: val} 12 | } 13 | 14 | func bstInsert(val int, root *BNode) *BNode { 15 | if root == nil { 16 | return newNode(val) 17 | } 18 | 19 | if val < root.Val { 20 | root.Left = bstInsert(val, root.Left) 21 | } else { 22 | root.Right = bstInsert(val, root.Right) 23 | } 24 | 25 | return root 26 | } 27 | 28 | func createTree(keys []int) *BNode { 29 | var root *BNode 30 | for _, k := range keys { 31 | root = bstInsert(k, root) 32 | } 33 | 34 | return root 35 | } 36 | 37 | func main() { 38 | bst1 := createTree([]int{15, 20, 20, 8, 5, 2}) 39 | fmt.Println(bst1) 40 | } 41 | -------------------------------------------------------------------------------- /binaryTree/findDeepestNode/readme.md: -------------------------------------------------------------------------------- 1 | # findDeepestNode 2 | -------------------------------------------------------------------------------- /binaryTree/isBinaryTreeSymmetric/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Val int 7 | Left, Right *BNode 8 | } 9 | 10 | func newNode(val int) *BNode { 11 | return &BNode{Val: val} 12 | } 13 | 14 | func isMirror(n1 *BNode, n2 *BNode) bool { 15 | if n1 == nil && n2 == nil { 16 | return true 17 | } 18 | 19 | if n1 != nil && n2 != nil && n1.Val == n2.Val { 20 | return isMirror(n1.Left, n2.Right) && isMirror(n2.Left, n1.Right) 21 | } 22 | 23 | return false 24 | } 25 | 26 | func isSymmetric(r *BNode) bool { 27 | return isMirror(r, r) 28 | } 29 | 30 | func main() { 31 | bst := newNode(1) 32 | bst.Left = newNode(2) 33 | bst.Right = newNode(2) 34 | bst.Left.Left = newNode(3) 35 | bst.Left.Right = newNode(4) 36 | bst.Right.Left = newNode(4) 37 | bst.Right.Right = newNode(3) 38 | 39 | bst2 := newNode(1) 40 | bst2.Left = newNode(2) 41 | bst2.Right = newNode(2) 42 | bst2.Left.Right = newNode(3) 43 | bst2.Right.Right = newNode(3) 44 | 45 | fmt.Println(isSymmetric(bst)) 46 | fmt.Println(isSymmetric(bst2)) 47 | } 48 | -------------------------------------------------------------------------------- /binaryTree/isBinaryTreeSymmetric/readme.md: -------------------------------------------------------------------------------- 1 | # Is a Binary Tree symmetric? 2 | 3 | ## Problem Definition 4 | 5 | Symmetric Tree (Mirror Image of itself) 6 | 7 | Given a binary tree, check whether it is a mirror of itself. 8 | 9 | For example, this binary tree is symmetric: 10 | 11 | 1 12 | / \ 13 | 2 2 14 | / \ / \ 15 | 3 4 4 3 16 | 17 | But the following is not: 18 | 19 | 1 20 | / \ 21 | 2 2 22 | \ \ 23 | 3 3 24 | -------------------------------------------------------------------------------- /binaryTree/lowestCommonAncestor/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Left, Right *BNode 7 | Val int 8 | } 9 | 10 | func newNode(key int) *BNode { 11 | return &BNode{Val: key} 12 | } 13 | 14 | func bstInsert(root *BNode, val int) *BNode { 15 | if root == nil { 16 | return newNode(val) 17 | } 18 | 19 | if val < root.Val { 20 | root.Left = bstInsert(root.Left, val) 21 | } else { 22 | root.Right = bstInsert(root.Right, val) 23 | } 24 | 25 | return root 26 | } 27 | 28 | func createTree(keys []int) *BNode { 29 | var root *BNode 30 | for _, k := range keys { 31 | root = bstInsert(root, k) 32 | } 33 | 34 | return root 35 | } 36 | 37 | func lowestCommonAncestor(root *BNode, q int, p int) *BNode { 38 | if root == nil || root.Val == q || root.Val == p { 39 | return root 40 | } 41 | 42 | left := lowestCommonAncestor(root.Left, q, p) 43 | right := lowestCommonAncestor(root.Right, q, p) 44 | 45 | // either return root or return left / right 46 | if left != nil && right != nil { 47 | return root 48 | } else { 49 | if left != nil { 50 | return left 51 | } 52 | 53 | return right 54 | } 55 | } 56 | 57 | func main() { 58 | mid := newNode(5) 59 | mid.Right = newNode(8) 60 | mid.Left = newNode(3) 61 | mid.Right.Left = newNode(7) 62 | mid.Right.Right = newNode(9) 63 | mid.Left.Left = newNode(2) 64 | mid.Left.Right = newNode(4) 65 | fmt.Println(lowestCommonAncestor(mid, 7, 9)) 66 | fmt.Println(lowestCommonAncestor(mid, 2, 4)) 67 | } 68 | -------------------------------------------------------------------------------- /binaryTree/printBinaryTree/readme.md: -------------------------------------------------------------------------------- 1 | # printBinaryTree 2 | 3 | ## Problem Definition 4 | ``` 5 | Print a binary tree in an m*n 2D string array following these rules: 6 | 7 | The row number m should be equal to the height of the given binary tree. 8 | The column number n should always be an odd number. 9 | The root node's value (in string format) should be put in the exactly middle of the first row it can be put. The column and the row where the root node belongs will separate the rest space into two parts (left-bottom part and right-bottom part). You should print the left subtree in the left-bottom part and print the right subtree in the right-bottom part. The left-bottom part and the right-bottom part should have the same size. Even if one subtree is none while the other is not, you don't need to print anything for the none subtree but still need to leave the space as large as that for the other subtree. However, if two subtrees are none, then you don't need to leave space for both of them. 10 | Each unused space should contain an empty string "". 11 | Print the subtrees following the same rules. 12 | 13 | Example 1: 14 | 15 | Input: 16 | 1 17 | / 18 | 2 19 | Output: 20 | [["", "1", ""], 21 | ["2", "", ""]] 22 | 23 | Example 2: 24 | 25 | Input: 26 | 1 27 | / \ 28 | 2 3 29 | \ 30 | 4 31 | Output: 32 | [["", "", "", "1", "", "", ""], 33 | ["", "2", "", "", "", "3", ""], 34 | ["", "", "4", "", "", "", ""]] 35 | 36 | Example 3: 37 | 38 | Input: 39 | 1 40 | / \ 41 | 2 5 42 | / 43 | 3 44 | / 45 | 4 46 | Output: 47 | 48 | [["", "", "", "", "", "", "", "1", "", "", "", "", "", "", ""] 49 | ["", "", "", "2", "", "", "", "", "", "", "", "5", "", "", ""] 50 | ["", "3", "", "", "", "", "", "", "", "", "", "", "", "", ""] 51 | ["4", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]] 52 | ``` 53 | 54 | 55 | -------------------------------------------------------------------------------- /binaryTree/returnRootToLeaves/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Val int 7 | Left, Right *BNode 8 | } 9 | 10 | func isLeafNode(node BNode) bool { 11 | return node.Right == nil && node.Left == nil 12 | } 13 | 14 | func preOrderTraverse(node *BNode, currPath []int, allPaths *[][]int) { 15 | if node == nil { 16 | return 17 | } 18 | currPath = append(currPath, node.Val) 19 | if isLeafNode(*node) == true { 20 | *allPaths = append(*allPaths, currPath) 21 | } 22 | if node.Left != nil { 23 | preOrderTraverse(node.Left, currPath, allPaths) 24 | } 25 | 26 | if node.Right != nil { 27 | preOrderTraverse(node.Right, currPath, allPaths) 28 | } 29 | } 30 | 31 | func newNode(val int) *BNode { 32 | return &BNode{Val: val} 33 | } 34 | 35 | func bstInsert(root *BNode, val int) *BNode { 36 | if root == nil { 37 | return newNode(val) 38 | } 39 | 40 | if val < root.Val { 41 | root.Left = bstInsert(root.Left, val) 42 | } else { 43 | root.Right = bstInsert(root.Right, val) 44 | } 45 | 46 | return root 47 | } 48 | 49 | func createTree(keys []int) *BNode { 50 | var root *BNode 51 | for _, k := range keys { 52 | root = bstInsert(root, k) 53 | } 54 | 55 | return root 56 | } 57 | 58 | func main() { 59 | fmt.Println("vim-go") 60 | // bst := createTree([]int{1, 2, 3, 4, 5}) 61 | bst := newNode(1) 62 | bst.Left = newNode(2) 63 | bst.Right = newNode(3) 64 | bst.Right.Left = newNode(4) 65 | bst.Right.Right = newNode(5) 66 | fmt.Println(*bst) 67 | finalArr := [][]int{} 68 | preOrderTraverse(bst, []int{}, &finalArr) 69 | fmt.Println(finalArr) 70 | } 71 | -------------------------------------------------------------------------------- /binaryTree/returnRootToLeaves/readme.md: -------------------------------------------------------------------------------- 1 | # return root to leaves 2 | 3 | ## Problem Definition 4 | Given a binary tree, return all paths from the root to leaves. 5 | 6 | For example, given the tree 7 | 8 | 1 9 | / \ 10 | 2 3 11 | / \ 12 | 4 5 13 | 14 | it should return [[1, 2], [1, 3, 4], [1, 3, 5]]. 15 | -------------------------------------------------------------------------------- /binaryTree/reverseBinaryTree/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type BNode struct { 6 | Val int 7 | Left, Right *BNode 8 | } 9 | 10 | func newNode(val int) *BNode { 11 | return &BNode{Val: val} 12 | } 13 | 14 | func bstInsert(root *BNode, val int) *BNode { 15 | if root == nil { 16 | return newNode(val) 17 | } 18 | 19 | if val < root.Val { 20 | root.Left = bstInsert(root.Left, val) 21 | } else { 22 | root.Right = bstInsert(root.Right, val) 23 | } 24 | 25 | return root 26 | } 27 | 28 | func createTree(keys []int) *BNode { 29 | var root *BNode 30 | for _, k := range keys { 31 | root = bstInsert(root, k) 32 | } 33 | 34 | return root 35 | } 36 | 37 | func reverseBinaryTree(root *BNode) *BNode { 38 | if root == nil { 39 | return root 40 | } 41 | 42 | tmp := root.Right 43 | root.Right = root.Left 44 | root.Left = tmp 45 | 46 | if root.Left != nil { 47 | reverseBinaryTree(root.Left) 48 | } 49 | 50 | if root.Right != nil { 51 | reverseBinaryTree(root.Right) 52 | } 53 | 54 | return root 55 | } 56 | 57 | func inOrder(root *BNode, sl *[]int) []int { 58 | if root == nil { 59 | return *sl 60 | } 61 | 62 | if root.Left != nil { 63 | inOrder(root.Left, sl) 64 | } 65 | 66 | *sl = append(*sl, root.Val) 67 | 68 | if root.Right != nil { 69 | inOrder(root.Right, sl) 70 | } 71 | 72 | return *sl 73 | } 74 | 75 | func main() { 76 | bst1 := createTree([]int{15, 10, 20, 8, 5, 2}) 77 | fmt.Println(inOrder(bst1, &[]int{})) 78 | rbst := reverseBinaryTree(bst1) 79 | 80 | fmt.Println("reverse starts here") 81 | fmt.Println(inOrder(rbst, &[]int{})) 82 | } 83 | -------------------------------------------------------------------------------- /binaryTree/reverseBinaryTree/readme.md: -------------------------------------------------------------------------------- 1 | # reverseBinaryTree 2 | 3 | ## Problem Definition 4 | Given a Binary Tree, reverse the binary tree 5 | -------------------------------------------------------------------------------- /dataStructures/lfuCache/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type LfuCache struct { 6 | ByKey map[string]*FrequencyNode 7 | FreqHead *FrequencyNode 8 | } 9 | 10 | type FrequencyNode struct { 11 | Value int 12 | Items map[string]string 13 | Prev, Next *FrequencyNode 14 | } 15 | 16 | type LfuItem struct { 17 | Data string 18 | Parent *FrequencyNode 19 | } 20 | 21 | func newLfuCache() LfuCache { 22 | var lc LfuCache 23 | lc.ByKey = make(map[string]*FrequencyNode) 24 | lc.FreqHead = &FrequencyNode{} 25 | 26 | return lc 27 | } 28 | 29 | func newFrequencyNode() FrequencyNode { 30 | var n FrequencyNode 31 | n.Value = 0 32 | n.Prev = &n 33 | n.Next = &n 34 | 35 | return n 36 | } 37 | 38 | func getNewFreqNode(value int, prev, next *FrequencyNode) FrequencyNode { 39 | nn := newFrequencyNode() 40 | nn.Value = value 41 | nn.Prev = prev 42 | nn.Next = next 43 | prev.Next = nn 44 | next.Prev = nn 45 | 46 | return nn 47 | } 48 | 49 | func newLfuItem(data string, parent *FrequencyNode) LfuItem { 50 | return LfuItem{data, parent} 51 | } 52 | 53 | func (c *LfuCache) Insert(key, val string) { 54 | if c.ByKey[key] { 55 | return nil 56 | } 57 | 58 | freq := c.FreqHead.Next 59 | if freq.Value != 1 { 60 | freq = getNewFreqNode(1, c.FreqHead, freq) 61 | } 62 | 63 | freq.Items.add(key, val) 64 | c.ByKey[key] = newLfuItem(val, freq) 65 | } 66 | 67 | func main() { 68 | 69 | lfuCache := newLfuCache() 70 | fmt.Println("%v", lfuCache.FreqHead) 71 | } 72 | -------------------------------------------------------------------------------- /dataStructures/lfuCache/readme.md: -------------------------------------------------------------------------------- 1 | # LfuCache 2 | -------------------------------------------------------------------------------- /dataStructures/lruCache/cache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type CacheNode struct { 6 | Val, Loc int 7 | Prev, Next *CacheNode 8 | } 9 | 10 | type LRUCache struct { 11 | Head, Tail *CacheNode 12 | NodeMap map[int]*CacheNode 13 | Length int 14 | Size int 15 | } 16 | 17 | func newCache(size int) LRUCache { 18 | return LRUCache{Size: size, NodeMap: make(map[int]*CacheNode)} 19 | } 20 | 21 | func (cache *LRUCache) Put(loc int, val int) { 22 | node := &CacheNode{Val: val, Loc: loc} 23 | if cache.Head == nil { 24 | cache.Head = node 25 | cache.Tail = node 26 | cache.NodeMap[loc] = node 27 | cache.Length++ 28 | return 29 | } 30 | 31 | // remove oldest element out of the cache 32 | if cache.Length == cache.Size { 33 | next := cache.Head.Next 34 | delete(cache.NodeMap, cache.Head.Loc) 35 | cache.Head = next 36 | cache.Length-- 37 | } 38 | 39 | cache.NodeMap[loc] = node 40 | cache.Tail.Next = node 41 | cache.Tail = node 42 | cache.Length++ 43 | } 44 | 45 | func (cache *LRUCache) Get(loc int) int { 46 | if _, ok := cache.NodeMap[loc]; ok == true { 47 | return cache.NodeMap[loc].Val 48 | } 49 | return -1 50 | } 51 | 52 | func (cache *LRUCache) Remove(loc int) { 53 | if _, ok := cache.NodeMap[loc]; ok == true { 54 | delete(cacheNodeMap, loc) 55 | cache.Length-- 56 | } 57 | } 58 | 59 | func main() { 60 | fmt.Println("vim-go") 61 | c := newCache(2) 62 | c.Put(1, 1) 63 | c.Put(2, 2) 64 | c.Put(3, 3) 65 | fmt.Println(c.Get(1)) 66 | c.Put(4, 4) 67 | fmt.Println(c) 68 | fmt.Println(c.Get(2)) 69 | c.Put(1, 1) 70 | } 71 | -------------------------------------------------------------------------------- /dataStructures/lruCache/readme.md: -------------------------------------------------------------------------------- 1 | # LruCache 2 | 3 | ## Problem Definition 4 | Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put. 5 | 6 | get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. 7 | put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. 8 | 9 | Follow up: 10 | Could you do both operations in O(1) time complexity? 11 | 12 | Example: 13 | 14 | ``` 15 | LRUCache cache = new LRUCache( 2 /* capacity */ ); 16 | 17 | cache.put(1, 1); 18 | cache.put(2, 2); 19 | cache.get(1); // returns 1 20 | cache.put(3, 3); // evicts key 2 21 | cache.get(2); // returns -1 (not found) 22 | cache.put(4, 4); // evicts key 1 23 | cache.get(1); // returns -1 (not found) 24 | cache.get(3); // returns 3 25 | cache.get(4); // returns 4 26 | ``` 27 | 28 | ## Questions to Ask 29 | 1. Do we just put ints in the cache? Should it handle other values? 30 | 2. Do we need a specific space complexity? 31 | 32 | ## Complexity 33 | - The Time complexity of all the operations is O(1). We achieve this by using a Head pointer, tail pointer, a list for the actual cache, and a hashmap of the individual nodes in the cache. 34 | - Since the Nodes are all linked together, we can remove the head node and move the pointer to its next in order to effectively delete the node (while removing it from the hashmap to all the nodes). 35 | - The space complexity is O(2N), where N is the length of the nodes in the cache. This is because we maintain the map of Nodes and the actual doubly linked list linking all the nodes together. 36 | -------------------------------------------------------------------------------- /dataStructures/minStack/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type MinNode struct { 6 | Val int 7 | Min int 8 | Prev *MinNode 9 | } 10 | 11 | type MinStack struct { 12 | Head *MinNode 13 | Min int 14 | } 15 | 16 | func NewMinStack() *MinStack { 17 | ms := MinStack{} 18 | return &ms 19 | } 20 | 21 | func Min(a, b int) int { 22 | if a < b { 23 | return a 24 | } 25 | 26 | return b 27 | } 28 | 29 | func (ms *MinStack) GetMin() int { 30 | return ms.Min 31 | } 32 | 33 | func (ms *MinStack) Pop() MinNode { 34 | var pn MinNode 35 | pn = *ms.Head 36 | 37 | ms.Head = ms.Head.Prev 38 | ms.Min = ms.Head.Min 39 | 40 | return pn 41 | } 42 | 43 | func (ms *MinStack) Push(val int) { 44 | if ms.Head == nil { 45 | ms.Head = &MinNode{Val: val, Min: val} 46 | ms.Min = val 47 | return 48 | } 49 | 50 | mn := MinNode{Val: val} 51 | mn.Min = Min(val, ms.Min) 52 | ms.Min = Min(val, ms.Min) 53 | 54 | mn.Prev = ms.Head 55 | ms.Head = &mn 56 | 57 | return 58 | } 59 | 60 | func (ms *MinStack) Top() MinNode { 61 | return *ms.Head 62 | } 63 | 64 | func main() { 65 | fmt.Println("vim-go") 66 | minStack := NewMinStack() 67 | minStack.Push(-2) 68 | minStack.Push(0) 69 | minStack.Push(-3) 70 | fmt.Println(minStack) 71 | fmt.Println(*minStack.Head) 72 | fmt.Printf("Get Min %d\n", minStack.GetMin()) 73 | fmt.Println(minStack.Pop()) 74 | fmt.Printf("Get Top %v\n", minStack.Top()) 75 | fmt.Printf("Get Min %d\n", minStack.GetMin()) 76 | } 77 | -------------------------------------------------------------------------------- /dataStructures/minStack/readme.md: -------------------------------------------------------------------------------- 1 | # Min Stack 2 | 3 | ## Problem Definition 4 | Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. 5 | 6 | push(x) -- Push element x onto stack. 7 | pop() -- Removes the element on top of the stack. 8 | top() -- Get the top element. 9 | getMin() -- Retrieve the minimum element in the stack. Example: 10 | 11 | MinStack minStack = new MinStack(); 12 | minStack.push(-2); 13 | minStack.push(0); 14 | minStack.push(-3); 15 | minStack.getMin(); --> Returns -3. 16 | minStack.pop(); 17 | minStack.top(); --> Returns 0. 18 | minStack.getMin(); --> Returns -2. 19 | 20 | ## Explanation 21 | The actual stack we implement here is a housing struct of a linked list with nodes linked to each other through Prev field in the node. We store a global min for readability sake, but we could just store the min 22 | inside of the min at any particular time. We can retrieve the current min either through querying the min at the current head (MinStack.Head.Min) or in our case from the global min (MinStack.Min). 23 | Each time we push, we set the min in the node AND the global stack as the minimum of the val and the current min. 24 | Each time we pop, we set the global min to what the node min is. 25 | 26 | ## Time Complexity 27 | Since we are manipulating pointers, all operations happen in O(1) time. 28 | -------------------------------------------------------------------------------- /dynamicProgramming/bestTimeBuySellStock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func max(a, b int) int { 6 | if a > b { 7 | return a 8 | } 9 | 10 | return b 11 | } 12 | 13 | func min(a, b int) int { 14 | if a < b { 15 | return a 16 | } 17 | 18 | return b 19 | } 20 | 21 | func maxProfit(prices []int) int { 22 | if len(prices) == 0 { 23 | return 0 24 | } 25 | 26 | minNum := prices[0] 27 | maxDiff := 0 28 | for i := 1; i < len(prices); i++ { 29 | potentialDiff := prices[i] - minNum 30 | maxDiff = max(potentialDiff, maxDiff) 31 | 32 | minNum = min(minNum, prices[i]) 33 | } 34 | 35 | return maxDiff 36 | 37 | } 38 | 39 | func main() { 40 | fmt.Println(maxProfit([]int{7, 6, 4, 3, 1})) 41 | fmt.Println(maxProfit([]int{7, 1, 5, 3, 6, 4})) 42 | } 43 | -------------------------------------------------------------------------------- /dynamicProgramming/bestTimeBuySellStock/readme.md: -------------------------------------------------------------------------------- 1 | # Best time to buy and sell stock 2 | 3 | ## Problem Definition 4 | Say you have an array for which the ith element is the price of a given stock on day i. 5 | 6 | If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit. 7 | 8 | Note that you cannot sell a stock before you buy one. 9 | 10 | Example 1: 11 | 12 | Input: [7,1,5,3,6,4] 13 | Output: 5 14 | Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5. 15 | Not 7-1 = 6, as selling price needs to be larger than buying price. 16 | 17 | Example 2: 18 | 19 | Input: [7,6,4,3,1] 20 | Output: 0 21 | Explanation: In this case, no transaction is done, i.e. max profit = 0. 22 | 23 | ## Questions to ask 24 | - Is this just tracking one stock over time? 25 | - What should we do if the array is empty? 26 | 27 | ## Complexity Analysis 28 | The implementation runs in linear time O(n) and constant space O(1). 29 | Basically we start out with a specific min and potential diff (the first array index and the second index minus the first index respectively). 30 | At each point in the for loop, we add the max of each potential diff vs the first diff (maxDiff) and find the minimum between the minimum index and the next index in the array. 31 | We use the minimum because we want to buy at the lowest cost we can. 32 | -------------------------------------------------------------------------------- /dynamicProgramming/coinChange/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func makeChange(coins []int, lenCoins int, n int) int { 6 | dp := []int{} 7 | for i := 0; i <= n; i++ { 8 | dp = append(dp, 0) 9 | } 10 | 11 | dp[0] = 1 12 | 13 | for i := 0; i < lenCoins; i++ { 14 | for j := coins[i]; j <= n; j++ { 15 | dp[j] += dp[j-coins[i]] 16 | } 17 | } 18 | fmt.Println(dp) 19 | 20 | return dp[n] 21 | } 22 | 23 | func main() { 24 | fmt.Println(makeChange([]int{1, 2, 3}, 3, 4)) 25 | fmt.Println(makeChange([]int{2, 5, 3, 6}, 4, 10)) 26 | } 27 | -------------------------------------------------------------------------------- /dynamicProgramming/coinChange/readme.md: -------------------------------------------------------------------------------- 1 | # Coin Change 2 | -------------------------------------------------------------------------------- /dynamicProgramming/dungeonGame/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | const MaxUint = ^uint(0) 6 | const MaxInt = int(MaxUint >> 1) 7 | 8 | func max(a, b int) int { 9 | if a > b { 10 | return a 11 | } 12 | 13 | return b 14 | } 15 | 16 | func findPath(grid [][]int) int { 17 | M := len(grid) 18 | N := len(grid[0]) 19 | // initialize everything with max int 20 | hp := [][]int{} 21 | for i := 0; i < M+1; i++ { 22 | hp = append(hp, []int{}) 23 | for j := 0; j < N+1; j++ { 24 | hp[i] = append(hp[i], MaxInt) 25 | } 26 | } 27 | 28 | hp[M][N-1] = 1 29 | hp[M-1][N] = 1 30 | 31 | for i := M - 1; i >= 0; i-- { 32 | for j := N - 1; j >= 0; j-- { 33 | need := min(hp[i+1][j], hp[i][j+1]) - grid[i][j] 34 | hp[i][j] = need 35 | if need <= 0 { 36 | hp[i][j] = 1 37 | } 38 | } 39 | } 40 | return hp[0][0] 41 | } 42 | 43 | func min(a, b int) int { 44 | if a < b { 45 | return a 46 | } 47 | 48 | return b 49 | } 50 | 51 | func main() { 52 | testCase := [][]int{[]int{-2, -3, 3}, []int{-5, -10, 1}, []int{10, 30, -5}} 53 | fmt.Println(findPath(testCase)) 54 | } 55 | -------------------------------------------------------------------------------- /dynamicProgramming/dungeonGame/readme.md: -------------------------------------------------------------------------------- 1 | # dungeon-game 2 | 3 | ## Problem Definition 4 | The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. The dungeon consists of M x N rooms laid out in a 2D grid. Our valiant knight (K) was initially positioned in the top-left room and must fight his way through the dungeon to rescue the princess. 5 | 6 | The knight has an initial health point represented by a positive integer. If at any point his health point drops to 0 or below, he dies immediately. 7 | 8 | Some of the rooms are guarded by demons, so the knight loses health (negative integers) upon entering these rooms; other rooms are either empty (0's) or contain magic orbs that increase the knight's health (positive integers). 9 | 10 | In order to reach the princess as quickly as possible, the knight decides to move only rightward or downward in each step. 11 | 12 | 13 | 14 | Write a function to determine the knight's minimum initial health so that he is able to rescue the princess. 15 | 16 | For example, given the dungeon below, the initial health of the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN. 17 | ``` 18 | -2 (K) -3 3 19 | -5 -10 1 20 | 10 30 -5 (P) 21 | ``` 22 | 23 | 24 | 25 | Note: 26 | 27 | The knight's health has no upper bound. 28 | Any room can contain threats or power-ups, even the first room the knight enters and the bottom-right room where the princess is imprisoned. 29 | 30 | ## Questions to ask 31 | - Are we certain the map is a square? (equal lengthwise and widthwise) 32 | - 33 | -------------------------------------------------------------------------------- /dynamicProgramming/goldMineProblem/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func build2dSquare(length int) [][]int { 6 | var square [][]int 7 | for i := 0; i <= length; i++ { 8 | square = append(square, []int{}) 9 | for j := 0; j <= length; j++ { 10 | square[i] = append(square[i], 0) 11 | } 12 | } 13 | 14 | return square 15 | } 16 | 17 | func max(a, b int) int { 18 | if a > b { 19 | return a 20 | } 21 | 22 | return b 23 | } 24 | 25 | // right = [row][col + 1] 26 | // rightTop = [row - 1][col + 1] 27 | // rightBottom = [row + 1][col + 1] 28 | 29 | func getHighestGold(mine [][]int) [][]int { 30 | dp := build2dSquare(len(mine) - 1) 31 | for col := len(mine[0]) - 1; col >= 0; col-- { 32 | for row := 0; row < len(mine); row++ { 33 | right := 0 34 | if col < len(mine[0])-1 { 35 | right = dp[row][col+1] 36 | } 37 | 38 | rightTop := 0 39 | if row > 0 && col < len(mine[0])-1 { 40 | rightTop = dp[row-1][col+1] 41 | } 42 | 43 | rightBottom := 0 44 | 45 | if row < len(mine)-1 && col < len(mine[0])-1 { 46 | rightBottom = dp[row+1][col+1] 47 | } 48 | 49 | dp[row][col] = mine[row][col] + max(right, max(rightTop, rightBottom)) 50 | } 51 | } 52 | return dp 53 | } 54 | 55 | func main() { 56 | mine := [][]int{[]int{1, 3, 3}, []int{2, 1, 4}, []int{0, 6, 4}} 57 | table := getHighestGold(mine) 58 | fmt.Println(table) 59 | fmt.Println(mine) 60 | res := table[0][0] 61 | for i := 1; i < len(table); i++ { 62 | res = max(res, table[i][0]) 63 | } 64 | 65 | fmt.Println(res) 66 | } 67 | -------------------------------------------------------------------------------- /dynamicProgramming/goldMineProblem/readme.md: -------------------------------------------------------------------------------- 1 | # gold-mine-problem 2 | 3 | ## Problem Statement 4 | 5 | Gold Mine Problem 6 | 7 | Given a gold mine of n*m dimensions. Each field in this mine contains a positive integer which is the amount of gold in tons. Initially the miner is at first column but can be at any row. 8 | He can move only (right->,right up /,right down\) that is from a given cell, the miner can move to the cell diagonally up towards the right or right or diagonally down towards the right. Find out maximum amount of gold he can collect. 9 | 10 | Examples: 11 | 12 | Input : mat[][] = {{1, 3, 3}, 13 | {2, 1, 4}, 14 | {0, 6, 4}}; 15 | Output : 12 16 | {(1,0)->(2,1)->(2,2)} 17 | 18 | Input: mat[][] = { {1, 3, 1, 5}, 19 | {2, 2, 4, 1}, 20 | {5, 0, 2, 3}, 21 | {0, 6, 1, 2}}; 22 | Output : 16 23 | (2,0) -> (1,1) -> (1,2) -> (0,3) OR 24 | (2,0) -> (3,1) -> (2,2) -> (2,3) 25 | 26 | Input : mat[][] = {{10, 33, 13, 15}, 27 | {22, 21, 04, 1}, 28 | {5, 0, 2, 3}, 29 | {0, 6, 14, 2}}; 30 | Output : 83 31 | 32 | 33 | ## Questions to Ask 34 | - Verify there can not be negative numbers in the mine 35 | - Verify the miner will start at the first column, but potentially be in any row. 36 | 37 | ## Things I struggled with / gotchas 38 | - When you are modifying the table at an index, you must do the index of the MINE + the right, top right, and bottom right of the DP TABLE. 39 | -- Although I knew this in other problems, I forgot it here. So during my implementation, I originally had this bug and realized it was because I was effectively picking up gold and dropping it. 40 | - You really have to understand that the way tabulation (bottom-up) works is by calculating the subproblem over and over again while updating the dp table. 41 | -- The reason it can actually work is because the input of the subproblem will increase with each row or column iteration that is stored in the subtable. 42 | 43 | So if I am grabbing the max value, I will update the dp table starting from the right: 44 | 45 | Step one will look like this. 46 | dp[][] = {{0, 0, 0, 0}, 47 | {0, 0, 0, 0}, 48 | {0, 0, 0, 0}, 49 | {0, 0, 0, 0}}; 50 | 51 | Step two will have nothing to compare to, so you only get 15: 52 | dp[][] = {{0, 0, 0, 15}, 53 | {0, 0, 0, 0}, 54 | {0, 0, 0, 0}, 55 | {0, 0, 0, 0}}; 56 | 57 | Step three will have compared 1 and 15 and chosen 15 to add to the 13 that is at the number 2 index of the first nested array: 58 | dp[][] = {{0, 0, 28, 15}, 59 | {0, 0, 0, 0}, 60 | {0, 0, 0, 0}, 61 | {0, 0, 0, 0}}; 62 | 63 | The algorithm is greedish, so it takes the max of what it can see, but in the next nested array (mat[1]) it will see the results in the dp table from the first pass through and can choose those as well. 64 | -------------------------------------------------------------------------------- /dynamicProgramming/knapsack01/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func max(a, b int) int { 6 | if a > b { 7 | return a 8 | } 9 | 10 | return b 11 | } 12 | 13 | // In this algorithm the W is the total weight of the sack 14 | // wt is the weight of each item at an index 15 | // val is the value of the item at the index 16 | // and the n is the number of items 17 | func knapSackGreedyRecursive(W int, wt []int, val []int, n int) int { 18 | if W == 0 || n == 0 { 19 | return 0 20 | } 21 | 22 | if wt[n-1] > W { 23 | return knapSackGreedyRecursive(W, wt, val, n-1) 24 | } 25 | 26 | return max((val[n-1] + knapSackGreedyRecursive(W-wt[n-1], wt, val, n-1)), knapSackGreedyRecursive(W, wt, val, n-1)) 27 | } 28 | 29 | func knapsackGreedyDP(W int, wt []int, val []int, n int) int { 30 | var K [][]int 31 | for i := 0; i <= n; i++ { 32 | K = append(K, []int{}) 33 | for w := 0; w <= W; w++ { 34 | K[i] = append(K[i], 0) 35 | } 36 | } 37 | 38 | for i := 0; i <= n; i++ { 39 | for w := 0; w <= W; w++ { 40 | if i == 0 || w == 0 { 41 | K[i][w] = 0 42 | } else if wt[i-1] <= w { 43 | K[i][w] = max(val[i-1]+K[i-1][w-wt[i-1]], K[i-1][w]) 44 | } else { 45 | K[i][w] = K[i-1][w] 46 | } 47 | } 48 | } 49 | 50 | return K[n][W] 51 | } 52 | 53 | func main() { 54 | fmt.Println("vim-go") 55 | 56 | fmt.Println(knapSackGreedyRecursive(50, []int{10, 20, 30}, []int{60, 100, 120}, 3)) 57 | fmt.Println(knapsackGreedyDP(50, []int{10, 20, 30}, []int{60, 100, 120}, 3)) 58 | } 59 | -------------------------------------------------------------------------------- /dynamicProgramming/knapsack01/readme.md: -------------------------------------------------------------------------------- 1 | # knapsack-0-1 2 | 3 | ## Problem Definition 4 | A thief is robbing a store and can carry a maximal weight of W into his knapsack. There are n items and weight of ith item is wi and the profit of selecting this item is pi. What items should the thief take? 5 | 6 | ## Questions to Ask (Not too many, since this is a classic problem): 7 | - Are we sure the values are integers? 8 | - How many copies of the items are allowed (in the classic question, it's usually just 1)? 9 | - Should we try to optimize with DP? (Usually yes) 10 | 11 | ## Personal Account 12 | The knapsack problem is one I have found particularly confusing in the past. 13 | The recursive problem seems understandable by following this sequence: 14 | 1. The algorithm starts with the last item, sees if taking it, by decreasing the sack weight (W - wt[n - 1]) and adding the value(val[n - 1] + recursive call) end up with a larger value than not choosing it, just making the recursive call n - 1. 15 | 2. Return the max of both of those values. 16 | 17 | Essentially, we just start from the last value and incrementally compare the result of taking and not taking each item. 18 | 19 | This does prove to be slow, as we do MANY comparisons for each situation and add the same value of an item over and over again when we already know how much it will add. 20 | 21 | I also always wondered why we can build up a 2d array with this problem. 22 | Now I know. 23 | One of the indices is all of the weights the knapsack could have from 0 to Max weight (W). 24 | The other index is the item index. 25 | 26 | So for each item, we see if the item or w is 0. If so, return 0. 27 | if it's not 0, and the wt of the current item is less than the weight index we are at, we set the current 28 | item and weight value as the max of the value of the current item plus the value of the bag when we take the item and subtract the weight and the 29 | value we have recorded of when we don't take the item and maintain the current weight. 30 | 31 | Even though this previous index was built earlier in the loop, it describes the situation where we have already taken the item. For example, let's say that we are describing a situation on the second item 32 | where the weight of the item is 20, the value is 30, and the current weight of the knapsack is 23. In the max calculation at K[2][23], we will compare max(20 + K[1][3], K[1][23]). 33 | The first option was built earlier in the for loop and describes the decisions you can make when you have those weight limitations at that point. So we are really comparing the max of 20 + best value possible at capacity 3 with previous item and best value possible at capacity 23 with previous item. 34 | 35 | Else, if the weight of the item is greater than the current weight index, we set the value of the bag as the same as it was before this item, just like we did in the recursive solution. 36 | 37 | ## Further Observations 38 | Observation: In the Knapsack DP solution, we have a 2d array where the inner arrays describe encountering an item with specific weights in the knapsack. 39 | 40 | So each actual inner array represents encountering an item at a period of time, and the 1, 2, 3, 4, 5 indices in the inner arrays describe the weight of the bag at that time. 41 | [ 42 | 1 [1, 2, 3, 4, 5] 43 | 2 [1, 2, 3, 4, 5] 44 | 3 [1, 2, 3, 4, 5] 45 | ] 46 | 47 | We actually also calculate some weight item pairs that are probably impossible combinations in the grand scheme of things, because they are not actual combinations of weights that the items comprise. 48 | 49 | We run the algorithm on each combination and optimize for max value, then return the max value at the biggest index, the max of maxes so to speak. 50 | 51 | -------------------------------------------------------------------------------- /dynamicProgramming/longestIncreasingSubsequence/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func longestIncreasingSubsequence(arr []int) int { 6 | longestSubsequence := []int{} 7 | longestLen := 0 8 | 9 | for i := 0; i < len(arr); i++ { 10 | fmt.Println(longestSubsequence) 11 | num := arr[i] 12 | if len(longestSubsequence) == 0 || num > longestSubsequence[len(longestSubsequence)-1] { 13 | longestSubsequence = append(longestSubsequence, num) 14 | } else { 15 | idx := 0 16 | longSubLength := len(longestSubsequence) - 1 17 | 18 | // use binary search to see if any numbers in the already assembled sequence array are less than the current number 19 | // if they are, keep binary searching to either find a number that is bigger, make the length where that number is and swap it 20 | // or keep searching until you do not find a number that is bigger (then we will append it to the end) 21 | for idx < longSubLength { 22 | fmt.Println("longSubLength: ", longSubLength) 23 | fmt.Println("num: ", num) 24 | fmt.Println("idx: ", idx) 25 | mid := (idx + longSubLength) / 2 26 | if longestSubsequence[mid] < num { 27 | idx += mid + 1 28 | } else { 29 | longSubLength = mid 30 | } 31 | } 32 | longestSubsequence[longSubLength] = num 33 | } 34 | 35 | longestLen = len(longestSubsequence) 36 | } 37 | fmt.Println(longestSubsequence) 38 | return longestLen 39 | } 40 | 41 | func main() { 42 | fmt.Println(longestIncreasingSubsequence([]int{2, 3, 4, 7, 13, 17, 9, 16})) 43 | } 44 | -------------------------------------------------------------------------------- /dynamicProgramming/longestIncreasingSubsequence/readme.md: -------------------------------------------------------------------------------- 1 | # Longest Increasing Subsequence 2 | 3 | ## Better Solution 4 | - In the better solution, a table is used to store the actual sequence of the longest subsequence inside of it. 5 | - As we loop through the numbers of the array, we check for a couple conditions: 6 | 1. Is the array empty? 7 | - If the array is empty, we append the current number to the table slice, and continue looping through the array 8 | 2. Is the current number greater than the number that is currently at the end of the table slice? 9 | - If the current number is grater than the number that is currently at the end of the table slice, we can also append to the end of the array and increase the length. 10 | 3. Any other case 11 | - In any other case, we use binary search on the table of subsequence to look for two other conditions 12 | - 1. we find elements in the current subsequence that are less than the number (which means the number could be in the longestIncreasingSubsequence) 13 | - 2. We find numbers that are bigger than the number in the table. In this situation, we set the length of the binary search to this middle number, and keep looking further down for other numbers that are not bigger. 14 | 4. Finally, we add the current number to the end of the longestSubsequence array, MAYBE REPLACING THE OTHER SUBSEQUENCE NUMBER. Since we only need to know how LONG the subsequence is, replacing a larger number with a smaller number that has the same length sequence is fine. 15 | 16 | So, for example, for the current subsequence array [2, 3, 7, 108], if in the for loop we encounter 18, we will 17 | 1. loop through binary search 18 | 2. Figure out that the 18 is less than 108 and 19 | 3. replace the 18 with the 108 at the end by making the longSubLength the index of 108 (3). 20 | 21 | the function then returns 4, the length of the longestSubsequence in O(n log n) time. 22 | -------------------------------------------------------------------------------- /dynamicProgramming/maxSumOfThreeSubarrays/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func createDefaultIntSlice(l int) (s []int) { 6 | for i := 0; i < l; i++ { 7 | s = append(s, 0) 8 | } 9 | 10 | return 11 | } 12 | 13 | func maxSumOfThreeSubarrays(nums []int, k int) []int { 14 | sum := 0 15 | win := createDefaultIntSlice(len(nums) - k + 1) 16 | fmt.Println(nums) 17 | for i := 0; i < len(nums); i++ { 18 | fmt.Printf("before addition: %d\n", sum) 19 | sum += nums[i] 20 | fmt.Printf("after addition: %d\n", sum) 21 | if i >= k { 22 | sum -= nums[i-k] 23 | } 24 | fmt.Printf("after subtraction: %d\n", sum) 25 | if i >= k-1 { 26 | win[i-(k-1)] = sum 27 | } 28 | } 29 | fmt.Println(win) 30 | fmt.Println(sum) 31 | 32 | j := 0 33 | left := createDefaultIntSlice(len(win)) 34 | 35 | for i := 0; i < len(win); i++ { 36 | if win[i] > win[j] { 37 | j = i 38 | } 39 | left[i] = j 40 | } 41 | 42 | j = len(win) - 1 43 | right := createDefaultIntSlice(len(win)) 44 | 45 | for i := len(win) - 1; i >= 0; i-- { 46 | if win[i] >= win[j] { 47 | j = i 48 | } 49 | right[i] = j 50 | } 51 | 52 | max := []int{-1, -1, -1} 53 | for b := k; b < len(win)-k; b++ { 54 | a := left[b-k] 55 | c := right[b+k] 56 | if max[0] == -1 || win[a]+win[b]+win[c] > win[max[0]]+win[max[1]]+win[max[2]] { 57 | max[0] = a 58 | max[1] = b 59 | max[2] = c 60 | } 61 | } 62 | return max 63 | } 64 | 65 | func main() { 66 | fmt.Println(maxSumOfThreeSubarrays([]int{1, 2, 1, 2, 6, 7, 5, 1}, 2)) 67 | } 68 | -------------------------------------------------------------------------------- /dynamicProgramming/maxSumOfThreeSubarrays/readme.md: -------------------------------------------------------------------------------- 1 | # maxSumOfThreeSubarrays 2 | 3 | ## Problem Definition 4 | In a given array nums of positive integers, find three non-overlapping subarrays with maximum sum. Each subarray will be of size k, and we want to maximize the sum of all 3*k entries. Return the result as a list of indices representing the starting position of each interval (0-indexed). If there are multiple answers, return the lexicographically smallest one. Example: 5 | 6 | Input: [1,2,1,2,6,7,5,1], 2 7 | Output: [0, 3, 5] 8 | Explanation: Subarrays [1, 2], [2, 6], [7, 5] correspond to the starting indices [0, 3, 5]. 9 | We could have also taken [2, 1], but an answer of [1, 3, 5] would be lexicographically larger. 10 | 11 | ## Questions to ask 12 | - Is the maximum sum talking about the maximum sum within the array, or the maximum sum of relative indices in the array? 13 | -- I.E. so if I had 3 arrays [1, 2], [3, 4], [5 6] would I be adding the arrays together, or adding the relative indices together? 14 | -------------------------------------------------------------------------------- /dynamicProgramming/minCostClimbingStairs/readme.md: -------------------------------------------------------------------------------- 1 | # Min Cost Climbing Stairs 2 | -------------------------------------------------------------------------------- /dynamicProgramming/minimumStepsToOne/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func buildTable(n int) []int { 6 | var table []int 7 | for i := 0; i < n; i++ { 8 | table = append(table, 0) 9 | } 10 | 11 | return table 12 | } 13 | 14 | func min(a, b int) int { 15 | if a < b { 16 | return a 17 | } 18 | 19 | return b 20 | } 21 | 22 | func minimumStepsToOne(n int) int { 23 | dp := buildTable(n + 1) 24 | dp[1] = 0 25 | for i := 2; i <= n; i++ { 26 | dp[i] = 1 + dp[i-1] 27 | if i%2 == 0 { 28 | dp[i] = min(dp[i], 1+dp[i/2]) 29 | } 30 | 31 | if i%3 == 0 { 32 | dp[i] = min(dp[i], 1+dp[i/3]) 33 | } 34 | } 35 | 36 | return dp[n] 37 | } 38 | 39 | func main() { 40 | fmt.Println("vim-go") 41 | fmt.Println(minimumStepsToOne(1)) 42 | fmt.Println(minimumStepsToOne(2)) 43 | fmt.Println(minimumStepsToOne(4)) 44 | fmt.Println(minimumStepsToOne(7)) 45 | } 46 | -------------------------------------------------------------------------------- /dynamicProgramming/minimumStepsToOne/readme.md: -------------------------------------------------------------------------------- 1 | # minimum steps to one 2 | 3 | ## Problem Definition 4 | On a positive integer, you can perform any one of the following 3 steps: 5 | 1. Subtract one from it 6 | 2. If it's divisible by 2, divide by 2 7 | 3. If it's divisible by 3, divide by 3 8 | 9 | Given a positive integer n, find the minimum number of steps that takes n to 1. 10 | 11 | ## Thoughts on Bottom-Up solution 12 | - In general, we build a table and for each index run each operation (if they apply): 1 + dp[index], 1 + dp[index % 2], and 1 + dp[index % 3] 13 | - Because we always attach the min number of steps with each operation to that index in the table, we are effectively trying out each operation that could happen at that index and only keeping the least one. 14 | - The value at each index in the table is the minimum amount of steps that is required to get to that index in the table 15 | - We do not have the actual steps that are required to get to that index, however. 16 | 17 | ## Questions to Ask 18 | - Are there any space or time constraints on the problem? 19 | - Do we need to find out exactly which operations get to that index? 20 | - Do you want us to use top-down, bottom-up, or any specific solution? 21 | 22 | -------------------------------------------------------------------------------- /dynamicProgramming/nStaircase/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // first iteration without adding dynamic programming table 6 | // The space complexity of this will be high because it is re-figuring out the same 7 | // sub-problems multiple times 8 | func climbStairs(n int) int { 9 | // the case where someone jumped down too far 10 | if n < 0 { 11 | return 0 12 | } 13 | 14 | // the case where there is a complete solution, add one to the total number of solutions 15 | if n == 0 { 16 | return 1 17 | } 18 | 19 | return climbStairs(n-1) + climbStairs(n-2) 20 | } 21 | 22 | func climbStairsDynamic(n int, solutionTable map[int]int) int { 23 | if n < 0 { 24 | return 0 25 | } 26 | 27 | if n == 0 { 28 | return 1 29 | } 30 | 31 | // adds check in map to see if the solution has already been found before 32 | if _, ok := solutionTable[n]; ok { 33 | return solutionTable[n] 34 | } 35 | 36 | solutionTable[n] = climbStairsDynamic(n-1, solutionTable) + climbStairsDynamic(n-2, solutionTable) 37 | 38 | return solutionTable[n] 39 | } 40 | 41 | func climbStairsVariableJumps(n int, stepCount []int) int { 42 | if n == 0 { 43 | return 1 44 | } 45 | 46 | if n < 0 { 47 | return 0 48 | } 49 | 50 | total := 0 51 | 52 | // use a for loop through the steps array to calculate for each step 53 | // aggregate to a total and return the total on each step 54 | for i := len(stepCount) - 1; i >= 0; i-- { 55 | total += climbStairsVariableJumps(n-stepCount[i], stepCount) 56 | } 57 | 58 | return total 59 | } 60 | 61 | func climbStairsVariableJumpsDynamic(n int, stepCount []int, stepTable map[int]int) int { 62 | if n == 0 { 63 | return 1 64 | } 65 | 66 | if n < 0 { 67 | return 0 68 | } 69 | 70 | if _, ok := stepTable[n]; ok { 71 | return stepTable[n] 72 | } 73 | 74 | // prevent assignment issues by initializing the first value at 0 (although I think it will be 0 anyways) 75 | stepTable[n] = 0 76 | for i := len(stepCount) - 1; i >= 0; i-- { 77 | stepTable[n] += climbStairsVariableJumpsDynamic(n-stepCount[i], stepCount, stepTable) 78 | } 79 | 80 | return stepTable[n] 81 | } 82 | 83 | func main() { 84 | fmt.Println(climbStairs(4)) 85 | fmt.Println(climbStairsDynamic(4, make(map[int]int))) 86 | fmt.Println(climbStairsVariableJumps(4, []int{1, 2})) 87 | fmt.Println(climbStairsVariableJumpsDynamic(4, []int{1, 2}, make(map[int]int))) 88 | 89 | } 90 | -------------------------------------------------------------------------------- /dynamicProgramming/nStaircase/readme.md: -------------------------------------------------------------------------------- 1 | # nStaircase 2 | 3 | ## Problem Definition 4 | There exists a staircase with N steps, and you can climb up either 1 or 2 steps at a time. Given N, write a function that returns the number of unique ways you can climb the staircase. The order of the steps matters. 5 | 6 | For example, if N is 4, then there are 5 unique ways: 7 | 8 | 1, 1, 1, 1 9 | 2, 1, 1 10 | 1, 2, 1 11 | 1, 1, 2 12 | 2, 2 13 | 14 | What if, instead of being able to climb 1 or 2 steps at a time, you could climb any number from a set of positive integers X? For example, if X = {1, 3, 5}, you could climb 1, 3, or 5 steps at a time? 15 | 16 | ## Questions to ask? 17 | - Is there a specific space complexity I should solve it under? 18 | -------------------------------------------------------------------------------- /dynamicProgramming/paintHouses/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func min(a, b int) { 6 | if a < b { 7 | return a 8 | } 9 | 10 | return b 11 | } 12 | 13 | func paintHouses(costs [][]int) int { 14 | if len(costs) == 0 || len(costs[0]) == 0 { 15 | return 0 16 | } 17 | 18 | for i := 1; i < len(costs); i++ { 19 | costs[i][0] += min(costs[i-1][1], costs[i-1][2]) 20 | costs[i][1] += min(costs[i-1][0], costs[i-1[2]]) 21 | costs[i][2] += min(costs[i-1][0], costs[i-1][1]) 22 | } 23 | 24 | m := len(costs) - 1 25 | 26 | return min(min(costs[m][0], costs[m][1]), costs[m][2]) 27 | } 28 | 29 | func main() { 30 | fmt.Println("vim-go") 31 | } 32 | -------------------------------------------------------------------------------- /dynamicProgramming/paintHouses/readme.md: -------------------------------------------------------------------------------- 1 | # paint-houses 2 | 3 | ## Problem Definition 4 | There are a row of n houses, each house can be painted with one of the three colors: red, blue or green. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color. 5 | 6 | The cost of painting each house with a certain color is represented by a n x 3 cost matrix. For example, costs[0][0] is the cost of painting house 0 with color red; costs[1][2] is the cost of painting house 1 with color green, and so on... Find the minimum cost to paint all houses. 7 | 8 | ## Questions to Ask 9 | - Are the costs of each house positive numbers? 10 | - Can we reuse the array we are given, or should we build a new one? 11 | 12 | ## Analysis 13 | Since the amount of houses is finite, you just need to store in each row what the minimum cost of painting this house with the two separate colors is at this array index. 14 | So for the first cost index in the array. 15 | For Array Index [1][0], which is the first index of the second array in the 2d array, we add the minimum of the paint cost of the painting of the previous house, house 1, to the current cost of painting this house, house 1. 16 | So if the previous cost array (for house 1) was [5, 2, 3], and this current cost is [4, 6, 8], then for costs[1][0] we would add 4 to the minimum of 2 and 3, which is 2. So four would become six and would represent the permutation of painting the first house the 1 color, and the second house the 0 color. 17 | We continue to do this for each house and obtain the cost of painting all the houses in the final house's paint cost. 18 | We find the min of those 3 permutations, and return that number 19 | 20 | ## Array params 21 | The i param, or the nested arrays represent which house is being painted. 22 | The j param, or the indices in each array, represents the cost of the color of the house painted at each index. 23 | -------------------------------------------------------------------------------- /dynamicProgramming/robHousesStreet/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func max(a, b int) int { 8 | if a > b { 9 | return a 10 | } 11 | return b 12 | } 13 | 14 | func robHouse(nums []int) int { 15 | val := 0 16 | for i := len(nums); i >= 0; i-- { 17 | temp := robHouses(nums[:i], make(map[int]int)) 18 | val = max(temp, val) 19 | } 20 | 21 | return val 22 | } 23 | 24 | func robHouses(nums []int, alreadyRobbed map[int]int) int { 25 | lastIndex := len(nums) - 1 26 | // check for 0 27 | if len(nums) < 1 || nums[lastIndex] == 0 { 28 | return 0 29 | } 30 | 31 | if _, ok := alreadyRobbed[lastIndex]; ok { 32 | return alreadyRobbed[lastIndex] 33 | } 34 | 35 | minus2 := len(nums) - 2 36 | if minus2 < 0 { 37 | minus2 = 0 38 | } 39 | 40 | minus3 := len(nums) - 3 41 | if minus3 < 0 { 42 | minus3 = 0 43 | } 44 | 45 | alreadyRobbed[lastIndex] = nums[lastIndex] + max(robHouses(nums[:minus2], alreadyRobbed), robHouses(nums[:minus3], alreadyRobbed)) 46 | 47 | return alreadyRobbed[lastIndex] 48 | 49 | } 50 | 51 | func checkIdx(dp []int, i int) int { 52 | if i < 0 { 53 | return 0 54 | } 55 | 56 | return dp[i] 57 | } 58 | 59 | func robHousesDp(nums []int) int { 60 | dp := make([]int, len(nums), len(nums)) 61 | 62 | m := 0 63 | 64 | for i := 0; i < len(nums); i++ { 65 | dp[i] = nums[i] + max(checkIdx(dp, i-2), checkIdx(dp, i-3)) 66 | if dp[i] > m { 67 | m = dp[i] 68 | } 69 | } 70 | 71 | return m 72 | } 73 | 74 | func main() { 75 | fmt.Println(robHouse([]int{1, 2, 3, 1})) 76 | fmt.Println(robHouse([]int{2, 7, 9, 3, 1})) 77 | fmt.Println(robHousesDp([]int{1, 2, 3, 1})) 78 | fmt.Println(robHousesDp([]int{2, 7, 9, 3, 1})) 79 | fmt.Println(robHousesDp([]int{6, 3, 10, 8, 2, 10, 3, 5, 10, 5, 3})) 80 | 81 | } 82 | -------------------------------------------------------------------------------- /dynamicProgramming/robHousesStreet/readme.md: -------------------------------------------------------------------------------- 1 | # Rob houses street 2 | 3 | ## Problem Definition 4 | You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night. 5 | 6 | Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. 7 | 8 | ## Gotchas 9 | - With a top-down approach (memoization) to dynamic programming problems, you must start out finding the recursive algorithm. I locked up because I didn't know that 2 + 3 can add up to represent any number (including primes). 10 | - After finding the correct recursive approach, the memoization part was simple, and very similar to a problem called the child jumping stairs problem. 11 | 12 | ## Time Complexity 13 | - The space complexity of the problem using the table is O(n). 14 | - The time complexity is probably closer to O(n^2). But using the table The worst case might be around O(n) since we only run through each subproblem once. 15 | -------------------------------------------------------------------------------- /graphs/boggleWordFind/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var boggleBoard = [3][3]string{{"A", "P", "P"}, 6 | {"C", "A", "L"}, 7 | {"T", "R", "E"}} 8 | 9 | var dict = []string{"APPLE", "CAR", "CAT", "CARROT"} 10 | 11 | func isWord(str string) bool { 12 | for i := 0; i < len(dict); i++ { 13 | if str == dict[i] { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | func findWordsUtil(i, j int, visited *[][]bool, str *string) { 21 | (*visited)[i][j] = true 22 | *str = *str + boggleBoard[i][j] 23 | if isWord(*str) { 24 | fmt.Println(*str) 25 | } 26 | 27 | for row := i - 1; row <= i+1 && row < len(boggleBoard); row++ { 28 | for col := j - 1; col <= j+1 && col < len(boggleBoard[0]); col++ { 29 | if row >= 0 && col >= 0 && !(*visited)[row][col] { 30 | findWordsUtil(row, col, visited, str) 31 | } 32 | } 33 | } 34 | 35 | *str = (*str)[:len(*str)-1] 36 | (*visited)[i][j] = false 37 | } 38 | 39 | func findWords() { 40 | var visited [][]bool 41 | var str string 42 | for i := 0; i < 3; i++ { 43 | visited = append(visited, []bool{}) 44 | for j := 0; j < 3; j++ { 45 | visited[i] = append(visited[i], false) 46 | } 47 | } 48 | 49 | for i := 0; i < len(boggleBoard); i++ { 50 | for j := 0; j < len(boggleBoard[0]); j++ { 51 | findWordsUtil(i, j, &visited, &str) 52 | } 53 | } 54 | } 55 | 56 | func main() { 57 | findWords() 58 | } 59 | -------------------------------------------------------------------------------- /graphs/connectedComponentsUnconnectedGraph/readme.md: -------------------------------------------------------------------------------- 1 | # Find Connected Components in Unconnected Graph 2 | -------------------------------------------------------------------------------- /graphs/data/dag/sharedDag.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "five": { 4 | "children": ["eleven"] 5 | }, 6 | "seven": { 7 | "children": ["eleven", "eight"] 8 | }, 9 | "eleven": { 10 | "children": ["two", "nine", "ten"] 11 | }, 12 | "eight": { 13 | "children": ["nine"] 14 | }, 15 | "three": { 16 | "children": ["eight", "ten"] 17 | }, 18 | "two": { 19 | "children": [] 20 | }, 21 | "nine": { 22 | "children": [] 23 | }, 24 | "ten": { 25 | "children": [] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /graphs/data/dag/sharedNoCycleDag.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "five": { 4 | "children": ["eleven"] 5 | }, 6 | "seven": { 7 | "children": ["eleven", "eight"] 8 | }, 9 | "eleven": { 10 | "children": ["two", "nine", "ten"] 11 | }, 12 | "eight": { 13 | "children": ["nine"] 14 | }, 15 | "three": { 16 | "children": ["eight", "ten"] 17 | }, 18 | "two": { 19 | "children": [] 20 | }, 21 | "nine": { 22 | "children": [] 23 | }, 24 | "ten": { 25 | "children": [] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /graphs/data/dag/source.md: -------------------------------------------------------------------------------- 1 | # Dag Source 2 | 3 | ## Source 4 | This dag was based on the illustration in the Wikipedia Topological Sort article and can be found at [this link](https://en.wikipedia.org/wiki/Topological_sorting#Examples) 5 | 6 | -------------------------------------------------------------------------------- /graphs/isDag/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | ) 9 | 10 | type VisitedStatus int 11 | 12 | const ( 13 | NotVisited VisitedStatus = iota 14 | JustVisited 15 | PermVisited 16 | ) 17 | 18 | type GraphNode struct { 19 | Children []string `json:"children"` 20 | HasVisited VisitedStatus 21 | } 22 | 23 | type Graph struct { 24 | GraphNodes map[string]*GraphNode `json:"nodes"` 25 | } 26 | 27 | func readGraphFromJson(fileLoc string) Graph { 28 | // read from the data dir in the graphs folder 29 | jsonFile, err := os.Open(fileLoc) 30 | 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | defer jsonFile.Close() 36 | 37 | byteValue, _ := ioutil.ReadAll(jsonFile) 38 | 39 | var g Graph 40 | err = json.Unmarshal(byteValue, &g) 41 | 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | return g 47 | } 48 | 49 | func isDag(g Graph) bool { 50 | isDag := true 51 | for k, _ := range g.GraphNodes { 52 | 53 | graphHasCycle := doesGraphHaveCycle(&g.GraphNodes, k) 54 | if graphHasCycle == true { 55 | isDag = false 56 | break 57 | } 58 | } 59 | 60 | return isDag 61 | } 62 | 63 | func doesGraphHaveCycle(nodes *map[string]*GraphNode, n string) bool { 64 | node := (*nodes)[n] 65 | fmt.Printf("str: %s, node: %v+\n", n, node) 66 | 67 | // if the node HasVisited is true, that means it has been visited before. This means the graph has a cycle and is not a dag 68 | if node.HasVisited == PermVisited { 69 | return false 70 | } 71 | 72 | if node.HasVisited == JustVisited { 73 | return true 74 | } 75 | 76 | // mark node while recursing through all children 77 | node.HasVisited = JustVisited 78 | 79 | for _, nodeStr := range node.Children { 80 | if doesGraphHaveCycle(nodes, nodeStr) == true { 81 | return true 82 | } 83 | } 84 | 85 | node.HasVisited = PermVisited 86 | 87 | return false 88 | } 89 | 90 | func main() { 91 | dagG := readGraphFromJson("../data/dag/sharedDag.json") 92 | fmt.Println(isDag(dagG)) 93 | 94 | a := GraphNode{Children: []string{"b", "c"}} 95 | b := GraphNode{Children: []string{"c"}} 96 | c := GraphNode{Children: []string{"d", "a"}} 97 | d := GraphNode{Children: []string{"a"}} 98 | gm := make(map[string]*GraphNode) 99 | gm["a"] = &a 100 | gm["b"] = &b 101 | gm["c"] = &c 102 | gm["d"] = &d 103 | nonDagG := Graph{GraphNodes: gm} 104 | fmt.Println(isDag(nonDagG)) 105 | } 106 | -------------------------------------------------------------------------------- /graphs/isDag/readme.md: -------------------------------------------------------------------------------- 1 | # isDag 2 | -------------------------------------------------------------------------------- /graphs/isInGraph/List.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Element struct { 4 | next *Element 5 | list *List 6 | Val string 7 | } 8 | 9 | func (e *Element) Next() *Element { 10 | if p := e.next; e.list != nil && p != &e.list.root { 11 | return p 12 | } 13 | 14 | return nil 15 | } 16 | 17 | type List struct { 18 | root Element 19 | len int 20 | } 21 | 22 | func NewList() *List { return new(List).Init() } 23 | 24 | func (l *List) Front() *Element { 25 | if l.len == 0 { 26 | return nil 27 | } 28 | 29 | return l.root.next 30 | } 31 | 32 | func (l *List) InsertAtEnd(e *Element) *Element { 33 | if l.Front() == nil { 34 | l.root.next = e 35 | e.list = l 36 | l.len++ 37 | return e 38 | } 39 | 40 | node := l.Front() 41 | for node.Next() != nil { 42 | node = node.Next() 43 | } 44 | 45 | node.next = e 46 | e.list = l 47 | l.len++ 48 | 49 | return e 50 | } 51 | 52 | func (l *List) Init() *List { 53 | l.root.next = &l.root 54 | l.len = 0 55 | return l 56 | } 57 | -------------------------------------------------------------------------------- /graphs/isInGraph/isInGraph.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func BuildLL(strs []string) *List { 8 | list := NewList() 9 | for idx, _ := range strs { 10 | el := Element{} 11 | el.Val = string(strs[idx]) 12 | list.InsertAtEnd(&el) 13 | } 14 | return list 15 | } 16 | 17 | func IsInGraph(str string, l *List) (foundStr bool) { 18 | foundStr = false 19 | 20 | if str == "" { 21 | return 22 | } 23 | 24 | strHash := make(map[string]int) 25 | for e := l.Front(); e != nil; e = e.Next() { 26 | strHash[e.Val] += 1 27 | } 28 | 29 | for _, char := range str { 30 | if strHash[string(char)] == 0 { 31 | return 32 | } else { 33 | strHash[string(char)] -= 1 34 | } 35 | } 36 | 37 | foundStr = true 38 | return 39 | } 40 | 41 | func main() { 42 | strs1 := []string{"t", "X", "T", "x", "y", "s", "P", "x"} 43 | ll := BuildLL(strs1) 44 | fmt.Println(IsInGraph("tyler", ll)) 45 | } 46 | -------------------------------------------------------------------------------- /graphs/isInGraph/isInGraph_Test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestBuildLL(t *testing.T) { 9 | input := []string{string('t')} 10 | cases := []struct { 11 | in []string 12 | out *List 13 | }{ 14 | { 15 | input, 16 | &List{}, 17 | }, 18 | } 19 | 20 | for _, c := range cases { 21 | var out List 22 | el := Element{} 23 | el.Val = string('t') 24 | out.root = Element{&el, &out, ""} 25 | got := BuildLL(input) 26 | 27 | if out.root.next.Val != got.root.next.Val { 28 | t.Errorf("BuildLL(%g) != %g", c.in, got) 29 | } 30 | } 31 | } 32 | 33 | func TestIsInGraph(t *testing.T) { 34 | input := []string{"t", "X", "T", "x", "y", "s", "P", "x"} 35 | l := BuildLL(input) 36 | cases := []struct { 37 | graph *List 38 | str string 39 | out bool 40 | }{ 41 | { 42 | l, 43 | "tyler", 44 | false, 45 | }, 46 | 47 | { 48 | l, 49 | "cookies", 50 | false, 51 | }, 52 | 53 | { 54 | l, 55 | "tx", 56 | true, 57 | }, 58 | 59 | { 60 | l, 61 | "texas", 62 | false, 63 | }, 64 | 65 | { 66 | l, 67 | "", 68 | false, 69 | }, 70 | } 71 | 72 | for _, c := range cases { 73 | expected := c.out 74 | got := IsInGraph(c.str, c.graph) 75 | fmt.Println(expected) 76 | fmt.Println(got) 77 | 78 | if expected != got { 79 | t.Error("string is not contained within the graph") 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /graphs/isInGraph/readme.md: -------------------------------------------------------------------------------- 1 | # A problem to determine if a string is located in a graph 2 | 3 | ## Problem Statement: 4 | 5 | You have a Graph of strings which can be represented in any way. 6 | 7 | The Nodes look like the following: 8 | 9 | X C O O 10 | X X X K 11 | X S E I 12 | X X X X 13 | 14 | Write a function which takes the graph and a string as input and returns a boolean (true or false) value for whether or not the string can be found in the graph as output. 15 | 16 | ## Learning: 17 | 18 | One of the biggest problems I had when asked this question had to deal with how to represent a graph. 19 | 20 | Through some reading online and a bit of Khan Academy, I knew in the past that graphs can be represented in a number of ways, and I tried to represent it in a way I had heard before. 21 | 22 | 1. An Adjacency Matrix 23 | 24 | An Adjacency Matrix is actually just a fancy word to describe a two-dimensional array. 25 | 26 | Which looks like the following: 27 | [ 28 | [0, 1, 0, 1], 29 | [1, 0, 0, 0], 30 | [0, 0, 0, 0], 31 | [1, 0, 0, 0] 32 | ] 33 | 34 | I originally tried to simply represent the graph in two arrays and put the strings as array indices, like in the following: 35 | 36 | [ 37 | [X, C, O, O], 38 | [X, X, X, K], 39 | [X, S, E, I], 40 | [X, X, X, X] 41 | ] 42 | 43 | However, this was an incorrect understanding of how adjacency matrices work. 44 | 45 | In adjacency matrices, the nested arrays themselves are the vertices in the graph, the number at any index is the weights of the edge connecting that particular vertice 46 | to another, and the position in the nested array determines which vertices the edge connects together. 47 | 48 | So in the following example: 49 | 50 | [ 51 | [0, 1, 0, 1], 52 | [1, 0, 0, 0], 53 | [0, 0, 0, 0], 54 | [1, 0, 0, 0] 55 | ] 56 | 57 | The first vertice, which is the first nested array, or arr[0], is connected to the second and 4th (arr[1] and arr[3] respectively) by edges which have a weight of 1. 58 | 59 | These edges are also reflected in the 2nd vertice (arr[1]) and 4th vertice (arr[3]) by having a one in their 1st index (arr[1][0] and arr[3][0]). 60 | 61 | 2. Enter the linked list 62 | I later learned that I could also represent graphs as linked lists, and this method made a lot more sense to me. 63 | 64 | Simple Linked lists are individual objects which have a value and a link (called a pointer in traditional cs and lower-level languages) to the next object of the same type. 65 | 66 | Using a linked list, 67 | 68 | X C O O 69 | X X X K 70 | X S E I 71 | X X X X 72 | 73 | Can be represented 74 | 75 | X -> C -> O -> O -> X ... and so on. (Notice the end of the first line O is jumping to the next row.) 76 | 77 | Since in the problem statement it was mentioned that I can represent the graph anyway I want, representing it as a linked list is a standard way to solve the problem. 78 | 79 | ----- NEXT ----- 80 | -------------------------------------------------------------------------------- /graphs/iterativeDeepeningDepthFirstSearch/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | gu "github.com/tlboright/golang-interview/graphs/utils" 6 | ) 7 | 8 | func main() { 9 | sharedDagG := gu.ReadGraphFromJson("../data/dag/sharedDag.json") 10 | 11 | fmt.Println(sharedDagG) 12 | } 13 | -------------------------------------------------------------------------------- /graphs/readme.md: -------------------------------------------------------------------------------- 1 | # graph problems 2 | -------------------------------------------------------------------------------- /graphs/topologicalSort/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | gu "github.com/tlboright/golang-interview/graphs/utils" 6 | ) 7 | 8 | func topologicalSort(g gu.Graph) (sortedNodes []string) { 9 | for k, _ := range g.GraphNodes { 10 | visit(&g.GraphNodes, k, &sortedNodes) 11 | } 12 | 13 | return 14 | } 15 | 16 | func visit(nodes *map[string]*gu.GraphNode, n string, sortedNodes *[]string) { 17 | node := (*nodes)[n] 18 | 19 | if node.HasVisited == gu.PermVisited { 20 | return 21 | } 22 | 23 | if node.HasVisited == gu.JustVisited { 24 | panic("The graph you are sorting is not a dag. Topological Sort can only be used to sort Dags.") 25 | } 26 | 27 | // mark node while its dependencies are being evaluated 28 | node.HasVisited = gu.JustVisited 29 | 30 | for _, nodeStr := range node.Children { 31 | visit(nodes, nodeStr, sortedNodes) 32 | } 33 | 34 | node.HasVisited = gu.PermVisited 35 | *sortedNodes = append([]string{n}, *sortedNodes...) 36 | } 37 | 38 | func main() { 39 | sharedDagG := gu.ReadGraphFromJson("../data/dag/sharedDag.json") 40 | 41 | fmt.Println(topologicalSort(sharedDagG)) 42 | 43 | readmeGraph := gu.ReadGraphFromJson("nodeDag.json") 44 | fmt.Println(topologicalSort(readmeGraph)) 45 | } 46 | -------------------------------------------------------------------------------- /graphs/topologicalSort/nodeDag.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "Rust": { 4 | "children": ["NodeJS"] 5 | }, 6 | "NodeJS": { 7 | "children": ["ExpressJS"] 8 | }, 9 | "C++": { 10 | "children": ["NodeJS"] 11 | }, 12 | "ExpressJS": { 13 | "children": [] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /graphs/topologicalSort/readme.md: -------------------------------------------------------------------------------- 1 | # Topological Sort of a Dag 2 | 3 | ## Explanation 4 | The Topological Sort of a DAG (a directed acyclic graph) is one where each directed edge that can be labeled uv (the two vertices that are connected by this edge), the u comes before the v in any ordering made. 5 | If we use the idea of installing dependencies in a project as an example, you can image that each of the nodes in a graph is a dependency that other nodes maybe rely on before being able to be installed. 6 | 7 | Let's say we have a graph of 4 dependencies (I'm making up these dependencies on the fly, they might not work this way): 8 | 1. NodeJS 9 | 2. C++ 10 | 3. Rust 11 | 4. ExpressJS 12 | 13 | In order to install Express JS correctly, I need to install the other dependencies that ExpressJS has BEFORE Express can be installed. 14 | 15 | Let's say that Express JS has the Dependency of NodeJS. That can be represented by the following ascii art: 16 | ``` 17 | |-------| |------| 18 | | | | | 19 | |Express| <---- |NodeJS| 20 | |-------| |------| 21 | ``` 22 | 23 | So in order for Express to be Installed, NodeJS must be installed first. 24 | 25 | In this example, let's say that NodeJS requires two dependencies: Rust and C++. 26 | In order to install NodeJS, Rust and C++ need to be installed as well. 27 | 28 | More Ascii Art: 29 | ``` 30 | |------| 31 | - | | 32 | - | Rust | 33 | |-------| - |------| 34 | | | < 35 | |NodeJS | < 36 | |-------| - |------| 37 | - | | 38 | - | C++ | 39 | |------| 40 | ``` 41 | 42 | In this world, both Rust and C++ must be installed to install NodeJS, which in turn is required to use ExpressJS. 43 | 44 | A topological sort of the graph would return one of the following: 45 | [Rust, C++, NodeJS, ExpressJS] 46 | 47 | [C++, Rust, NodeJS, ExpressJS] 48 | 49 | Which would return the correct order of dependencies that are required to install ExpressJS. 50 | 51 | 52 | ## Implementation 53 | The algorithm I chose to implement Topological Sort in this instance is based off of DFS and can be found [here](https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search). 54 | The reason I loop through the array of nodes and run visit on each node is because these abstract tasks are not directly connected. If I just inputted one node, the DFS would only figure out which tasks are required (children) of that particular node. 55 | With Topological Sort, we can feed an entire graph of disparate tasks that might not have direct relationships with each other and still ensure that they are processed in the correct order. 56 | 57 | The input to the function is the graph represented as a map and the output is a slice of strings that are ordered from left to right. This order represents the tasks on the left being required to be completed before the tasks on the right. 58 | 59 | ## Most Realest Application I can think of right now 60 | I represented the graph as a JSON file that contains nodes as a HashMap in which each node has a name, a value, and the children that are dependent on this node. In most real life programming scenarios I have seen, the dependencies would not know the dependent libraries, rather the dependent libraries would know which 61 | dependencies they have. This would alter the relationships in my previous explanation by changing the meaning of each edge to be read as which depencies are REQUIRED to install the library. 62 | I.E. NodeJS would know that it requires ExpressJS before it can install. Rust and C++ know that they require NodeJS to install, etc. 63 | 64 | We can easily modify the algorithm to calculate this order by having the sorted nodes append to the end of the array in the visit function instead of the beginning. 65 | ``` 66 | ...other logic 67 | 68 | *sortedNodes = append([]string{n}, *sortedNodes...) 69 | // will become 70 | *sortedNodes = append(*sortedNodes, n) 71 | ``` 72 | In this example, the `children` field in the json file will really represent the idea of `dependencies`. 73 | You could also just reverse the sorted array after running the same logic, but it's more fun to figure out a way to do it as we parse through the graph. 74 | -------------------------------------------------------------------------------- /graphs/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | type Graph struct { 10 | GraphNodes map[string]*GraphNode `json:"nodes"` 11 | } 12 | 13 | type GraphNode struct { 14 | Children []string `json:"children"` 15 | HasVisited VisitedStatus 16 | } 17 | 18 | // declare an enum VisitedStatus to map out 3 states in the sort: 19 | // NotVisited -- This GraphNode has never been seen before 20 | // JustVisited -- The GraphNode was seen in the current DFS visit recursive stack. This enables us to check for cycles 21 | // HasVisited -- This node is already in the list of sorted nodes for the graph and should not be visited again 22 | type VisitedStatus int 23 | 24 | const ( 25 | NotVisited VisitedStatus = iota 26 | JustVisited 27 | PermVisited 28 | ) 29 | 30 | func ReadGraphFromJson(fileLoc string) Graph { 31 | // read from the data dir in the graphs folder 32 | jsonFile, err := os.Open(fileLoc) 33 | 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | defer jsonFile.Close() 39 | 40 | byteValue, _ := ioutil.ReadAll(jsonFile) 41 | 42 | var g Graph 43 | err = json.Unmarshal(byteValue, &g) 44 | 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | return g 50 | } 51 | -------------------------------------------------------------------------------- /greedy/jumpGame/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func canJump(nums []int) bool { 6 | n := len(nums) 7 | last := n - 1 8 | for i := n - 2; i >= 0; i-- { 9 | if i + nums[i] >= last { 10 | last = i 11 | } 12 | } 13 | 14 | return last == 0 15 | } 16 | 17 | func main() { 18 | fmt.Println(canJump([]int{2,3,1,1,4})) 19 | fmt.Println(canJump([]int{3,2,1,0,4})) 20 | } -------------------------------------------------------------------------------- /greedy/jumpGame/readme.md: -------------------------------------------------------------------------------- 1 | # Jump Game 2 | 3 | ## Problem Statement 4 | You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position. 5 | 6 | Return true if you can reach the last index, or false otherwise. 7 | 8 | 9 | ``` 10 | Example 1: 11 | 12 | Input: nums = [2,3,1,1,4] 13 | Output: true 14 | Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index. 15 | Example 2: 16 | 17 | Input: nums = [3,2,1,0,4] 18 | Output: false 19 | Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index. 20 | 21 | 22 | Constraints: 23 | 24 | 1 <= nums.length <= 104 25 | 0 <= nums[i] <= 105 26 | ``` 27 | 28 | ## Solution 29 | When I first encountered this problem I wasn't sure how to solve this. 30 | 31 | I thought it might be a dp problem, but it's structure also doesn't seem to require doing continuous calculation while returning the result. 32 | 33 | In further iterations of this problem where we must find the minimum jumps required, using some type of recursion (or dp) is necessary to store all possible combinations we can use. 34 | 35 | It turns out that the main mechanism we use to determine whether a jump can happen is seeing if we can drag a lastJumpSpot index all the way from the left of the array to the beginning (if there is a path). 36 | 37 | We loop backwards through the array, and if the max jump size (each value at the idx) of the idx + the idx is greater than the lastJumpSpot, we modify the lastJumpSpot to equal the current index. 38 | 39 | After we run the algorithm, we return a conditional that the lastJumpSpot == 0 (the beginning of the array). This signifies the frog being able to jump to each spot in the array. 40 | 41 | ## Time / Space Complexity 42 | Since we loop backwards through the array, the time complexity of the solution is O(n), and the space complexity is O(1) as we just keep a pointer to the lastJumpIdx that we modify. 43 | 44 | If we did choose to use a dp array, the space complexity would also be O(n) for each position in the dp array. -------------------------------------------------------------------------------- /hashMap/addTwoNumbers/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | * You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. 7 | 8 | You may assume the two numbers do not contain any leading zero, except the number 0 itself. 9 | 10 | Example: 11 | 12 | Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) 13 | Output: 7 -> 0 -> 8 14 | Explanation: 342 + 465 = 807. 15 | 16 | */ 17 | 18 | /** 19 | * Definition for singly-linked list. 20 | **/ 21 | type ListNode struct { 22 | Val int 23 | Next *ListNode 24 | } 25 | 26 | func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { 27 | carryVal := 0 28 | returnLst := &ListNode{Val: 0} 29 | currentNode := returnLst 30 | for l1 != nil || l2 != nil { 31 | p := l1 32 | if l1 == nil { 33 | p = &ListNode{Val: 0} 34 | } 35 | q := l2 36 | if l2 == nil { 37 | q = &ListNode{Val: 0} 38 | } 39 | sum := p.Val + q.Val + carryVal 40 | 41 | carryVal = sum / 10 42 | currentNode.Next = &ListNode{Val: sum % 10} 43 | currentNode = currentNode.Next 44 | if l1 != nil { 45 | l1 = l1.Next 46 | } 47 | 48 | if l2 != nil { 49 | l2 = l2.Next 50 | } 51 | } 52 | 53 | if carryVal > 0 { 54 | currentNode.Next = &ListNode{Val: carryVal} 55 | } 56 | 57 | return returnLst.Next 58 | } 59 | 60 | func createLinkedList(nums []int) *ListNode { 61 | returnNode := &ListNode{} 62 | currentNode := returnNode 63 | for i, num := range nums { 64 | currentNode.Val = num 65 | if i < len(nums)-1 { 66 | currentNode.Next = &ListNode{} 67 | currentNode = currentNode.Next 68 | } 69 | } 70 | 71 | return returnNode 72 | } 73 | 74 | func main() { 75 | firstNode := createLinkedList([]int{5}) 76 | secondNode := createLinkedList([]int{5}) 77 | res := addTwoNumbers(firstNode, secondNode) 78 | for res != nil { 79 | fmt.Println(res) 80 | res = res.Next 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hashMap/addTwoNumbers/readme.md: -------------------------------------------------------------------------------- 1 | # Add Two Numbers 2 | 3 | ## Problem Statement 4 | You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. 5 | 6 | You may assume the two numbers do not contain any leading zero, except the number 0 itself. 7 | 8 | Example: 9 | 10 | Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) 11 | Output: 7 -> 0 -> 8 12 | Explanation: 342 + 465 = 807. 13 | 14 | ## Things Learned 15 | - Feeding a traversal algorithm a dummy node and manipulating the .Next works well with linked lists 16 | - Not accidentally appending a final Node to the List with a default value is difficult. 17 | - There is a lot to learn from not knowing the answer to a problem, trying the problem by yourself for half an hour, then reading the answer / approach 18 | - If a number is under 10, dividing by 10 will equal 0 19 | - If a number is under 10, modulo by 10 will equal the number 20 | - The carry value might still be populated at the end. We need to add another node outside of the while loop. 21 | 22 | ## Questions to Ask from Problem Statement 23 | - How do you want me to handle linked lists of different lengths? 24 | - How should we handle empty linked lists? 25 | -------------------------------------------------------------------------------- /increasing-triplet-subsequence/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func increasingTriplet(nums []int) bool { 9 | min := nums[0] 10 | second_min := int(math.MaxInt32) 11 | 12 | for i := 1; i < len(nums); i++ { 13 | // This will set the absolute minimum number in the array to min 14 | if nums[i] <= min { 15 | min = nums[i] 16 | // If the minimum number is set, this will set the second-minimum number to the second minimum 17 | } else if nums[i] <= second_min { 18 | second_min = nums[i] 19 | // If both of the top ifs do not occur, which means that the first and second minimums have already been set and there is a third number that is bigger than them, 20 | // return true. We have found an increasing triplet 21 | } else { 22 | return true 23 | } 24 | } 25 | return false 26 | } 27 | 28 | func main() { 29 | fmt.Println(increasingTriplet([]int{5, 4, 3, 2, 1})) 30 | fmt.Println(increasingTriplet([]int{5, 4, 3, 6, 3})) 31 | fmt.Println(increasingTriplet([]int{5, 4, 3, 6, 7})) 32 | fmt.Println(increasingTriplet([]int{5, 2, 3, 1, 7})) 33 | fmt.Println(increasingTriplet([]int{1, 2, 3, 5, 6, 7})) 34 | } 35 | -------------------------------------------------------------------------------- /increasing-triplet-subsequence/readme.md: -------------------------------------------------------------------------------- 1 | # increasing-triplet-subsequence 2 | 3 | ## Definition 4 | Given an unsorted array, return whether an increasing subsequence of length 3 exists in the array or not. 5 | 6 | Input: An array 7 | Output: True or False 8 | 9 | ## Formal Definition of the function 10 | Return true if there exists i, j, k 11 | such that arr[i] < arr[j] < arr[k] given 0 <= i < j < k <= n - 1 12 | 13 | Else return false 14 | 15 | ## Examples 16 | Example 1: 17 | Input: [1, 2, 3, 4, 5] 18 | Output: true 19 | 20 | 21 | Example 2: 22 | Input: [5, 4, 3, 2, 1] 23 | Output: false 24 | 25 | Example 3: 26 | Input: [5, 3, 1, 6, 3, 8] 27 | Output: true 28 | 29 | ## Gotchas 30 | - Need to set the second_min to the highest number in the language, or else it might conflict with the min pointer. 31 | 32 | ## Questions to ask 33 | - Should the numbers need to be able to be increased consecutively. Or if ANY three numbers are in increasing order do they count? 34 | 35 | ## Things learned 36 | - It is important in this question to specify exactly what the question is asking for. It seems that it could be extremely simple, deceptively simple, and would throw many 37 | people off track with an unclear definition. 38 | -------------------------------------------------------------------------------- /lengthLongestSubstring/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func getMax(i, j int) int { 8 | if i > j { 9 | return i 10 | } 11 | return j 12 | } 13 | 14 | func lengthOfLongestSubstring(s string) int { 15 | seenHash := make(map[string]int) 16 | ans := 0 17 | for i, j := 0, 0; j < len(s); j++ { 18 | strChar := string(s[j]) 19 | if _, ok := seenHash[strChar]; ok == true { 20 | i = getMax(seenHash[strChar], i) 21 | } 22 | 23 | ans = getMax(ans, j-i+1) 24 | seenHash[strChar] = j + 1 25 | } 26 | 27 | return ans 28 | } 29 | 30 | func main() { 31 | fmt.Println(lengthOfLongestSubstring("abcabcabb")) 32 | } 33 | -------------------------------------------------------------------------------- /lengthLongestSubstring/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestLengthOfLongestSubstring(t *testing.T) { 6 | cases := []struct { 7 | expected int 8 | stringToTest string 9 | }{ 10 | { 11 | 3, 12 | "abcabc", 13 | }, 14 | { 15 | 4, 16 | "pwwkewppooo", 17 | }, 18 | { 19 | 4, 20 | "pokeppp", 21 | }, 22 | { 23 | 1, 24 | "aaaaaa", 25 | }, 26 | { 27 | 3, 28 | "dvdk", 29 | }, 30 | } 31 | 32 | for _, c := range cases { 33 | actual := lengthOfLongestSubstring(c.stringToTest) 34 | if c.expected != actual { 35 | t.Errorf("The expected length of the longest Substring didn't match the actual. Expected: %d\nActual: %d", c.expected, actual) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lengthLongestSubstring/readme.md: -------------------------------------------------------------------------------- 1 | # Length of Longest Substring 2 | 3 | ## Problem Statement 4 | Given a string, find the length of the longest substring without repeating characters. 5 | 6 | Example 1: 7 | 8 | Input: "abcabcbb" 9 | Output: 3 10 | Explanation: The answer is "abc", with the length of 3. 11 | 12 | Example 2: 13 | 14 | Input: "bbbbb" 15 | Output: 1 16 | Explanation: The answer is "b", with the length of 1. 17 | 18 | Example 3: 19 | 20 | Input: "pwwkew" 21 | Output: 3 22 | Explanation: The answer is "wke", with the length of 3. 23 | Note that the answer must be a substring, "pwke" is a subsequence and not a substring. 24 | 25 | ## Things Learned 26 | - In golang, the first part of a map accessor returned is the value stored under the key. 27 | -- If there is no value stored under the key, that value is the empty value for that type i.e. for a map[string]int, val, _ := map["cookies"], if there are no cookies, this value is 0. 28 | - If I just loop through and assign the characters to the hash, I only get the tail end comparison. If we imagine the examined substring as a "Sliding Window", my window has gaps because I'm blindly resetting on each duplicate character. I'm not comparing the substrings in between. 29 | - The Sliding Window can be optimized: instead of moving the pointer one at a time when a duplicate is found, we can store the index of the duplicate each time it is found and use it to jump the Head pointer of the sliding window to the location of the duplicated letter instead of incrementing it by one. 30 | - In this way, instead of potentially having a time complexity of O(2n), we only have to loop through the elements once and can have a time complexity of O(n) since we won't have to wait for the Head pointer AND Tail pointer to move to the end of the string. 31 | 32 | ## Questions to Ask from Problem Statement 33 | - Are there any memory constraints that are imposed on the algorithm? 34 | - Can we assume all strings are lowercase letters, or will there be capital letters too? 35 | - If a character in the substring is found to repeat, do we count that character as the beginning of the new substring? 36 | 37 | ## Complexity 38 | - The run time complexity of the algorithm should be O(length of string), which is O(n) 39 | - The space complexity is in the worst-case O(length of string) for when the string has all unique characters and the hash stores each character in the string, which is also O(n) 40 | -------------------------------------------------------------------------------- /linkedlist/ConstantReverseLL/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Node struct { 6 | Data int 7 | Next *Node 8 | } 9 | 10 | func newNode(num int) *Node { 11 | return &Node{Data: num} 12 | } 13 | 14 | func createList(nums []int) *Node { 15 | root := newNode(nums[0]) 16 | idx := root 17 | for i := 1; i <= len(nums)-1; i++ { 18 | num := newNode(nums[i]) 19 | idx.Next = num 20 | idx = num 21 | } 22 | 23 | return root 24 | } 25 | 26 | func reverseList(root *Node) *Node { 27 | var prev *Node 28 | for current := root; current != nil; { 29 | next := current.Next 30 | current.Next = prev 31 | prev = current 32 | current = next 33 | } 34 | 35 | return prev 36 | } 37 | 38 | func main() { 39 | list := createList([]int{1, 2, 3, 4, 5}) 40 | for current := list; current != nil; { 41 | fmt.Println(current) 42 | current = current.Next 43 | } 44 | reversed := reverseList(list) 45 | 46 | for current := reversed; current != nil; { 47 | fmt.Println(current) 48 | current = current.Next 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /linkedlist/ConstantReverseLL/readme.md: -------------------------------------------------------------------------------- 1 | # Reverse a Linked List 2 | 3 | ## Problem Statement 4 | Given a linked list node, Reverse a linked list. 5 | 6 | Can you do it in O(1) space? 7 | 8 | ## Questions to ask 9 | - Not sure what to ask for this one. It seems pretty straightforward. 10 | 11 | ## Analysis 12 | By using many pointers to prev, current, and next nodes, it works out pretty well that we can reverse the list in 13 | O(n) time and O(1) space. 14 | 15 | -------------------------------------------------------------------------------- /linkedlist/deleteLLNode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Node struct { 6 | Data int 7 | Next *Node 8 | } 9 | 10 | func newNode(num int) *Node { 11 | return &Node{Data: num} 12 | } 13 | 14 | func createList(nums []int) *Node { 15 | root := newNode(nums[0]) 16 | idx := root 17 | for i := 1; i <= len(nums)-1; i++ { 18 | num := newNode(nums[i]) 19 | idx.Next = num 20 | idx = num 21 | } 22 | 23 | return root 24 | } 25 | 26 | func deleteListNode(entry Node, root Node) *Node { 27 | var indirect *Node 28 | indirect = &(root) 29 | for (*indirect) != entry { 30 | indirect = indirect.Next 31 | } 32 | *indirect = *entry.Next 33 | 34 | return indirect 35 | } 36 | 37 | func main() { 38 | list := createList([]int{1, 2, 3, 4, 5}) 39 | list2 := &Node{6, list} 40 | for current := list2; current != nil; { 41 | fmt.Println(current) 42 | current = current.Next 43 | } 44 | 45 | list2 = deleteListNode(*list2, *list2) 46 | for current := list2; current != nil; { 47 | fmt.Println(current) 48 | current = current.Next 49 | } 50 | 51 | secList := newNode(1) 52 | third := newNode(2) 53 | fourth := newNode(3) 54 | secList.Next = third 55 | third.Next = fourth 56 | third = deleteListNode(*third, *secList) 57 | for current := secList; current != nil; { 58 | fmt.Println(current) 59 | current = current.Next 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /linkedlist/deleteLLNode/readme.md: -------------------------------------------------------------------------------- 1 | # deleteLLNode 2 | 3 | ## Problem Statement 4 | Implement an delete LLNode operation in a pointed language which does not use conditionals 5 | This was mentioned in a talk on code taste linus torvalds did that was highlighted in this article: https://grisha.org/blog/2013/04/02/linus-on-understanding-pointers/ 6 | 7 | ## Analysis 8 | - This algorithm runs in O(n) time complexity where n is the length of the linked list 9 | -------------------------------------------------------------------------------- /linkedlist/insertLLNode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Node struct { 6 | Data int 7 | Next *Node 8 | } 9 | 10 | func newNode(num int) *Node { 11 | return &Node{Data: num} 12 | } 13 | 14 | func createList(nums []int) *Node { 15 | root := newNode(nums[0]) 16 | idx := root 17 | for i := 1; i <= len(nums)-1; i++ { 18 | num := newNode(nums[i]) 19 | idx.Next = num 20 | idx = num 21 | } 22 | 23 | return root 24 | } 25 | 26 | func insertListNode(head **Node, val int) { 27 | for *head != nil { 28 | head = &((*head).Next) 29 | } 30 | *head = &Node{Data: val} 31 | } 32 | 33 | func main() { 34 | list := createList([]int{1, 2, 3, 4, 5}) 35 | for current := list; current != nil; { 36 | fmt.Println(current) 37 | current = current.Next 38 | } 39 | insertListNode(&list, 6) 40 | 41 | for current := list; current != nil; { 42 | fmt.Println(current) 43 | current = current.Next 44 | } 45 | 46 | var headNode *Node 47 | insertListNode(&headNode, 5) 48 | fmt.Println(headNode) 49 | } 50 | -------------------------------------------------------------------------------- /linkedlist/insertLLNode/readme.md: -------------------------------------------------------------------------------- 1 | # insertLLNode 2 | 3 | ## Problem Statement 4 | Implement an insert LLNode operation to a linked list in a pointed language which does not use conditionals 5 | This is an iteration over the removeLLNode method mentioned in a talk on code taste linus torvalds did that was highlighted in this article: https://grisha.org/blog/2013/04/02/linus-on-understanding-pointers/ 6 | 7 | ## Analysis 8 | - This algorithm runs in O(n) time complexity where n is the length of the linked list 9 | -------------------------------------------------------------------------------- /linkedlist/removeDuplicates/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type ListNode struct { 6 | Val int 7 | Next *ListNode 8 | } 9 | 10 | // (1) -> (1) -> (1) 11 | // (1) -> (1) -> nil 12 | 13 | func deleteDuplicates(head *ListNode) *ListNode { 14 | ret := head 15 | for head.Next != nil { 16 | if head.Val == (head.Next).Val { 17 | head.Next = head.Next.Next 18 | } else { 19 | head = head.Next 20 | } 21 | } 22 | 23 | return ret.Next 24 | } 25 | 26 | func main() { 27 | nodes := []int{1, 1, 1, 3, 4, 5, 5, 5, 5, 5} 28 | head := &ListNode{} 29 | curr := head 30 | for i := 0; i < len(nodes); i++ { 31 | node := ListNode{Val: nodes[i], Next: nil} 32 | head.Next = &node 33 | head = head.Next 34 | } 35 | 36 | curr = deleteDuplicates(curr) 37 | 38 | for curr != nil { 39 | fmt.Println("nodeVal: ", curr.Val) 40 | curr = curr.Next 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # golang-interview 2 | 3 | ## Description 4 | A repo of interview questions written by a guy who had to teach himself 5 | 6 | 7 | ## How to start learning this stuff by yourself 8 | - LEARN BY CATEGORY 9 | In the past, I chose leetcode / interview topics at random to learn. 10 | Because of this, I never felt confident enough in any particular topic. 11 | When you focus on a topic, you gain the knowledge required to recognize the problem in context, know how to start solving it AND 12 | start to know how the answer code should be constructed. 13 | 14 | If you choose questions haphazardly and haven't solved any similar questions before you will probably not be able to start. 15 | Take a few (I chose around 5) questions on each new topic you learn and see the pattern of solution work in multiple contexts. 16 | 17 | - Review 18 | After completing a few questions on each topic, make sure you find some way to review. 19 | 20 | I like to use a Spaced Repetition System to store my mistakes and create QA pairs of intuitions I've gained through solving multiple problems. 21 | 22 | I also signed up to a couple newletters that ping me each week / day with questions I can choose to solve when I have time. 23 | 24 | This might seem like it violates the "don't choose random question to study" rule, but I think that after you are fairly familiar a few topics, 25 | some type of review mechanism will work to refresh these in your head from time to time. 26 | 27 | ## You Will Fail Interviews 28 | I haven't met any people yet who don't have failure interview experiences. Mine are especially bad. 29 | Keep practicing consistently and reviewing concepts. Do this until you are bored. Then do something else. Then (maybe) start again. 30 | I am not the type of person who can grind these concepts right before interviewing everywhere. Instead, I'm the kind of person who enjoys 31 | the pain of the slow interview grind. 32 | 33 | This might sound more efficient, but I have still yet to get asked to an onsite FANG (Facebook, Amazon, Netflix, Google) interview and have failed each 34 | phone interview so far. :monkey_face: 35 | 36 | So DON'T GIVE UP, keep trying, and also do some projects for fun. If you aren't having fun programming, why do it at all? 37 | Yes, you can make some money starting off, but most (not all) jobs will make you similar money if you become extremely skilled in them. 38 | Many jobs will make you even more when you become great. So program for love, study algorithm questions to learn and interview, 39 | and also try to find projects to enjoy. 40 | -------------------------------------------------------------------------------- /strings/defangingIpAddress/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func defangIPaddr(address string) string { 6 | var ret string 7 | for _, val := range address { 8 | char := string(val) 9 | if string(char) == "." { 10 | ret += "[.]" 11 | continue 12 | } 13 | 14 | ret += char 15 | } 16 | 17 | return ret 18 | } 19 | 20 | func main() { 21 | fmt.Println("vim-go") 22 | fmt.Println(defangIPaddr("255.100.50.0")) 23 | } 24 | -------------------------------------------------------------------------------- /strings/defangingIpAddress/readme.md: -------------------------------------------------------------------------------- 1 | # Defanging IP Address 2 | 3 | ## Problem Description 4 | Given a valid (IPv4) IP address, return a defanged version of that IP address. 5 | 6 | A defanged IP address replaces every period "." with "[.]". 7 | 8 | 9 | 10 | Example 1: 11 | 12 | Input: address = "1.1.1.1" 13 | Output: "1[.]1[.]1[.]1" 14 | Example 2: 15 | 16 | Input: address = "255.100.50.0" 17 | Output: "255[.]100[.]50[.]0" 18 | 19 | 20 | Constraints: 21 | 22 | The given address is a valid IPv4 address. 23 | -------------------------------------------------------------------------------- /strings/findFirstMaxOccurrence/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func max(a, b int) int { 6 | if a > b { 7 | return a 8 | } 9 | 10 | return b 11 | } 12 | 13 | func firstMaxOccurrence(str string) (returnCh string) { 14 | intervals := make(map[rune]int) 15 | maxVal := 0 16 | 17 | for _, c := range str { 18 | intervals[c]++ 19 | maxVal = max(maxVal, intervals[c]) 20 | } 21 | 22 | for _, c := range str { 23 | if intervals[c] == maxVal { 24 | returnCh = string(c) 25 | return 26 | } 27 | } 28 | 29 | return 30 | } 31 | 32 | func main() { 33 | fmt.Println(firstMaxOccurrence("cacb")) 34 | fmt.Println(firstMaxOccurrence("bcbc")) 35 | fmt.Println(firstMaxOccurrence("GeeksforGeeks")) 36 | fmt.Println(firstMaxOccurrence("GeeksQuiz")) 37 | } 38 | -------------------------------------------------------------------------------- /strings/findFirstMaxOccurrence/readme.md: -------------------------------------------------------------------------------- 1 | # findFirstMaxOccurrence 2 | 3 | ## Problem Definition 4 | Given a string, find its first Most Repeated character 5 | 6 | Given a string, find the first most repeatedcharacter in it. For example, if the input string is “GeeksforGeeks”, then output should be ‘e’ and if input string is “GeeksQuiz”, then output should be ‘G’. 7 | 8 | If the string was "aaabbb" the output would be "a", and if the string was "abbccbccb" the output would be b. 9 | ## Questions 10 | - Is there a specific time / space complexity you want it in? 11 | - How do you want me to handle an empty input? 12 | 13 | ## Complexity 14 | - The time complexity of this problem is O(2n) since in the worst case we could loop through the string twice. 15 | This reduces to O(n). 16 | - The space complexity of this problem is O(n), since if every character in the string is different, the hash would contain each character 17 | -------------------------------------------------------------------------------- /strings/findLastNonDuplicate/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func lastNonDuplicate(str string) (returnCh string) { 6 | intervals := make(map[byte]int) 7 | 8 | for i := 0; i < len(str); i++ { 9 | intervals[str[i]]++ 10 | } 11 | 12 | for i := len(str) - 1; i >= 0; i-- { 13 | if intervals[str[i]] == 1 { 14 | returnCh = string(str[i]) 15 | return 16 | } 17 | } 18 | 19 | return 20 | } 21 | 22 | func main() { 23 | fmt.Println(lastNonDuplicate("cacb")) 24 | fmt.Println(lastNonDuplicate("bcbc")) 25 | fmt.Println(lastNonDuplicate("GeeksforGeeks")) 26 | fmt.Println(lastNonDuplicate("GeeksQuiz")) 27 | } 28 | -------------------------------------------------------------------------------- /strings/findLastNonDuplicate/readme.md: -------------------------------------------------------------------------------- 1 | # lastNonDuplicate 2 | 3 | ## Problem Definition 4 | Given a string, find its last non-repeating character 5 | 6 | Given a string, find the non-repeating character in it. For example, if the input string is “GeeksforGeeks”, then output should be ‘r’ and if input string is “GeeksQuiz”, then output should be ‘z’. 7 | 8 | ## Questions 9 | - Is there a specific time / space complexity you want it in? 10 | - How do you want me to handle an empty input? 11 | 12 | ## Complexity 13 | - The time complexity of this problem is O(2n) since in the worst case we could loop through the string twice. 14 | This reduces to O(n). 15 | - The space complexity of this problem is O(n), since if every character in the string is different, the hash would contain each character. 16 | 17 | ## Difference from firstNonDuplicate 18 | 1. This implementation does run two for loops, but one starts from the front, and another from the back of the string array. 19 | 2. I chose not to loop through the string with range in the first loop because it uses the rune, and when looping backwards you don't have the rune so easily accessible. 20 | 3. If the rune method is used, you need to return string(rune(str[i])) to return a string from the function. This seems like too many conversions for me! 21 | -------------------------------------------------------------------------------- /strings/firstNonDuplicate/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func firstNonDuplicate(str string) (returnCh string) { 6 | intervals := make(map[rune]int) 7 | 8 | for _, c := range str { 9 | intervals[c]++ 10 | } 11 | 12 | for _, c := range str { 13 | if intervals[c] == 1 { 14 | returnCh = string(c) 15 | return 16 | } 17 | } 18 | 19 | return 20 | } 21 | 22 | func main() { 23 | fmt.Println(firstNonDuplicate("cacb")) 24 | fmt.Println(firstNonDuplicate("bcbc")) 25 | fmt.Println(firstNonDuplicate("GeeksforGeeks")) 26 | fmt.Println(firstNonDuplicate("GeeksQuiz")) 27 | } 28 | -------------------------------------------------------------------------------- /strings/firstNonDuplicate/readme.md: -------------------------------------------------------------------------------- 1 | # firstNonDuplicate 2 | 3 | ## Problem Definition 4 | Given a string, find its first non-repeating character 5 | 6 | Given a string, find the first non-repeating character in it. For example, if the input string is “GeeksforGeeks”, then output should be ‘f’ and if input string is “GeeksQuiz”, then output should be ‘G’. 7 | 8 | ## Questions 9 | - Is there a specific time / space complexity you want it in? 10 | - How do you want me to handle an empty input? 11 | 12 | ## Complexity 13 | - The time complexity of this problem is O(2n) since in the worst case we could loop through the string twice. 14 | This reduces to O(n). 15 | - The space complexity of this problem is O(n), since if every character in the string is different, the hash would contain each character. 16 | -------------------------------------------------------------------------------- /strings/palindromeIterative/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func isPalindromeIterative(text string) bool { 6 | for i, l := 0, len(text)-1; i < len(text)/2; i++ { 7 | if text[i] != text[l-i] { 8 | return false 9 | } 10 | } 11 | 12 | return true 13 | } 14 | 15 | func main() { 16 | fmt.Println(isPalindromeIterative("mom")) 17 | fmt.Println(isPalindromeIterative("dad")) 18 | fmt.Println(isPalindromeIterative("maam")) 19 | fmt.Println(isPalindromeIterative("racecar")) 20 | fmt.Println(isPalindromeIterative("amaryllis sillyrama")) 21 | } 22 | -------------------------------------------------------------------------------- /strings/palindromeIterative/readme.md: -------------------------------------------------------------------------------- 1 | # Is a String a Palindrome Recursive 2 | 3 | ## Problem Statement 4 | Determine whether or not a string is a palindrome in an iterative fasion. 5 | 6 | ## Solution 7 | We define the function to take a string and return a boolean. 8 | We loop up to the number of characters / 2 of a string and compare the index and last character - index for each value. 9 | 10 | ## Gotchas 11 | The "is a string a palindrome" question is a common interview question to see if an engineer has much programming experience. 12 | In the iterative solution of this problem, we use a for loop to iterate over the string instead of recursion. 13 | 14 | The only gotcha in this problem is that we need to make sure we set the final index to compare against to len(string) - 1 to begin in order to not hit any slicing memory issues. 15 | 16 | ## Time Complexity 17 | The time complexity of this algorithm in the worst case is O(log N). This occurs if a string is a palindrome and the algorithm runs all the way through the string. 18 | 19 | ## Optimizations 20 | Similar to the recursive solution, we could modify the algorithm to preprocess the string to filter out punctuation and convert the string to lowercase in order to compare more varied inputs. 21 | -------------------------------------------------------------------------------- /strings/palindromeRecursive/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func isPalindromeRecursive(text string) bool { 6 | if len(text) <= 1 { 7 | return true 8 | } 9 | 10 | if text[0] != text[len(text)-1] { 11 | 12 | return false 13 | } 14 | 15 | return isPalindromeRecursive(text[1 : len(text)-1]) 16 | } 17 | 18 | func main() { 19 | fmt.Println(isPalindromeRecursive("mom")) 20 | fmt.Println(isPalindromeRecursive("dad")) 21 | fmt.Println(isPalindromeRecursive("racecar")) 22 | 23 | fmt.Println(isPalindromeRecursive("amaryllis sillyrama")) 24 | } 25 | -------------------------------------------------------------------------------- /strings/palindromeRecursive/readme.md: -------------------------------------------------------------------------------- 1 | # Is a String a Palindrome Recursive 2 | 3 | ## Problem Statement 4 | Determine whether or not a string is a palindrome recursively 5 | 6 | ## Solution 7 | We define the function to take a string and return a boolean. 8 | The two base cases: 9 | 1. If the characters at the front and end of the string are not the same, return false. 10 | 2. If the length of the string is 1 or less, it is a palindrome and we should return true. 11 | 12 | The recurse case is hit if none of the previous two base cases are triggered. 13 | 14 | ## Gotchas 15 | The "is a string a palindrome" question is a common interview question to see if an engineer has much programming experience. 16 | The little twist of adding recursion on top of the algorithm also lets the interview dive a bit into the cs background of a candidate. 17 | 18 | I had originally seen this algorithm in Javascript and was tripped up by appropriate slicing of the text in the recursive solution. 19 | 20 | Given a string "racecar", you compare the character at the last index in the array (str[len(str) - 1]) with the first character in the array (str[i]). In this string they are both r respectively. 21 | The gotcha can occur when you call the isPalindrome recursively and is a Go-specific idiosyncracy. 22 | 23 | Slices of a slice include the first index and not the second. 24 | When recursing, you should take taking s[1:len(str) - 1] instead of s[1:len(str) - 2]. 25 | So for the string "racecar", the first recursion will amount to s[1:6] ("aceca") instead of s[1:5] ("acec"). 26 | 27 | ## Time Complexity 28 | The time complexity of this algorithm in the worst case is O(log N). This occurs if a string is a palindrome and the algorithm runs all the way through the string. 29 | 30 | ## Optimizations 31 | We could modify the algorithm to filter out punctuation and convert the string to lowercase in order to compare more varied inputs. 32 | -------------------------------------------------------------------------------- /strings/reverseStringRecursively/readme.md: -------------------------------------------------------------------------------- 1 | # reverseStringRecursively 2 | -------------------------------------------------------------------------------- /strings/shiftStrings/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func canBeShifted(a string, b string) bool { 9 | aArr := strings.Split(a, "") 10 | for i := 0; i < len(a); i++ { 11 | if strings.Join(aArr, "") == b { 12 | return true 13 | } 14 | aArr = append(aArr[1:], aArr[0]) 15 | } 16 | 17 | return false 18 | } 19 | 20 | func main() { 21 | fmt.Println(canBeShifted("abc", "bca")) 22 | fmt.Println(canBeShifted("abc", "dog")) 23 | } 24 | -------------------------------------------------------------------------------- /strings/shiftStrings/readme.md: -------------------------------------------------------------------------------- 1 | # Can Be Shifted 2 | 3 | ## Problem Definition 4 | Given two strings A and B, return whether or not A can be shifted some number of times to get B. 5 | 6 | For example, if A is abcde and B is cdeab, return true. If A is abc and B is acb, return false. 7 | -------------------------------------------------------------------------------- /strings/uniqueEmailAddresses/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func filterAdd(add string) string { 9 | addArr := strings.Split(add, "") 10 | for i := len(addArr) - 1; i >= 0; i-- { 11 | if addArr[i] == "." { 12 | addArr = append(addArr[:i], addArr[i+1:]...) 13 | } 14 | 15 | if addArr[i] == "+" { 16 | addArr = addArr[:i] 17 | } 18 | } 19 | 20 | return strings.Join(addArr, "") 21 | } 22 | 23 | func numUniqueEmails(emails []string) int { 24 | strHash := make(map[string]int) 25 | count := 0 26 | for i := 0; i < len(emails); i++ { 27 | emailArr := strings.Split(emails[i], "@") 28 | add := filterAdd(emailArr[0]) 29 | dom := emailArr[1] 30 | filteredEmail := add + "@" + dom 31 | if _, ok := strHash[filteredEmail]; ok == false { 32 | strHash[filteredEmail] = 1 33 | count++ 34 | } 35 | } 36 | 37 | return count 38 | } 39 | 40 | func main() { 41 | fmt.Println(numUniqueEmails([]string{"test.email+alex@leetcode.com", "test.e.mail+bob.cathy@leetcode.com", "testemail+david@lee.tcode.com"})) 42 | fmt.Println(numUniqueEmails([]string{"test@leetcode.com", "te@stleetcode.com"})) 43 | } 44 | -------------------------------------------------------------------------------- /strings/uniqueEmailAddresses/readme.md: -------------------------------------------------------------------------------- 1 | # uniqueEmailAddresses 2 | 3 | ## Problem Definition 4 | Every email consists of a local name and a domain name, separated by the @ sign. 5 | 6 | For example, in alice@leetcode.com, alice is the local name, and leetcode.com is the domain name. 7 | 8 | Besides lowercase letters, these emails may contain '.'s or '+'s. 9 | 10 | If you add periods ('.') between some characters in the local name part of an email address, mail sent there will be forwarded to the same address without dots in the local name. For example, "alice.z@leetcode.com" and "alicez@leetcode.com" forward to the same email address. (Note that this rule does not apply for domain names.) 11 | 12 | If you add a plus ('+') in the local name, everything after the first plus sign will be ignored. This allows certain emails to be filtered, for example m.y+name@email.com will be forwarded to my@email.com. (Again, this rule does not apply for domain names.) 13 | 14 | It is possible to use both of these rules at the same time. 15 | 16 | Given a list of emails, we send one email to each address in the list. How many different addresses actually receive mails? 17 | 18 | ## Complexity 19 | The Time algorithm runs in O(m * n) where m is the length of array emails and n is the length of the longest string in emails. 20 | 21 | The space complexity is n in the worst case, where n is the total amount of strings in the array of emails that would be stored in the hashmap. This happens when there are no duplicate email addresses. 22 | -------------------------------------------------------------------------------- /twoPointers/anagram-diff/anagram.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func CreateMap(str string) (strMap map[string]float64) { 9 | strMap = make(map[string]float64) 10 | for idx, _ := range str { 11 | char := string(str[idx]) 12 | if strMap[char] != 0 { 13 | strMap[char] += 1 14 | } else { 15 | strMap[char] = 1 16 | } 17 | } 18 | return 19 | } 20 | 21 | func StrDiff(str1 string, str2 string) (count float64) { 22 | hash1 := CreateMap(str1) 23 | hash2 := CreateMap(str2) 24 | 25 | for char, _ := range hash1 { 26 | if hash2[char] == 0 { 27 | count += hash1[char] 28 | } else { 29 | count += math.Max(0, hash1[char]-hash2[char]) 30 | } 31 | } 32 | 33 | for char, _ := range hash2 { 34 | if hash1[char] == 0 { 35 | count += hash1[char] 36 | } else { 37 | count += math.Max(0, hash2[char]-hash1[char]) 38 | } 39 | } 40 | 41 | return 42 | } 43 | 44 | func main() { 45 | count := StrDiff("tyler", "trever") 46 | count1 := StrDiff("", "") 47 | count2 := StrDiff("tyler", "") 48 | fmt.Println(count) 49 | fmt.Println(count1) 50 | fmt.Println(count2) 51 | } 52 | -------------------------------------------------------------------------------- /twoPointers/anagram-diff/anagram_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestCreateMap(t *testing.T) { 9 | cases := []struct { 10 | in string 11 | out map[string]float64 12 | }{ 13 | { 14 | "tyler", 15 | map[string]float64{ 16 | "t": 1, 17 | "y": 1, 18 | "l": 1, 19 | "e": 1, 20 | "r": 1, 21 | }, 22 | }, 23 | } 24 | 25 | for _, c := range cases { 26 | got := CreateMap(c.in) 27 | if reflect.DeepEqual(got, c.out) != true { 28 | t.Errorf("CreateMap(%q) == %q, not %q", c.in, got, c.out) 29 | } 30 | } 31 | } 32 | 33 | func TestStrDiff(t *testing.T) { 34 | cases := []struct { 35 | str1, str2 string 36 | diff float64 37 | }{ 38 | {"tyler", "tyl", 2}, 39 | {"", "", 0}, 40 | } 41 | 42 | for _, c := range cases { 43 | got := StrDiff(c.str1, c.str2) 44 | if got != c.diff { 45 | t.Errorf("StrDiff(%q, %q) == %q, not %q", c.str1, c.str2, got, c.diff) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /twoPointers/anagram-diff/readme.md: -------------------------------------------------------------------------------- 1 | # anagram-diff 2 | 3 | Input: Two strings 4 | Output: return a number which determined the amount of characters each string differs 5 | 6 | 7 | --------------------------------------------------------------------------------