├── go.mod ├── .travis.yml ├── .gitignore ├── common ├── tree.go ├── stack_test.go ├── queue_test.go ├── listnode_test.go ├── listnode.go ├── list_test.go ├── stack.go ├── heap_test.go ├── maxheap.go ├── minheap.go ├── queue.go └── list.go ├── Makefile ├── LICENSE ├── count.go ├── gtci ├── duplicate_test.go ├── middle_list_test.go ├── remove_duplicates_test.go ├── corrupt_pair_test.go ├── duplicates_test.go ├── pair_target_sum_test.go ├── linked_list_cycle_test.go ├── subsets_test.go ├── missing_number_test.go ├── happy_number_test.go ├── dutch_flag_test.go ├── avg_subarray_test.go ├── reverse_list_test.go ├── missing_numbers_test.go ├── conflict_appointment_test.go ├── cyclic_sort_test.go ├── max_subarray_test.go ├── no_repeat_substring_test.go ├── square_sorted_array_test.go ├── longest_substring_ones_replacement_test.go ├── spermutations_test.go ├── path_sum_test.go ├── reorder_list_test.go ├── smallest_subarray_test.go ├── intervals_intersection_test.go ├── fibonacci_numbers_test.go ├── sum_path_test.go ├── palindrome_list_test.go ├── longest_substring_k_replacement_test.go ├── median_number_stream_test.go ├── insert_interval_test.go ├── staircase_test.go ├── number_factors_test.go ├── fruits_baskets_test.go ├── string_anagrams_test.go ├── subsets_duplicates_test.go ├── longest_substring_k_distinct_test.go ├── merge_intervals_test.go ├── permutation_string_test.go ├── level_avg_test.go ├── cycle_start_test.go └── reverse_level_order_traversal_test.go ├── interviewcake ├── stolen_breakfast_drone_test.go ├── reverse_linked_list_test.go ├── delete_node_test.go ├── inplace_shuffle_test.go ├── reverse_string_test.go ├── queue_two_stacks_test.go ├── linked_list_cycle_test.go ├── fibonacci_number_test.go ├── inflight_test.go ├── permutation_palindrome_test.go ├── kth_to_last_test.go ├── largest_stack_test.go ├── bracket_validator_test.go ├── product_of_others_test.go ├── top_scores_test.go ├── apple_stocks_test.go ├── parenthesis_matching_test.go ├── highest_product_of_three_test.go ├── reverse_word_test.go ├── word_cloud_test.go ├── merge_sorted_arrays_test.go ├── merge_meetings_test.go └── recursive_string_permutation_test.go ├── lab ├── selection_sort_test.go ├── insertion_sort_test.go ├── bubble_sort_test.go ├── counting_sort_test.go ├── merge_sort_test.go ├── quicksort_test.go ├── heapsort_test.go └── balanced_binary_tree_test.go ├── leetcode ├── climbing_stairs_test.go ├── two_sum_i_test.go ├── strstr_test.go ├── min_stack_test.go ├── valid_palindrome_test.go ├── two_sum_ii_test.go ├── valid_parentheses_test.go ├── reverse_integer_test.go ├── plus_one_test.go ├── reverse_words_string_test.go ├── swap_nodes_in_pairs_test.go ├── longest_substring_test.go ├── merge_sorted_linked_list_test.go ├── single_number_ii_test.go ├── palindrome_number_test.go ├── integer_palindrome_test.go ├── group_anagrams_test.go ├── max_depth_binary_tree_test.go ├── valid_bst_test.go ├── min_depth_binary_tree_test.go └── balanced_binary_tree_test.go └── ctci └── bit_insertion_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hoanhan101/algo 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | before_script: 4 | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.23.1 5 | 6 | script: 7 | - make lint 8 | - make test 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac caches 2 | .DS_Store 3 | 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | -------------------------------------------------------------------------------- /common/tree.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // TreeNode represents a node in a binary tree. 4 | type TreeNode struct { 5 | Left *TreeNode 6 | Value int 7 | Right *TreeNode 8 | } 9 | 10 | // NewTreeNode returns a new TreeNode. 11 | func NewTreeNode(v int) *TreeNode { 12 | return &TreeNode{nil, v, nil} 13 | } 14 | -------------------------------------------------------------------------------- /common/stack_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStack(t *testing.T) { 8 | s := NewStack() 9 | 10 | Equal(t, 0, s.Size()) 11 | 12 | s.Push(1) 13 | s.Push(2) 14 | s.Push(3) 15 | 16 | Equal(t, 3, s.Size()) 17 | 18 | Equal(t, 3, s.Top()) 19 | Equal(t, 3, s.Pop()) 20 | Equal(t, 2, s.Pop()) 21 | Equal(t, 1, s.Pop()) 22 | 23 | Equal(t, 0, s.Size()) 24 | } 25 | -------------------------------------------------------------------------------- /common/queue_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestQueue(t *testing.T) { 8 | q := NewQueue() 9 | 10 | Equal(t, 0, q.Size()) 11 | 12 | q.Push(1) 13 | q.Push(2) 14 | q.Push(3) 15 | 16 | Equal(t, 3, q.Size()) 17 | 18 | Equal(t, 1, q.Front()) 19 | Equal(t, 3, q.Back()) 20 | 21 | Equal(t, 1, q.Pop()) 22 | Equal(t, 2, q.Pop()) 23 | Equal(t, 3, q.Pop()) 24 | 25 | Equal(t, 0, q.Size()) 26 | } 27 | -------------------------------------------------------------------------------- /common/listnode_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestListNodeAddNext(t *testing.T) { 8 | l11 := NewListNode(1) 9 | l11.AddNext(2) 10 | 11 | l12 := NewListNode(1) 12 | l12.Next = NewListNode(2) 13 | Equal(t, l12, l11) 14 | } 15 | 16 | func TestLinkedListToSlice(t *testing.T) { 17 | t1 := &ListNode{1, nil} 18 | 19 | t2 := &ListNode{1, nil} 20 | t2.Next = &ListNode{2, nil} 21 | 22 | t3 := &ListNode{1, nil} 23 | t3.Next = &ListNode{2, nil} 24 | t3.Next.Next = &ListNode{3, nil} 25 | 26 | Equal(t, []int{1}, LinkedListToSlice(t1)) 27 | Equal(t, []int{1, 2}, LinkedListToSlice(t2)) 28 | Equal(t, []int{1, 2, 3}, LinkedListToSlice(t3)) 29 | } 30 | -------------------------------------------------------------------------------- /common/listnode.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // ListNode represents a node in a linked list. 4 | type ListNode struct { 5 | Value int 6 | Next *ListNode 7 | } 8 | 9 | // NewListNode returns a new list node. 10 | func NewListNode(v int) *ListNode { 11 | return &ListNode{v, nil} 12 | } 13 | 14 | // AddNext adds a next node to the end of list. 15 | func (l *ListNode) AddNext(v int) { 16 | for n := l; n != nil; n = n.Next { 17 | if n.Next == nil { 18 | new := NewListNode(v) 19 | n.Next = new 20 | break 21 | } 22 | } 23 | } 24 | 25 | // LinkedListToSlice converts a linked list into an array of integer. 26 | func LinkedListToSlice(node *ListNode) []int { 27 | out := []int{} 28 | 29 | for n := node; n != nil; n = n.Next { 30 | out = append(out, n.Value) 31 | } 32 | 33 | return out 34 | } 35 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # algo cmd helpers 3 | # 4 | 5 | .PHONY: count 6 | count: ## Count the number of questions 7 | go run count.go 8 | 9 | .PHONY: clean 10 | clean: ## Clean test cache 11 | go clean -testcache 12 | rm -f coverage.out 13 | 14 | .PHONY: cover 15 | cover: test ## Run unit tests and open the coverage report 16 | go tool cover -html=coverage.out 17 | 18 | .PHONY: lint 19 | lint: ## Lint source files 20 | golangci-lint run 21 | 22 | .PHONY: test 23 | test: ## Run unit tests 24 | go test -short -race -coverprofile=coverage.out -covermode=atomic ./... 25 | 26 | .PHONY: test-verbose 27 | test-verbose: ## Run tests verbosely 28 | go test -v ./... 29 | 30 | .PHONY: ready 31 | ready: clean lint test count ## Clean up, lint source files, run tests and be ready for a push 32 | 33 | .PHONY: help 34 | help: ## Print usage information 35 | @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-16s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort 36 | 37 | .DEFAULT_GOAL := help 38 | -------------------------------------------------------------------------------- /common/list_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestList(t *testing.T) { 8 | l := NewList() 9 | 10 | Equal(t, 0, l.Len()) 11 | Equal(t, []interface{}{}, l.Slice()) 12 | 13 | // note that we don't use type assertion here because Equal uses DeepEqual 14 | // to compare these 2 values. 15 | Equal(t, 6, l.PushFront(6)) 16 | Equal(t, 1, l.Len()) 17 | Equal(t, []interface{}{6}, l.Slice()) 18 | 19 | Equal(t, 1, l.PushFront(1)) 20 | Equal(t, 2, l.Len()) 21 | Equal(t, []interface{}{1, 6}, l.Slice()) 22 | 23 | Equal(t, 16, l.PushBack(16)) 24 | Equal(t, 3, l.Len()) 25 | Equal(t, []interface{}{1, 6, 16}, l.Slice()) 26 | 27 | Equal(t, 66, l.PushBack(66)) 28 | Equal(t, 4, l.Len()) 29 | Equal(t, []interface{}{1, 6, 16, 66}, l.Slice()) 30 | 31 | Equal(t, 1, l.Front()) 32 | Equal(t, 66, l.Back()) 33 | 34 | Equal(t, 1, l.RemoveFront()) 35 | Equal(t, 3, l.Len()) 36 | Equal(t, []interface{}{6, 16, 66}, l.Slice()) 37 | 38 | Equal(t, 66, l.RemoveBack()) 39 | Equal(t, 2, l.Len()) 40 | Equal(t, []interface{}{6, 16}, l.Slice()) 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Hoanh An 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /common/stack.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | ) 7 | 8 | // Stack implements a stack data structure. 9 | type Stack struct { 10 | elements *list.List 11 | } 12 | 13 | // NewStack initializes and returns a new stack. 14 | func NewStack() *Stack { 15 | l := list.New() 16 | return &Stack{elements: l} 17 | } 18 | 19 | // Empty checks if the stack is empty. 20 | func (q *Stack) Empty() bool { 21 | return q.Size() == 0 22 | } 23 | 24 | // Size returns the total number of elements in the stack. 25 | func (q *Stack) Size() int { 26 | return q.elements.Len() 27 | } 28 | 29 | // Top returns the element on top of the stack. 30 | func (q *Stack) Top() interface{} { 31 | return q.elements.Back().Value 32 | } 33 | 34 | // Push adds the element on top of the stack. 35 | func (q *Stack) Push(v interface{}) { 36 | _ = q.elements.PushBack(v) 37 | } 38 | 39 | // Pop removes and returns the element on top of the stack. 40 | func (q *Stack) Pop() interface{} { 41 | return q.elements.Remove(q.elements.Back()) 42 | } 43 | 44 | // Print prints all elements in the stack. 45 | func (q *Stack) Print() { 46 | for e := q.elements.Front(); e != nil; e = e.Next() { 47 | fmt.Println(e.Value) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /count.go: -------------------------------------------------------------------------------- 1 | /* 2 | count.go counts the number of files in given directories. 3 | Since one file contains one problem, the program's output can also 4 | tell how many questions that we have completed so far. 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "path/filepath" 13 | "text/tabwriter" 14 | ) 15 | 16 | func main() { 17 | // format in tab-separated columns with a tab stop of 8. 18 | w := new(tabwriter.Writer) 19 | w.Init(os.Stdout, 0, 8, 0, '\t', 0) 20 | 21 | total := 0 22 | directories := []string{"ctci", "gtci", "interviewcake", "leetcode", "lab"} 23 | 24 | for _, dir := range directories { 25 | n := countFiles(dir) 26 | fmt.Fprintf(w, "%v\t%v\n", dir, n) 27 | 28 | total += n 29 | } 30 | 31 | fmt.Fprintf(w, "-------------\t--\n") 32 | fmt.Fprintf(w, "total\t%v\n", total) 33 | w.Flush() 34 | } 35 | 36 | func countFiles(dir string) int { 37 | counter := 0 38 | 39 | err := filepath.Walk(dir, 40 | func(path string, info os.FileInfo, err error) error { 41 | if err != nil { 42 | return err 43 | } 44 | if dir != path { 45 | counter++ 46 | } 47 | return nil 48 | }) 49 | 50 | if err != nil { 51 | fmt.Println(err) 52 | } 53 | 54 | return counter 55 | } 56 | -------------------------------------------------------------------------------- /common/heap_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestMinHeap(t *testing.T) { 8 | h := NewMinHeap() 9 | 10 | Equal(t, 0, h.Len()) 11 | 12 | h.Push(46) 13 | h.Push(86) 14 | h.Push(6) 15 | h.Push(66) 16 | h.Push(96) 17 | h.Push(16) 18 | 19 | Equal(t, 6, h.Len()) 20 | 21 | Equal(t, 6, h.Peek()) 22 | Equal(t, 6, h.Pop()) 23 | Equal(t, 16, h.Peek()) 24 | Equal(t, 16, h.Pop()) 25 | Equal(t, 46, h.Peek()) 26 | Equal(t, 46, h.Pop()) 27 | Equal(t, 66, h.Peek()) 28 | Equal(t, 66, h.Pop()) 29 | Equal(t, 86, h.Peek()) 30 | Equal(t, 86, h.Pop()) 31 | Equal(t, 96, h.Peek()) 32 | Equal(t, 96, h.Pop()) 33 | 34 | Equal(t, 0, h.Len()) 35 | } 36 | 37 | func TestMaxHeap(t *testing.T) { 38 | h := NewMaxHeap() 39 | 40 | Equal(t, 0, h.Len()) 41 | 42 | h.Push(46) 43 | h.Push(86) 44 | h.Push(6) 45 | h.Push(66) 46 | h.Push(96) 47 | h.Push(16) 48 | 49 | Equal(t, 6, h.Len()) 50 | 51 | Equal(t, 96, h.Peek()) 52 | Equal(t, 96, h.Pop()) 53 | Equal(t, 86, h.Peek()) 54 | Equal(t, 86, h.Pop()) 55 | Equal(t, 66, h.Peek()) 56 | Equal(t, 66, h.Pop()) 57 | Equal(t, 46, h.Peek()) 58 | Equal(t, 46, h.Pop()) 59 | Equal(t, 16, h.Peek()) 60 | Equal(t, 16, h.Pop()) 61 | Equal(t, 6, h.Peek()) 62 | Equal(t, 6, h.Pop()) 63 | 64 | Equal(t, 0, h.Len()) 65 | } 66 | -------------------------------------------------------------------------------- /common/maxheap.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "container/heap" 5 | ) 6 | 7 | // MaxHeap represents a max heap data structure. 8 | type MaxHeap struct { 9 | heap *maxHeap 10 | } 11 | 12 | // NewMaxHeap returns a new max heap. 13 | func NewMaxHeap() *MaxHeap { 14 | h := &maxHeap{} 15 | heap.Init(h) 16 | return &MaxHeap{heap: h} 17 | } 18 | 19 | // Push adds a new node to the heap. 20 | func (h *MaxHeap) Push(x int) { 21 | heap.Push(h.heap, x) 22 | } 23 | 24 | // Pop returns the root node after removing it from the heap. 25 | func (h *MaxHeap) Pop() int { 26 | return heap.Pop(h.heap).(int) 27 | } 28 | 29 | // Len returns the number of items in the heap. 30 | func (h *MaxHeap) Len() int { 31 | return h.heap.Len() 32 | } 33 | 34 | // Peek returns the maximum item in the heap. 35 | func (h *MaxHeap) Peek() int { 36 | return (*h.heap)[0] 37 | } 38 | 39 | type maxHeap []int 40 | 41 | func (h maxHeap) Len() int { return len(h) } 42 | func (h maxHeap) Less(i, j int) bool { return h[i] > h[j] } 43 | func (h maxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 44 | 45 | func (h *maxHeap) Push(x interface{}) { 46 | *h = append(*h, x.(int)) 47 | } 48 | 49 | func (h *maxHeap) Pop() interface{} { 50 | old := *h 51 | n := len(old) 52 | x := old[n-1] 53 | *h = old[0 : n-1] 54 | return x 55 | } 56 | -------------------------------------------------------------------------------- /common/minheap.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "container/heap" 5 | ) 6 | 7 | // MinHeap represents a min heap data structure. 8 | type MinHeap struct { 9 | heap *minHeap 10 | } 11 | 12 | // NewMinHeap returns a new min heap. 13 | func NewMinHeap() *MinHeap { 14 | h := &minHeap{} 15 | heap.Init(h) 16 | return &MinHeap{heap: h} 17 | } 18 | 19 | // Push adds a new node to the heap. 20 | func (h *MinHeap) Push(x int) { 21 | heap.Push(h.heap, x) 22 | } 23 | 24 | // Pop returns the root node after removing it from the heap. 25 | func (h *MinHeap) Pop() int { 26 | return heap.Pop(h.heap).(int) 27 | } 28 | 29 | // Len returns the number of items in the heap. 30 | func (h *MinHeap) Len() int { 31 | return h.heap.Len() 32 | } 33 | 34 | // Peek returns the minimum item in the heap. 35 | func (h *MinHeap) Peek() int { 36 | return (*h.heap)[0] 37 | } 38 | 39 | type minHeap []int 40 | 41 | func (h minHeap) Len() int { return len(h) } 42 | func (h minHeap) Less(i, j int) bool { return h[i] < h[j] } 43 | func (h minHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 44 | 45 | func (h *minHeap) Push(x interface{}) { 46 | *h = append(*h, x.(int)) 47 | } 48 | 49 | func (h *minHeap) Pop() interface{} { 50 | old := *h 51 | n := len(old) 52 | x := old[n-1] 53 | *h = old[0 : n-1] 54 | return x 55 | } 56 | -------------------------------------------------------------------------------- /common/queue.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | ) 7 | 8 | // Queue implements a queue data structure. 9 | type Queue struct { 10 | elements *list.List 11 | } 12 | 13 | // NewQueue initializes and returns a new queue. 14 | func NewQueue() *Queue { 15 | l := list.New() 16 | return &Queue{elements: l} 17 | } 18 | 19 | // Empty checks if the queue is empty. 20 | func (q *Queue) Empty() bool { 21 | return q.Size() == 0 22 | } 23 | 24 | // Size returns the total number of elements in the queue. 25 | func (q *Queue) Size() int { 26 | return q.elements.Len() 27 | } 28 | 29 | // Front returns the element in the front of the queue. 30 | func (q *Queue) Front() interface{} { 31 | return q.elements.Front().Value 32 | } 33 | 34 | // Back returns the element in the back of the queue. 35 | func (q *Queue) Back() interface{} { 36 | return q.elements.Back().Value 37 | } 38 | 39 | // Push adds a element in the back of the queue. 40 | func (q *Queue) Push(v interface{}) { 41 | _ = q.elements.PushBack(v) 42 | } 43 | 44 | // Pop removes and returns the element in the front of the queue. 45 | func (q *Queue) Pop() interface{} { 46 | return q.elements.Remove(q.elements.Front()) 47 | } 48 | 49 | // Print prints all the elements in the queue. 50 | func (q *Queue) Print() { 51 | for e := q.elements.Front(); e != nil; e = e.Next() { 52 | fmt.Println(e.Value) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /gtci/duplicate_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing n+1 numbers taken from the range 1 to n. It has 4 | only one duplicate number but can be repeated over time. Find that one. 5 | 6 | Example: 7 | - Input: []int{1, 4, 4, 3, 2} 8 | Output: 4 9 | 10 | Approach: 11 | - Similar to missing number problem, can place each number on its correct 12 | index. 13 | - If while swapping the number with its index both the numbers being swapped 14 | are same, we have found the duplicate. 15 | 16 | Cost: 17 | - O(n) time, O(1) space. 18 | */ 19 | 20 | package gtci 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestFindDuplicate(t *testing.T) { 29 | tests := []struct { 30 | in []int 31 | expected int 32 | }{ 33 | {[]int{}, 0}, 34 | {[]int{1, 4, 4, 3, 2}, 4}, 35 | {[]int{2, 4, 1, 4, 4}, 4}, 36 | {[]int{2, 1, 3, 3, 5, 4}, 3}, 37 | } 38 | 39 | for _, tt := range tests { 40 | common.Equal( 41 | t, 42 | tt.expected, 43 | findDuplicate(tt.in), 44 | ) 45 | } 46 | } 47 | 48 | func findDuplicate(nums []int) int { 49 | i := 0 50 | 51 | for i < len(nums) { 52 | if nums[i] != i+1 { 53 | correctIndex := nums[i] - 1 54 | if nums[i] != nums[correctIndex] { 55 | common.Swap(nums, i, correctIndex) 56 | } else { 57 | return nums[i] 58 | } 59 | } else { 60 | i++ 61 | } 62 | } 63 | 64 | return 0 65 | } 66 | -------------------------------------------------------------------------------- /interviewcake/stolen_breakfast_drone_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of integer where every element appears even number of time 4 | except for one, find that unique one in O(1) space. 5 | 6 | Example: 7 | - Input: []int{1, 1, 2, 4, 2, 1, 4, 6, 1} 8 | Output: 6, because 6 appears 1 time only. 9 | 10 | Approach: 11 | - Bitwise XOR all integers in the list. 12 | - Since XOR-ing a number with itself is zero, we could cancel out all the even 13 | duplicates. 14 | - That leaves us the one that appears one time only. 15 | 16 | Cost: 17 | - O(n) time, O(1) space. 18 | */ 19 | 20 | package interviewcake 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestFindUniqueID(t *testing.T) { 29 | tests := []struct { 30 | in []int 31 | expected int 32 | }{ 33 | {[]int{}, 0}, 34 | {[]int{6}, 6}, 35 | {[]int{6, 2, 2}, 6}, 36 | {[]int{6, 2, 2, 4, 4, 1, 1, 1, 1}, 6}, 37 | {[]int{1, 1, 2, 4, 2, 1, 4, 6, 1}, 6}, 38 | {[]int{4, 1, 2, 1, 6, 1, 4, 2, 1}, 6}, 39 | } 40 | 41 | for _, tt := range tests { 42 | result := findUniqueID(tt.in) 43 | common.Equal(t, tt.expected, result) 44 | } 45 | } 46 | 47 | func findUniqueID(list []int) int { 48 | // ones represents the number that appears only 1 time. 49 | ones := 0 50 | 51 | // XOR all integers together. 52 | for _, id := range list { 53 | ones ^= id 54 | } 55 | 56 | return ones 57 | } 58 | -------------------------------------------------------------------------------- /gtci/middle_list_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given the head of a singly linked list, write a function to return the 4 | middle value. 5 | 6 | Approach: 7 | - Have a slow pointer move one step at a time while the fast one move 8 | 2 steps at a time. 9 | - Once the fast one reaches the end, the slow is in the middle. 10 | 11 | Cost: 12 | - O(n) time, O(1) space. 13 | */ 14 | 15 | package gtci 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/hoanhan101/algo/common" 21 | ) 22 | 23 | func TestFindMid(t *testing.T) { 24 | // define tests input. 25 | t1 := common.NewListNode(1) 26 | t1.AddNext(2) 27 | t1.AddNext(3) 28 | t1.AddNext(4) 29 | t1.AddNext(5) 30 | t1.AddNext(6) 31 | 32 | t2 := common.NewListNode(1) 33 | t2.AddNext(2) 34 | t2.AddNext(3) 35 | t2.AddNext(4) 36 | t2.AddNext(5) 37 | 38 | // define tests output. 39 | tests := []struct { 40 | in *common.ListNode 41 | expected int 42 | }{ 43 | {t1, 4}, 44 | {t2, 3}, 45 | } 46 | 47 | for _, tt := range tests { 48 | common.Equal( 49 | t, 50 | tt.expected, 51 | findMid(tt.in), 52 | ) 53 | } 54 | } 55 | 56 | func findMid(node *common.ListNode) int { 57 | // keep two pointers at the beginning. 58 | slow := node 59 | fast := node 60 | 61 | // traverse until it hit the end of the list. 62 | for fast != nil && fast.Next != nil { 63 | slow = slow.Next 64 | fast = fast.Next.Next 65 | } 66 | 67 | return slow.Value 68 | } 69 | -------------------------------------------------------------------------------- /gtci/remove_duplicates_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of sorted numbers and a target sum, find a pair in the 4 | array whose sum is equal to the given target. 5 | 6 | Example: 7 | - Input: []int{1, 2, 6, 8, 16, 26}, target=14 8 | Output: []int{2, 3} 9 | Explanation: 6 (index 2) + 8 (index 3) = 14 10 | 11 | Approach: 12 | - Have one pointer iterate the array and one placing non-duplicate number. 13 | 14 | Cost: 15 | - O(n) time, O(1) space. 16 | */ 17 | 18 | package gtci 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/hoanhan101/algo/common" 24 | ) 25 | 26 | func TestRemoveDuplicates(t *testing.T) { 27 | tests := []struct { 28 | in []int 29 | expected int 30 | }{ 31 | {[]int{}, 0}, 32 | {[]int{1}, 1}, 33 | {[]int{1, 2}, 2}, 34 | {[]int{1, 2, 2}, 2}, 35 | {[]int{1, 2, 2, 3, 3, 3, 4, 4, 4, 4}, 4}, 36 | } 37 | 38 | for _, tt := range tests { 39 | common.Equal( 40 | t, 41 | tt.expected, 42 | removeDuplicates(tt.in), 43 | ) 44 | } 45 | } 46 | 47 | func removeDuplicates(a []int) int { 48 | if len(a) == 0 { 49 | return 0 50 | } 51 | 52 | nonDuplicate := 1 53 | 54 | i := 1 55 | for i < len(a) { 56 | // if we see a non-duplicate number, move it next to the last 57 | // non-duplicate one that we've seen. 58 | if a[nonDuplicate-1] != a[i] { 59 | a[nonDuplicate] = a[i] 60 | nonDuplicate++ 61 | } 62 | i++ 63 | } 64 | 65 | return nonDuplicate 66 | } 67 | -------------------------------------------------------------------------------- /lab/selection_sort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement selection sort. 4 | 5 | Approach: 6 | - Repeatedly select the next smallest element from the unsorted array and 7 | move it to the front. 8 | 9 | Cost: 10 | - O(n^2) time and O(1) space. 11 | */ 12 | 13 | package lab 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestSelectionSort(t *testing.T) { 22 | tests := []struct { 23 | in []int 24 | expected []int 25 | }{ 26 | {[]int{}, []int{}}, 27 | {[]int{1}, []int{1}}, 28 | {[]int{1, 2}, []int{1, 2}}, 29 | {[]int{2, 1}, []int{1, 2}}, 30 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 31 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 32 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 33 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 34 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | selectionSort(tt.in) 40 | common.Equal(t, tt.expected, tt.in) 41 | } 42 | } 43 | 44 | func selectionSort(in []int) { 45 | minIndex := 0 46 | for i := 0; i < len(in)-1; i++ { 47 | minIndex = i 48 | // find the minimum in the rest of the array. 49 | for j := i + 1; j < len(in); j++ { 50 | if in[j] < in[minIndex] { 51 | minIndex = j 52 | } 53 | } 54 | 55 | // swap the minimum value with the first value. 56 | common.Swap(in, i, minIndex) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lab/insertion_sort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement insertion sort. 4 | 5 | Approach: 6 | - Insert elements from an unsorted array into a sorted subsection of the 7 | array, one item at a time. 8 | 9 | Cost: 10 | - O(n^2) time and O(1) space. 11 | */ 12 | 13 | package lab 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestInsertionSort(t *testing.T) { 22 | tests := []struct { 23 | in []int 24 | expected []int 25 | }{ 26 | {[]int{}, []int{}}, 27 | {[]int{1}, []int{1}}, 28 | {[]int{1, 2}, []int{1, 2}}, 29 | {[]int{2, 1}, []int{1, 2}}, 30 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 31 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 32 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 33 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 34 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | insertionSort(tt.in) 40 | common.Equal(t, tt.expected, tt.in) 41 | } 42 | } 43 | 44 | func insertionSort(in []int) { 45 | // iterate through the list from position 1. 46 | for i := 1; i < len(in); i++ { 47 | // shift each one to the left by swapping it with the one before until 48 | // it's in the right spot. 49 | current := in[i] 50 | j := i - 1 51 | 52 | for j >= 0 && current < in[j] { 53 | in[j+1] = in[j] 54 | j-- 55 | } 56 | 57 | in[j+1] = current 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /gtci/corrupt_pair_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing n+1 numbers taken from the range 1 to n. One 4 | of the numbers got duplicated which also resulted in one number going 5 | missing. Find these numbers. 6 | 7 | Example: 8 | - Input: []int{3, 1, 2, 5, 2} 9 | Output: []int{2, 4} 10 | 11 | Approach: 12 | - Similar to finding duplicates problem, can place each number on its correct 13 | index. 14 | - The one is not at its correct index is the duplicate and its index itself 15 | is the missing number. 16 | 17 | Cost: 18 | - O(n) time, O(1) space. 19 | */ 20 | 21 | package gtci 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/hoanhan101/algo/common" 27 | ) 28 | 29 | func TestFindCorruptPair(t *testing.T) { 30 | tests := []struct { 31 | in []int 32 | expected []int 33 | }{ 34 | {[]int{}, []int{0, 0}}, 35 | {[]int{3, 1, 2, 5, 2}, []int{2, 4}}, 36 | {[]int{3, 1, 2, 3, 6, 4}, []int{3, 5}}, 37 | } 38 | 39 | for _, tt := range tests { 40 | common.Equal( 41 | t, 42 | tt.expected, 43 | findCorruptPair(tt.in), 44 | ) 45 | } 46 | } 47 | 48 | func findCorruptPair(nums []int) []int { 49 | i := 0 50 | 51 | for i < len(nums) { 52 | correctIndex := nums[i] - 1 53 | if nums[i] != nums[correctIndex] { 54 | common.Swap(nums, i, correctIndex) 55 | } else { 56 | i++ 57 | } 58 | } 59 | 60 | for j := 0; j < len(nums); j++ { 61 | if nums[j] != j+1 { 62 | return []int{nums[j], j + 1} 63 | } 64 | } 65 | 66 | return []int{0, 0} 67 | } 68 | -------------------------------------------------------------------------------- /gtci/duplicates_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing n numbers taken from the range 1 to n. It can 4 | have some duplicates. Find all those numbers. 5 | 6 | Example: 7 | - Input: []int{5, 4, 7, 2, 3, 5, 3} 8 | Output: []int{3, 5} 9 | 10 | Approach: 11 | - Similar to missing number problem, can rearrange the array using cyclic 12 | sort. 13 | - Those that do not have the correct indices are the duplicate ones. 14 | 15 | Cost: 16 | - O(n) time, O(1) space. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestFindDuplicates(t *testing.T) { 28 | tests := []struct { 29 | in []int 30 | expected []int 31 | }{ 32 | {[]int{}, []int{}}, 33 | {[]int{3, 4, 4, 5, 5}, []int{5, 4}}, 34 | {[]int{5, 4, 7, 2, 3, 5, 3}, []int{3, 5}}, 35 | } 36 | 37 | for _, tt := range tests { 38 | common.Equal( 39 | t, 40 | tt.expected, 41 | findDuplicates(tt.in), 42 | ) 43 | } 44 | } 45 | 46 | func findDuplicates(nums []int) []int { 47 | duplicates := []int{} 48 | i := 0 49 | 50 | for i < len(nums) { 51 | correctIndex := nums[i] - 1 52 | if nums[i] != nums[correctIndex] { 53 | common.Swap(nums, i, correctIndex) 54 | } else { 55 | i++ 56 | } 57 | } 58 | 59 | // return the duplicates that do not have the correct index. 60 | for j := 0; j < len(nums); j++ { 61 | if nums[j] != j+1 { 62 | duplicates = append(duplicates, nums[j]) 63 | } 64 | } 65 | 66 | return duplicates 67 | } 68 | -------------------------------------------------------------------------------- /leetcode/climbing_stairs_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - You are climbing a staircase. It takes n steps to reach to the top. 4 | - Each time you can either climb 1 or 2 steps. In how many distinct ways 5 | can you climb to the top. 6 | 7 | Example: 8 | - Input: 2 9 | Output: 2 10 | Explanation: Two ways to climb the top are 1+1 and 2. 11 | - Input: 3 12 | Output: 3 13 | Explanation: Two ways to climb the top are 1+1+1, 1+2, and 2+1. 14 | 15 | Approach: 16 | - To reach n step, one either advance one step from the n-1 step or 17 | 2 steps from the n-2 step. 18 | - This is basically the same recurrence formula defined by the Fibonacci 19 | sequence. 20 | 21 | Solution: 22 | - Initialize two variables to store 2 previous value. 23 | - Iterate from 2 to n and update these two values at each step. 24 | 25 | Cost: 26 | - O(n) time, O(1) space. 27 | */ 28 | 29 | package leetcode 30 | 31 | import ( 32 | "testing" 33 | 34 | "github.com/hoanhan101/algo/common" 35 | ) 36 | 37 | func TestClimbStairs(t *testing.T) { 38 | tests := []struct { 39 | in int 40 | expected int 41 | }{ 42 | {0, 1}, 43 | {1, 1}, 44 | {2, 2}, 45 | {3, 3}, 46 | {4, 5}, 47 | {5, 8}, 48 | {6, 13}, 49 | } 50 | 51 | for _, tt := range tests { 52 | common.Equal( 53 | t, 54 | tt.expected, 55 | climbStairs(tt.in), 56 | ) 57 | } 58 | } 59 | 60 | func climbStairs(n int) int { 61 | p, q := 1, 1 62 | 63 | for i := 2; i <= n; i++ { 64 | tmp := q 65 | q += p 66 | p = tmp 67 | } 68 | 69 | return q 70 | } 71 | -------------------------------------------------------------------------------- /gtci/pair_target_sum_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of sorted numbers and a target sum, find a pair in the 4 | array whose sum is equal to the given target. 5 | 6 | Example: 7 | - Input: []int{1, 2, 6, 8, 16, 26}, target=14 8 | Output: []int{2, 3} 9 | Explanation: 6 (index 2) + 8 (index 3) = 14 10 | 11 | Approach: 12 | - Have one pointer start at the beginning and one at the end of the array. 13 | - At each step, see if the two pointers add up to the target sum and move 14 | them toward each other accordingly. 15 | 16 | Cost: 17 | - O(n) time, O(n) space. 18 | */ 19 | 20 | package gtci 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestFindPairTargetSum(t *testing.T) { 29 | tests := []struct { 30 | in1 []int 31 | in2 int 32 | expected []int 33 | }{ 34 | {[]int{}, 6, []int{}}, 35 | {[]int{1, 2, 6, 8, 16, 26}, 14, []int{2, 3}}, 36 | {[]int{1, 2, 3, 4, 5, 6}, 6, []int{0, 4}}, 37 | } 38 | 39 | for _, tt := range tests { 40 | common.Equal( 41 | t, 42 | tt.expected, 43 | findPairTargetSum(tt.in1, tt.in2), 44 | ) 45 | } 46 | } 47 | 48 | func findPairTargetSum(a []int, target int) []int { 49 | out := []int{} 50 | 51 | start := 0 52 | end := len(a) - 1 53 | 54 | for start < end { 55 | sum := a[start] + a[end] 56 | if sum > target { 57 | end-- 58 | } else if sum < target { 59 | start++ 60 | } else { 61 | out = append(out, start, end) 62 | break 63 | } 64 | } 65 | 66 | return out 67 | } 68 | -------------------------------------------------------------------------------- /gtci/linked_list_cycle_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given the head of a singly linked list, write a function to determine 4 | if it contains a cycle. 5 | 6 | Approach: 7 | - Have a slow pointer move one step at a time while the fast one move 8 | 2 steps at a time. 9 | - If the linked list has a cycle, the fast pointer will catch the slow one. 10 | 11 | Cost: 12 | - O(n) time, O(1) space. 13 | */ 14 | 15 | package gtci 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/hoanhan101/algo/common" 21 | ) 22 | 23 | func TestHasCycle(t *testing.T) { 24 | // define tests input. 25 | t1 := common.NewListNode(1) 26 | t1.AddNext(2) 27 | t1.AddNext(3) 28 | 29 | t2 := common.NewListNode(1) 30 | t2.AddNext(2) 31 | t2.AddNext(3) 32 | t2.Next.Next.Next = t2 33 | 34 | // define tests output. 35 | tests := []struct { 36 | in *common.ListNode 37 | expected bool 38 | }{ 39 | {t1, false}, 40 | {t2, true}, 41 | } 42 | 43 | for _, tt := range tests { 44 | common.Equal( 45 | t, 46 | tt.expected, 47 | hasCycle(tt.in), 48 | ) 49 | } 50 | } 51 | 52 | func hasCycle(node *common.ListNode) bool { 53 | // keep two pointers at the beginning. 54 | slow := node 55 | fast := node 56 | 57 | // traverse until it hit the end of the list. 58 | for fast != nil && fast.Next != nil { 59 | slow = slow.Next 60 | fast = fast.Next.Next 61 | 62 | // if the fast pointer catches the slow one, there exists a cycle. 63 | if fast.Value == slow.Value { 64 | return true 65 | } 66 | } 67 | 68 | return false 69 | } 70 | -------------------------------------------------------------------------------- /lab/bubble_sort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement bubble sort. 4 | 5 | Approach: 6 | - Repeatedly swap the adjacent elements if they are in the wrong order in the 7 | array, one item at a time. 8 | 9 | Cost: 10 | - O(n^2) time and O(1) space. 11 | */ 12 | 13 | package lab 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestBubbleSort(t *testing.T) { 22 | tests := []struct { 23 | in []int 24 | expected []int 25 | }{ 26 | {[]int{}, []int{}}, 27 | {[]int{1}, []int{1}}, 28 | {[]int{1, 2}, []int{1, 2}}, 29 | {[]int{2, 1}, []int{1, 2}}, 30 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 31 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 32 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 33 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 34 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | bubbleSort(tt.in) 40 | common.Equal(t, tt.expected, tt.in) 41 | } 42 | } 43 | 44 | func bubbleSort(in []int) { 45 | length := len(in) 46 | 47 | // for each element in the list, check it with almost every other element. 48 | for i := 0; i < length; i++ { 49 | // since the last i element is already in place, only iterate through 50 | // the item before the last one. 51 | for j := 0; j < length-i-1; j++ { 52 | // swap the adjacent elements if they are not in ascending order. 53 | if in[j] > in[j+1] { 54 | common.Swap(in, j, j+1) 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /gtci/subsets_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a set with distinct elements, find all the distinct subsets. 4 | 5 | Example: 6 | - Input: [1 2] 7 | Output: [[] [1] [2] [1 2]] 8 | - Input: [1 2 5] 9 | Output: [[] [1] [2] [1 2] [3] [1 3] [2 3] [1 2 3]] 10 | 11 | Approach: 12 | - Start with an empty set. 13 | - Iterate through the set one by one and add them to existing sets to create new ones. 14 | 15 | Cost: 16 | - O(2^n) time, O(2^n) space since we would have a total of 2^n subsets. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestFindSubsets(t *testing.T) { 28 | tests := []struct { 29 | in []int 30 | expected [][]int 31 | }{ 32 | {[]int{}, [][]int{{}}}, 33 | {[]int{1}, [][]int{{}, {1}}}, 34 | {[]int{1, 2}, [][]int{{}, {1}, {2}, {1, 2}}}, 35 | {[]int{1, 2, 3}, [][]int{{}, {1}, {2}, {1, 2}, {3}, {1, 3}, {2, 3}, {1, 2, 3}}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | common.Equal( 40 | t, 41 | tt.expected, 42 | findSubsets(tt.in), 43 | ) 44 | } 45 | } 46 | 47 | func findSubsets(nums []int) [][]int { 48 | subsets := [][]int{} 49 | 50 | // start with an empty set. 51 | subsets = append(subsets, []int{}) 52 | 53 | // for each number in the set, add it to all existing sets. 54 | for _, num := range nums { 55 | n := len(subsets) 56 | for i := 0; i < n; i++ { 57 | set := subsets[i] 58 | set = append(set, num) 59 | subsets = append(subsets, set) 60 | } 61 | } 62 | 63 | return subsets 64 | } 65 | -------------------------------------------------------------------------------- /interviewcake/reverse_linked_list_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Reverse a linked list in-place. 4 | 5 | Approach: 6 | - Iterate through the list and point each node's next pointer to the previous 7 | item. 8 | 9 | Cost: 10 | - O(n) time and O(1) space. 11 | */ 12 | 13 | package interviewcake 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestReverseLinkedList(t *testing.T) { 22 | // define tests input. 23 | t0 := common.NewListNode(0) 24 | 25 | t1 := common.NewListNode(1) 26 | 27 | t2 := common.NewListNode(1) 28 | t2.AddNext(2) 29 | 30 | t3 := common.NewListNode(1) 31 | t3.AddNext(2) 32 | t3.AddNext(3) 33 | 34 | // define tests output. 35 | tests := []struct { 36 | in *common.ListNode 37 | expected []int 38 | }{ 39 | {t0, []int{0}}, 40 | {t1, []int{1}}, 41 | {t2, []int{2, 1}}, 42 | {t3, []int{3, 2, 1}}, 43 | } 44 | 45 | for _, tt := range tests { 46 | node := reverseLinkedList(tt.in) 47 | common.Equal(t, tt.expected, common.LinkedListToSlice(node)) 48 | } 49 | } 50 | 51 | func reverseLinkedList(node *common.ListNode) *common.ListNode { 52 | current := node 53 | var previous, next *common.ListNode 54 | 55 | for current != nil { 56 | // copy a pointer to the next element. 57 | next = current.Next 58 | 59 | // reverse the next pointer. 60 | current.Next = previous 61 | 62 | // step forward in the list. 63 | previous = current 64 | current = next 65 | } 66 | 67 | // return the head of new linked list. 68 | return previous 69 | } 70 | -------------------------------------------------------------------------------- /gtci/missing_number_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing n numbers taken from the range 1 to n. It can 4 | have duplicates. Find all those missing numbers. 5 | 6 | Example: 7 | - Input: []int{2, 3, 1, 8, 2, 3, 5, 1} 8 | Output: []int{4, 6, 7} 9 | 10 | Approach: 11 | - Similar to missing number problem, can rearrange the array using cyclic 12 | sort. 13 | - Those that do not have the correct indices are the missing ones. 14 | 15 | Cost: 16 | - O(n) time, O(1) space. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestMissingNumbers(t *testing.T) { 28 | tests := []struct { 29 | in []int 30 | expected []int 31 | }{ 32 | {[]int{}, []int{}}, 33 | {[]int{2, 4, 1, 2}, []int{3}}, 34 | {[]int{2, 3, 2, 1}, []int{4}}, 35 | {[]int{2, 3, 1, 8, 2, 3, 5, 1}, []int{4, 6, 7}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | common.Equal( 40 | t, 41 | tt.expected, 42 | missingNumbers(tt.in), 43 | ) 44 | } 45 | } 46 | 47 | func missingNumbers(nums []int) []int { 48 | missed := []int{} 49 | i := 0 50 | 51 | for i < len(nums) { 52 | correctIndex := nums[i] - 1 53 | if nums[i] != nums[correctIndex] { 54 | common.Swap(nums, i, correctIndex) 55 | } else { 56 | i++ 57 | } 58 | } 59 | 60 | // return the missing number that does not have the correct index. 61 | for j := 0; j < len(nums); j++ { 62 | if nums[j] != j+1 { 63 | missed = append(missed, j+1) 64 | } 65 | } 66 | 67 | return missed 68 | } 69 | -------------------------------------------------------------------------------- /interviewcake/delete_node_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Delete a node from a singly-linked list, given only a pointer to that node. 4 | 5 | Approach: 6 | - Since we don't have access to the previous node, simply copy the value and 7 | pointer of the next node and copy them into the current node. 8 | 9 | Solution: 10 | - Cache the next node. 11 | - If the next node is nil, it's the last node. Just simply return. 12 | - Copy the current node's value to the next node's value 13 | - Copy the node's pointer to the next node's pointer. 14 | 15 | Cost: 16 | - O(1) time and O(1) space. 17 | */ 18 | 19 | package interviewcake 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestDeleteNode(t *testing.T) { 28 | // define test input. 29 | t1 := common.NewListNode(1) 30 | for i := 2; i <= 6; i++ { 31 | t1.AddNext(i) 32 | } 33 | 34 | // deletes node 4. 35 | deleteNode(t1.Next.Next.Next) 36 | common.Equal(t, []int{1, 2, 3, 5, 6}, common.LinkedListToSlice(t1)) 37 | 38 | // deletes node 3. 39 | deleteNode(t1.Next.Next) 40 | common.Equal(t, []int{1, 2, 5, 6}, common.LinkedListToSlice(t1)) 41 | 42 | // deletes node 5. 43 | deleteNode(t1.Next.Next) 44 | common.Equal(t, []int{1, 2, 6}, common.LinkedListToSlice(t1)) 45 | } 46 | 47 | func deleteNode(node *common.ListNode) { 48 | nextNode := node.Next 49 | 50 | // if the next node is nil, it's the last node. this implementation does 51 | // not work. 52 | if nextNode == nil { 53 | return 54 | } 55 | 56 | node.Value = nextNode.Value 57 | node.Next = nextNode.Next 58 | } 59 | -------------------------------------------------------------------------------- /gtci/happy_number_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Write an algorithm to determine if a number is happy. 4 | - Any number will be called a happy number if, after repeatedly replacing 5 | it with a number equal to the sum of the square of all of its digits, 6 | leads us to 1. 7 | 8 | Example: 9 | - Input: 19 10 | Output: true 11 | Explanation: 12 | 1^2 + 9^2 = 82 13 | 8^2 + 2^2 = 68 14 | 6^2 + 8^2 = 100 15 | 1^2 + 0^2 + 0^2 = 1 16 | 17 | Approach: 18 | - Since the process always end in a cycle, we can use a similar approach to 19 | finding a cycle in linked list problem. 20 | - Once is cycle is found, check if it is stuck on 1. 21 | 22 | Cost: 23 | - O(n) time, O(1) space. 24 | */ 25 | 26 | package gtci 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestIsHappy(t *testing.T) { 35 | tests := []struct { 36 | in int 37 | expected bool 38 | }{ 39 | {23, true}, 40 | {19, true}, 41 | {12, false}, 42 | } 43 | 44 | for _, tt := range tests { 45 | common.Equal( 46 | t, 47 | tt.expected, 48 | isHappy(tt.in), 49 | ) 50 | } 51 | } 52 | 53 | func isHappy(num int) bool { 54 | slow, fast := num, num 55 | 56 | for { 57 | slow = squareSum(slow) 58 | fast = squareSum(squareSum(fast)) 59 | 60 | if slow == fast { 61 | break 62 | } 63 | } 64 | 65 | return slow == 1 66 | } 67 | 68 | func squareSum(num int) int { 69 | sum := 0 70 | 71 | for num > 0 { 72 | lastDigit := num % 10 73 | sum += lastDigit * lastDigit 74 | 75 | // get rid of last digit. 76 | num /= 10 77 | } 78 | 79 | return sum 80 | } 81 | -------------------------------------------------------------------------------- /interviewcake/inplace_shuffle_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of integers, shuffle its location in-place. 4 | 5 | Example: 6 | - Input: []int{1, 2, 3, 4, 5} 7 | Output: []int{2, 1, 4, 3, 5} 8 | 9 | Approach: 10 | - Iterate through the list, swap current value with a value in a randomized 11 | index that is between the current and last index. 12 | 13 | Solution: 14 | - Cache the last index value of the list. 15 | - Iterate through the list, get a randomized index value between the 16 | current index and the last index, then use it to swap the corresponding 17 | values at these two indices. 18 | 19 | Cost: 20 | - O(n) time, O(1) space. 21 | */ 22 | 23 | package interviewcake 24 | 25 | import ( 26 | "testing" 27 | 28 | "github.com/hoanhan101/algo/common" 29 | ) 30 | 31 | func TestInplaceShuffle(t *testing.T) { 32 | tests := []struct { 33 | in []int 34 | expected []int 35 | }{ 36 | {[]int{}, []int{}}, 37 | {[]int{1}, []int{1}}, 38 | {[]int{1, 2, 3, 4, 5}, []int{2, 1, 4, 3, 5}}, 39 | } 40 | 41 | for _, tt := range tests { 42 | result := inplaceShuffle(tt.in) 43 | common.Equal(t, tt.expected, result) 44 | } 45 | } 46 | 47 | func inplaceShuffle(list []int) []int { 48 | // check edge case. 49 | if len(list) <= 1 { 50 | return list 51 | } 52 | 53 | lastIndex := len(list) - 1 54 | for i := 0; i < len(list); i++ { 55 | // get a andomized index that is between the current and last index. 56 | randomIndex := common.Random(i, lastIndex) 57 | 58 | // swap current value. 59 | if i != randomIndex { 60 | common.Swap(list, i, randomIndex) 61 | } 62 | } 63 | 64 | return list 65 | } 66 | -------------------------------------------------------------------------------- /gtci/dutch_flag_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing 0s, 1s and 2s, sort the array in-place. 4 | 5 | Example: 6 | - Input: []int{1, 0, 2, 1, 0} 7 | Output: []int{0, 0, 1, 1, 2} 8 | 9 | Approach: 10 | - Have one pointer start at the beginning and the other at the end 11 | while iterating through the array. 12 | - We will move all 0s before that start pointer and 2s after the end 13 | pointer so that all 1s would be between in the end. 14 | 15 | Cost: 16 | - O(n) time, O(1) space. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestDutchFlagSort(t *testing.T) { 28 | tests := []struct { 29 | in []int 30 | expected []int 31 | }{ 32 | {[]int{0, 0, 0, 0, 0}, []int{0, 0, 0, 0, 0}}, 33 | {[]int{1, 1, 1, 1, 1}, []int{1, 1, 1, 1, 1}}, 34 | {[]int{2, 2, 2, 2, 2}, []int{2, 2, 2, 2, 2}}, 35 | {[]int{1, 0, 2, 1, 0}, []int{0, 0, 1, 1, 2}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | dutchFlagSort(tt.in) 40 | common.Equal( 41 | t, 42 | tt.expected, 43 | tt.in, 44 | ) 45 | } 46 | } 47 | 48 | func dutchFlagSort(a []int) { 49 | start := 0 50 | end := len(a) - 1 51 | 52 | i := 0 53 | for i <= end { 54 | if a[i] == 0 { 55 | // swap a[i] and a[start]. 56 | common.Swap(a, i, start) 57 | 58 | // increment start pointer and i. 59 | i++ 60 | start++ 61 | } else if a[i] == 1 { 62 | // increment i pointer only. 63 | i++ 64 | } else if a[i] == 2 { 65 | // swap a[i] and a[end]. 66 | common.Swap(a, i, end) 67 | 68 | // decrement end pointer only. 69 | end-- 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /gtci/avg_subarray_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array, find the average of all contiguous subarrays of size k. 4 | 5 | Example: 6 | - Input: []int{1, 3, 2, 6, -1, 4, 1, 8, 2}, k=5 7 | Output: []int{2.2, 2.8, 2.4, 3.6, 2.8} 8 | 9 | Approach: 10 | - View each contiguous subarray as a sliding window of k elements. 11 | - As we move to the next subarray, slide the window by one element to 12 | reuse the sum from previous array. 13 | 14 | Cost: 15 | - O(n) time, O(k) space. 16 | */ 17 | 18 | package gtci 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/hoanhan101/algo/common" 24 | ) 25 | 26 | func TestAvgSubarray(t *testing.T) { 27 | tests := []struct { 28 | in1 []int 29 | in2 int 30 | expected []float64 31 | }{ 32 | {[]int{}, 5, []float64{}}, 33 | {[]int{1, 3, 2, 6, -1}, 5, []float64{2.2}}, 34 | {[]int{1, 3, 2, 6, -1, 4, 1, 8, 2}, 5, []float64{2.2, 2.8, 2.4, 3.6, 2.8}}, 35 | } 36 | 37 | for _, tt := range tests { 38 | common.Equal( 39 | t, 40 | tt.expected, 41 | avgSubarray(tt.in1, tt.in2), 42 | ) 43 | } 44 | } 45 | 46 | func avgSubarray(a []int, k int) []float64 { 47 | out := []float64{} 48 | 49 | // sum keeps track of the sum of a window while start keeps track of 50 | // its start index. 51 | sum, start := 0, 0 52 | 53 | for end := range a { 54 | sum += a[end] 55 | 56 | // slide the window once we hit its limit. 57 | if end >= k-1 { 58 | out = append(out, float64(sum)/float64(k)) 59 | 60 | // subtract the start element and increase the start index to move 61 | // the window ahead by one element. 62 | sum -= a[start] 63 | start++ 64 | } 65 | } 66 | 67 | return out 68 | } 69 | -------------------------------------------------------------------------------- /gtci/reverse_list_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given the head of a singly linked list, write a function to return the 4 | new head of the reversed linked list. 5 | 6 | Approach: 7 | - Iterate through the linked list and at each step, reverse the current 8 | node by pointing it to the previous node before moving on. 9 | 10 | Cost: 11 | - O(n) time, O(1) space. 12 | */ 13 | 14 | package gtci 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/hoanhan101/algo/common" 20 | ) 21 | 22 | func TestReverseList(t *testing.T) { 23 | t0 := common.NewListNode(0) 24 | 25 | t1 := common.NewListNode(1) 26 | 27 | t2 := common.NewListNode(1) 28 | t2.AddNext(2) 29 | 30 | t3 := common.NewListNode(1) 31 | t3.AddNext(2) 32 | t3.AddNext(3) 33 | 34 | tests := []struct { 35 | in *common.ListNode 36 | expected []int 37 | }{ 38 | {t0, []int{0}}, 39 | {t1, []int{1}}, 40 | {t2, []int{2, 1}}, 41 | {t3, []int{3, 2, 1}}, 42 | } 43 | 44 | for _, tt := range tests { 45 | h := reverseList(tt.in) 46 | common.Equal( 47 | t, 48 | tt.expected, 49 | common.LinkedListToSlice(h), 50 | ) 51 | } 52 | } 53 | 54 | func reverseList(head *common.ListNode) *common.ListNode { 55 | if head == nil || head.Next == nil { 56 | return head 57 | } 58 | 59 | current := head 60 | var previous, next *common.ListNode 61 | 62 | for current != nil { 63 | // temporarily store the next node. 64 | next = current.Next 65 | 66 | // reverse the current node. 67 | current.Next = previous 68 | 69 | // point the previous node to the current node. 70 | previous = current 71 | 72 | // move on to the next node 73 | current = next 74 | } 75 | 76 | return previous 77 | } 78 | -------------------------------------------------------------------------------- /interviewcake/reverse_string_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of string, reverse its order in place. 4 | 5 | Example: 6 | - Input: []string{"a", "b", "c", "d"} 7 | Output: []string{"d", "c", "b", "a"} 8 | 9 | Approach: 10 | - Use two pointers approach to swap two values on both ends as we move toward 11 | the middle. 12 | 13 | Solution: 14 | - Initialize the two pointers, one starts at the beginning and one starts at 15 | the end of the list. 16 | - While the start pointer does not meet the end pointer in the middle, keep 17 | swapping these two values. 18 | - Move the start pointer up and move the end pointer down. 19 | 20 | Cost: 21 | - O(n) time, O(1) space. 22 | */ 23 | 24 | package interviewcake 25 | 26 | import ( 27 | "testing" 28 | 29 | "github.com/hoanhan101/algo/common" 30 | ) 31 | 32 | func TestReverseString(t *testing.T) { 33 | tests := []struct { 34 | in []string 35 | expected []string 36 | }{ 37 | {[]string{}, []string{}}, 38 | {[]string{"a"}, []string{"a"}}, 39 | {[]string{"a", "b"}, []string{"b", "a"}}, 40 | {[]string{"a", "b", "c"}, []string{"c", "b", "a"}}, 41 | {[]string{"a", "b", "c", "d"}, []string{"d", "c", "b", "a"}}, 42 | } 43 | 44 | for _, tt := range tests { 45 | result := reverseString(tt.in) 46 | common.Equal(t, tt.expected, result) 47 | } 48 | } 49 | 50 | func reverseString(list []string) []string { 51 | // initialize start and end index pointer. 52 | start := 0 53 | end := len(list) - 1 54 | 55 | for start < end { 56 | // swap 2 character using a temp variable. 57 | common.Swap(list, start, end) 58 | 59 | // move the cursor toward the middle. 60 | start++ 61 | end-- 62 | } 63 | 64 | return list 65 | } 66 | -------------------------------------------------------------------------------- /gtci/missing_numbers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing n distinct numbers taken from the range 0 to n. 4 | Since the array has only n numbers out of the total n+1 numbers, find the 5 | missing number. 6 | 7 | Example: 8 | - Input: []int{4, 0, 3, 1} 9 | Output: 2 10 | 11 | Approach: 12 | - Sort the array using the cyclic sort first. 13 | - The one that does not have the correct index is the missing one. 14 | 15 | Cost: 16 | - O(n) time, O(1) space. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestMissingNumber(t *testing.T) { 28 | tests := []struct { 29 | in []int 30 | expected int 31 | }{ 32 | {[]int{}, 0}, 33 | {[]int{4, 0, 3, 1}, 2}, 34 | {[]int{8, 3, 5, 2, 4, 7, 0, 1}, 6}, 35 | } 36 | 37 | for _, tt := range tests { 38 | common.Equal( 39 | t, 40 | tt.expected, 41 | missingNumber(tt.in), 42 | ) 43 | } 44 | } 45 | 46 | func missingNumber(nums []int) int { 47 | i := 0 48 | for i < len(nums) { 49 | // if the current number is not at the correct index, swap it with the 50 | // one that is at the correct index. 51 | // note that we have to also make sure the current value is not larger 52 | // than the length of the array because it would causes out of index 53 | // error. 54 | correctIndex := nums[i] 55 | if nums[i] < len(nums) && nums[i] != nums[correctIndex] { 56 | common.Swap(nums, i, correctIndex) 57 | } else { 58 | i++ 59 | } 60 | } 61 | 62 | // return the missing number that does not have the correct index. 63 | for j := 0; j < len(nums); j++ { 64 | if nums[j] != j { 65 | return j 66 | } 67 | } 68 | 69 | return len(nums) 70 | } 71 | -------------------------------------------------------------------------------- /gtci/conflict_appointment_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of intervals, check if any of them is conflicting. 4 | 5 | Example: 6 | - Input: []interval{{1, 2}, {2, 3}, {4, 5}} 7 | Output: false 8 | - Input: []interval{{1, 5}, {2, 3}} 9 | Output: true 10 | 11 | Approach: 12 | - Similar to merge intervals problem, need to return the true 13 | immediately if any of them is conflicting. 14 | 15 | Cost: 16 | - O(nlogn) time, O(n) space. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "sort" 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestIsConflicting(t *testing.T) { 29 | tests := []struct { 30 | in []interval 31 | expected bool 32 | }{ 33 | {[]interval{}, false}, 34 | {[]interval{{1, 2}}, false}, 35 | {[]interval{{1, 2}, {2, 3}}, false}, 36 | {[]interval{{2, 3}, {1, 2}}, false}, 37 | {[]interval{{1, 2}, {2, 3}, {5, 6}}, false}, 38 | {[]interval{{1, 2}, {5, 6}, {2, 3}}, false}, 39 | {[]interval{{1, 2}, {1, 3}}, true}, 40 | {[]interval{{1, 3}, {1, 3}}, true}, 41 | {[]interval{{1, 2}, {2, 3}, {2, 6}}, true}, 42 | {[]interval{{1, 2}, {2, 6}, {2, 3}}, true}, 43 | } 44 | 45 | for _, tt := range tests { 46 | common.Equal( 47 | t, 48 | tt.expected, 49 | isConflicting(tt.in), 50 | ) 51 | } 52 | } 53 | 54 | func isConflicting(intervals []interval) bool { 55 | // sort the intervals in ascending order. 56 | sort.Slice(intervals, func(i, j int) bool { 57 | return intervals[i].start < intervals[j].start 58 | }) 59 | 60 | // return false immediately if any interval is conflicting. 61 | for i := 1; i < len(intervals); i++ { 62 | if intervals[i].start < intervals[i-1].end { 63 | return true 64 | } 65 | } 66 | 67 | return false 68 | } 69 | -------------------------------------------------------------------------------- /lab/counting_sort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement counting sort. 4 | 5 | Approach: 6 | - Iterate through the input, count the number of times each item occurs, use 7 | these counts to compute each item's index in the final sorted array. 8 | 9 | Cost: 10 | - O(n) time and O(n) space. 11 | */ 12 | 13 | package lab 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestCountingSort(t *testing.T) { 22 | tests := []struct { 23 | in []int 24 | expected []int 25 | }{ 26 | {[]int{}, []int{}}, 27 | {[]int{1}, []int{1}}, 28 | {[]int{1, 2}, []int{1, 2}}, 29 | {[]int{2, 1}, []int{1, 2}}, 30 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 31 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 32 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 33 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 34 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | result := countingSort(tt.in, 6) 40 | common.Equal(t, tt.expected, result) 41 | } 42 | } 43 | 44 | func countingSort(in []int, max int) []int { 45 | // utilize max value to create a fix-sized list of item counts. 46 | counts := make([]int, max+1) 47 | out := make([]int, 0) 48 | 49 | // populate the array where its indices represent items themselves and 50 | // its values represent how many time the item appears. 51 | for _, item := range in { 52 | counts[item]++ 53 | } 54 | 55 | // iterate through the counts and add the item to the output list. 56 | for i := 0; i < len(counts); i++ { 57 | count := counts[i] 58 | 59 | for j := 0; j < count; j++ { 60 | out = append(out, i) 61 | } 62 | } 63 | 64 | return out 65 | } 66 | -------------------------------------------------------------------------------- /gtci/cyclic_sort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing n objects where each object, when created, 4 | was assigned a unique number from 1 to n based on their creation sequence. 5 | This means that the object with sequence number 3 was created just before 6 | the object with sequence number 4. 7 | - Write a function to sort the objects in-place on their creation sequence 8 | number in O(n) and without any extra space. 9 | 10 | Example: 11 | - Input: []int{6, 3, 5, 2, 4, 1} 12 | Output: []int{1, 2, 3, 4, 5, 6} 13 | 14 | Approach: 15 | - Use the fact that we are given a range of 1-n, can try placing each number at 16 | its correct index in the array, say 1 at 0, 2 at 1, 3 at 2 and so on. 17 | - Iterate through the array and if the current number is not at the correct index, 18 | swap it with the number at its correct index. 19 | 20 | Cost: 21 | - O(n) time, O(1) space. 22 | */ 23 | 24 | package gtci 25 | 26 | import ( 27 | "testing" 28 | 29 | "github.com/hoanhan101/algo/common" 30 | ) 31 | 32 | func TestCyclicSort(t *testing.T) { 33 | tests := []struct { 34 | in []int 35 | expected []int 36 | }{ 37 | {[]int{}, []int{}}, 38 | {[]int{6, 4, 5, 2, 3, 1}, []int{1, 2, 3, 4, 5, 6}}, 39 | } 40 | 41 | for _, tt := range tests { 42 | cyclicSort(tt.in) 43 | common.Equal( 44 | t, 45 | tt.expected, 46 | tt.in, 47 | ) 48 | } 49 | } 50 | 51 | func cyclicSort(nums []int) { 52 | i := 0 53 | for i < len(nums) { 54 | // if the current number is not at the correct index, swap it with the 55 | // one that is at the correct index. 56 | correctIndex := nums[i] - 1 57 | if nums[i] != nums[correctIndex] { 58 | common.Swap(nums, i, correctIndex) 59 | } else { 60 | i++ 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /interviewcake/queue_two_stacks_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement a queue with 2 stacks. 4 | 5 | Approach: 6 | - Use one stack for enqueuing item and the other to reverse the order them for 7 | dequeuing/popping item. 8 | 9 | Cost: 10 | - O(1) time, O(m) space m is the number of operations. 11 | */ 12 | 13 | package interviewcake 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestQueueStack(t *testing.T) { 22 | s := newQueueStack() 23 | 24 | // return -1 if there is nothing in the stack. 25 | common.Equal(t, -1, s.dequeue()) 26 | 27 | // define test cases. 28 | tests := []int{1, 2, 3, 4, 5, 6} 29 | 30 | // enqueue items to the queue one by one. 31 | for _, tt := range tests { 32 | s.enqueue(tt) 33 | } 34 | 35 | // dequeue items from the queue one by one and check against it. 36 | for _, tt := range tests { 37 | common.Equal(t, tt, s.dequeue()) 38 | } 39 | 40 | // return -1 if there is nothing in the stack. 41 | common.Equal(t, -1, s.dequeue()) 42 | } 43 | 44 | type queueStack struct { 45 | stack1 *common.Stack 46 | stack2 *common.Stack 47 | } 48 | 49 | func newQueueStack() *queueStack { 50 | s1 := common.NewStack() 51 | s2 := common.NewStack() 52 | 53 | return &queueStack{ 54 | stack1: s1, 55 | stack2: s2, 56 | } 57 | } 58 | 59 | func (q *queueStack) enqueue(i int) { 60 | q.stack1.Push(i) 61 | } 62 | 63 | func (q *queueStack) dequeue() int { 64 | if q.stack2.Size() == 0 { 65 | // reverse the order of all items in other stack. 66 | for q.stack1.Size() > 0 { 67 | q.stack2.Push(q.stack1.Pop()) 68 | } 69 | 70 | // return -1 if the queue is empty 71 | if q.stack2.Size() == 0 { 72 | return -1 73 | } 74 | } 75 | 76 | return q.stack2.Pop().(int) 77 | } 78 | -------------------------------------------------------------------------------- /gtci/max_subarray_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of positive numbers and a positive number k, find the maximum 4 | sum of any contiguous subarray of size k. 5 | 6 | Example: 7 | - Input: int{2, 1, 5, 1, 3, 2}, k=3 8 | Output: 9 9 | Explanation: Subarray with maximum sum is [5, 1, 3]. 10 | 11 | Approach: 12 | - View each contiguous subarray as a sliding window of k elements. 13 | - As we move to the next subarray, slide the window by one element to 14 | reuse the sum from previous array and update the maximum sum. 15 | 16 | Cost: 17 | - O(n) time, O(1) space. 18 | */ 19 | 20 | package gtci 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestMaxSubarray(t *testing.T) { 29 | tests := []struct { 30 | in1 []int 31 | in2 int 32 | expected int 33 | }{ 34 | {[]int{}, 1, 0}, 35 | {[]int{1}, 1, 1}, 36 | {[]int{1}, 2, 0}, 37 | {[]int{1, 2}, 1, 2}, 38 | {[]int{1, 2}, 2, 3}, 39 | {[]int{1, 2}, 3, 0}, 40 | {[]int{2, 1, 5, 1, 3, 2}, 3, 9}, 41 | {[]int{2, 3, 4, 1, 5}, 2, 7}, 42 | } 43 | 44 | for _, tt := range tests { 45 | common.Equal( 46 | t, 47 | tt.expected, 48 | maxSubarray(tt.in1, tt.in2), 49 | ) 50 | } 51 | } 52 | 53 | func maxSubarray(a []int, k int) int { 54 | max := 0 55 | 56 | // sum keeps track of the sum of a window while start keeps track of 57 | // its start index. 58 | sum, start := 0, 0 59 | 60 | for end := range a { 61 | sum += a[end] 62 | 63 | // slide the window once we hit its limit. 64 | if end >= k-1 { 65 | max = common.Max(max, sum) 66 | 67 | // subtract the start element and increase the start index to move 68 | // the window ahead by one element. 69 | sum -= a[start] 70 | start++ 71 | } 72 | } 73 | 74 | return max 75 | } 76 | -------------------------------------------------------------------------------- /gtci/no_repeat_substring_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, find the length of the longest substring which has no repeating characters. 4 | 5 | Example: 6 | - Input: "aabccbb" 7 | Output: 3 8 | Explanation: Longest substring which has no repeating characters.is "abc" 9 | 10 | Approach: 11 | - Similar to longest substring with k distinct characters, we can use a hashmap 12 | to remember the last index of each character we have processed. 13 | 14 | Cost: 15 | - O(n) time, O(k) space where k is the number of characters in the map. 16 | */ 17 | 18 | package gtci 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/hoanhan101/algo/common" 24 | ) 25 | 26 | func TestNoRepeatSubstring(t *testing.T) { 27 | tests := []struct { 28 | in string 29 | expected int 30 | }{ 31 | {"", 0}, 32 | {"a", 1}, 33 | {"aa", 1}, 34 | {"ab", 2}, 35 | {"aba", 2}, 36 | {"abba", 2}, 37 | {"abb", 2}, 38 | {"abc", 3}, 39 | {"abccde", 3}, 40 | {"aabccbb", 3}, 41 | } 42 | 43 | for _, tt := range tests { 44 | common.Equal( 45 | t, 46 | tt.expected, 47 | noRepeatSubstring(tt.in), 48 | ) 49 | } 50 | } 51 | 52 | func noRepeatSubstring(s string) int { 53 | maxLength, start := 0, 0 54 | 55 | // char remembers the last index of each character we have processed. 56 | char := map[string]int{} 57 | 58 | for end := range s { 59 | endChar := string(s[end]) 60 | 61 | // if we have seen the character before, update the start index to 62 | // skip visited characters. 63 | if _, ok := char[endChar]; ok { 64 | start = common.Max(start, char[endChar]+1) 65 | } 66 | 67 | // cache the current character' index. 68 | char[endChar] = end 69 | 70 | // update the maximum length. 71 | maxLength = common.Max(maxLength, end-start+1) 72 | } 73 | 74 | return maxLength 75 | } 76 | -------------------------------------------------------------------------------- /interviewcake/linked_list_cycle_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Determine if a singly-linked list has a cycle. 4 | 5 | Approach: 6 | - Keep two pointers starting at the first node such that: every time one moves 7 | one node ahead, the other moves 2 nodes ahead. 8 | - If the linked list has a cycle, the faster one will catch up with the slow 9 | one. Otherwise, the faster one will each the end. 10 | 11 | Solution: 12 | - Keep two pointers, a slow one and a fast one, at the beginning. 13 | - Traverse the list and move the fast one 2 nodes once the slow one move a node. 14 | - If the fast one catches the slow one, there exists a cycle. 15 | 16 | Cost: 17 | - O(n) time and O(1) space. 18 | */ 19 | 20 | package interviewcake 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestContainCycle(t *testing.T) { 29 | // define tests input. 30 | t1 := common.NewListNode(1) 31 | t1.AddNext(2) 32 | t1.AddNext(3) 33 | 34 | t2 := common.NewListNode(1) 35 | t2.AddNext(2) 36 | t2.AddNext(3) 37 | t2.Next.Next.Next = t2 38 | 39 | // define tests output. 40 | tests := []struct { 41 | in *common.ListNode 42 | expected bool 43 | }{ 44 | {t1, false}, 45 | {t2, true}, 46 | } 47 | 48 | for _, tt := range tests { 49 | common.Equal(t, tt.expected, containCycle(tt.in)) 50 | } 51 | } 52 | 53 | func containCycle(node *common.ListNode) bool { 54 | // keep two pointers at the beginning. 55 | slow := node 56 | fast := node 57 | 58 | // traverse until it hit the end of the list. 59 | for fast != nil && fast.Next != nil && fast.Next.Next != nil { 60 | slow = slow.Next 61 | fast = fast.Next.Next 62 | 63 | // if the fast pointer catches the slow one, there exists a cycle. 64 | if fast.Value == slow.Value { 65 | return true 66 | } 67 | } 68 | 69 | return false 70 | } 71 | -------------------------------------------------------------------------------- /gtci/square_sorted_array_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a sorted array, create a new array containing squares of all the 4 | number of the input array in the sorted order. 5 | 6 | Assumption: 7 | - The input can have negative numbers. 8 | 9 | Example: 10 | - Input: []int{-2, -1, 0, 1, 2} 11 | Output: []int{0, 1, 1, 4, 4} 12 | 13 | Approach: 14 | - The difficult part is to generate the output array with squares in sorted order because we have negative numbers and their squares are positive. 15 | - Have one pointer start at the beginning and one at the end and let them 16 | move toward each other. 17 | - At each step, whichever bigger will be added to the output array, from 18 | right to left. 19 | 20 | Cost: 21 | - O(n) time, O(n) space. 22 | */ 23 | 24 | package gtci 25 | 26 | import ( 27 | "testing" 28 | 29 | "github.com/hoanhan101/algo/common" 30 | ) 31 | 32 | func TestSortedSquare(t *testing.T) { 33 | tests := []struct { 34 | in []int 35 | expected []int 36 | }{ 37 | {[]int{-3, -2, -1}, []int{1, 4, 9}}, 38 | {[]int{1, 2, 3}, []int{1, 4, 9}}, 39 | {[]int{-2, -1, 0, 1, 2}, []int{0, 1, 1, 4, 4}}, 40 | } 41 | 42 | for _, tt := range tests { 43 | common.Equal( 44 | t, 45 | tt.expected, 46 | sortedSquare(tt.in), 47 | ) 48 | } 49 | } 50 | 51 | func sortedSquare(a []int) []int { 52 | out := make([]int, len(a)) 53 | 54 | start := 0 55 | end := len(a) - 1 56 | outIndex := end 57 | 58 | for start <= end { 59 | startSquare := a[start] * a[start] 60 | endSquare := a[end] * a[end] 61 | 62 | if startSquare < endSquare { 63 | out[outIndex] = endSquare 64 | end-- 65 | } else if startSquare > endSquare { 66 | out[outIndex] = startSquare 67 | start++ 68 | } else { 69 | // only need to move one side if their squares are equal. 70 | out[outIndex] = startSquare 71 | start++ 72 | } 73 | 74 | outIndex-- 75 | } 76 | 77 | return out 78 | } 79 | -------------------------------------------------------------------------------- /gtci/longest_substring_ones_replacement_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array containing 0s and 1s, if you are allowed to replace no more 4 | than k 0s with 1s, find the length of the longest contiguous subarray having all 1s. 5 | 6 | Example: 7 | - Input: []int{0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1}, k=2 8 | Output: 6 9 | Explanation: Have the longest subarray of 1s after replacing 0 at index 5 and 8 10 | 11 | Approach: 12 | - Similar to longest substring after k replacements problem, except we only have 13 | 1 and 0 in the array. 14 | 15 | Cost: 16 | - O(n) time, O(1) space since there are only 26 characters in the alphabet. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestLongestSubstringOnesReplacement(t *testing.T) { 28 | tests := []struct { 29 | in1 []int 30 | in2 int 31 | expected int 32 | }{ 33 | {[]int{}, 1, 0}, 34 | {[]int{0}, 1, 1}, 35 | {[]int{0, 0}, 1, 1}, 36 | {[]int{0, 0, 1}, 1, 2}, 37 | {[]int{1, 0, 1}, 1, 3}, 38 | {[]int{0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1}, 2, 6}, 39 | {[]int{0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1}, 3, 9}, 40 | } 41 | 42 | for _, tt := range tests { 43 | common.Equal( 44 | t, 45 | tt.expected, 46 | longestSubstringOnesReplacement(tt.in1, tt.in2), 47 | ) 48 | } 49 | } 50 | 51 | func longestSubstringOnesReplacement(arr []int, k int) int { 52 | maxLength, start, maxOnesCount := 0, 0, 0 53 | 54 | for end := range arr { 55 | if arr[end] == 1 { 56 | maxOnesCount++ 57 | } 58 | 59 | // shrink ther window as we are not allowed to replace more than k 0s. 60 | if end-start+1-maxOnesCount > k { 61 | if arr[start] == 1 { 62 | maxOnesCount-- 63 | } 64 | 65 | start++ 66 | } 67 | 68 | // update the maximum length at each step. 69 | maxLength = common.Max(maxLength, end-start+1) 70 | } 71 | 72 | return maxLength 73 | } 74 | -------------------------------------------------------------------------------- /interviewcake/fibonacci_number_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an integer n, write a function to return the nth Fibonacci 4 | number. Assume that n is a positive integer. 5 | 6 | Example: 7 | - Input: 4 8 | Output: 3, because the 4th Fibonacci of the sequence [0, 1, 1, 2, 3] is 3 9 | 10 | Approach: 11 | - Instead of using a recursive approach, use a bottom-up approach and 12 | iteratively compute subsequent numbers until we get to n. 13 | 14 | Solution: 15 | - Initialize a value that holds the nth Fibonacci number. 16 | - Initialize two values that hold two previous numbers' values of the series. 17 | - Iterate from 0 to n and update these 3 values at each step where: 18 | - the current value is equal to the sum of two previous numbers. 19 | - one the previous number is equal to the one that is next to it while 20 | the other is equal to the current Fibonacci number. 21 | 22 | Cost: 23 | - O(n) time, O(1) space. 24 | */ 25 | 26 | package interviewcake 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestFib(t *testing.T) { 35 | tests := []struct { 36 | in int 37 | expected int 38 | }{ 39 | {0, 0}, 40 | {1, 1}, 41 | {2, 1}, 42 | {3, 2}, 43 | {4, 3}, 44 | {5, 5}, 45 | {6, 8}, 46 | } 47 | 48 | for _, tt := range tests { 49 | result := fib(tt.in) 50 | common.Equal(t, tt.expected, result) 51 | } 52 | } 53 | 54 | func fib(n int) int { 55 | // current holds the nth Fibonacci number. 56 | current := 0 57 | 58 | // return 0 1 or for base cases. 59 | if n == 0 || n == 1 { 60 | return n 61 | } 62 | 63 | // keep track of 2 previous numbers at each step to build the Fibonacci 64 | // series from the bottom up 65 | f0 := 0 66 | f1 := 1 67 | 68 | // to get nth Fibonacci, do n-1 iterations. 69 | for i := 0; i < n-1; i++ { 70 | current = f0 + f1 71 | f0 = f1 72 | f1 = current 73 | } 74 | 75 | return current 76 | } 77 | -------------------------------------------------------------------------------- /leetcode/two_sum_i_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of integers, return indices of the two numbers such that they 4 | add up to a specific target. 5 | - You may assume that each input would have exactly one solution, and you may 6 | not use the same element twice. 7 | 8 | Example: 9 | - Input: nums = []int{2, 5, 4}, target = 6 10 | Output: [0, 2] since nums[0] + nums[2] = 2 + 4 = 6 11 | 12 | Approach: 13 | - Use a hash map to store the value and its index as we iterate through the 14 | list. 15 | - Within each iteration, look up the difference of target and the current 16 | value to see if we have seen that number. 17 | - Simply return two cached indices once that condition meets. 18 | 19 | Cost: 20 | - O(n) time, O(n) space. 21 | */ 22 | 23 | package leetcode 24 | 25 | import ( 26 | "testing" 27 | 28 | "github.com/hoanhan101/algo/common" 29 | ) 30 | 31 | func TestTwoSumI(t *testing.T) { 32 | tests := []struct { 33 | in1 []int 34 | in2 int 35 | expected []int 36 | }{ 37 | {[]int{}, 6, []int{0, 0}}, 38 | {[]int{1}, 6, []int{0, 0}}, 39 | {[]int{2, 4}, 6, []int{0, 1}}, 40 | {[]int{2, 5}, 6, []int{0, 0}}, 41 | {[]int{2, 5, 4}, 6, []int{0, 2}}, 42 | {[]int{2, 5, 8}, 6, []int{0, 0}}, 43 | } 44 | 45 | for _, tt := range tests { 46 | result := twoSumI(tt.in1, tt.in2) 47 | common.Equal(t, tt.expected, result) 48 | } 49 | } 50 | 51 | func twoSumI(nums []int, target int) []int { 52 | m := map[int]int{} 53 | out := make([]int, 2) 54 | 55 | // return empty slice on edge cases. 56 | if len(nums) == 0 || len(nums) == 1 { 57 | return out 58 | } 59 | 60 | // iterate through the list, look up the temporary hash map to see if we 61 | // have seen the number before and return both cached indices. 62 | for i, num := range nums { 63 | if j, ok := m[target-num]; ok { 64 | out[0] = j 65 | out[1] = i 66 | } 67 | 68 | m[num] = i 69 | } 70 | 71 | return out 72 | } 73 | -------------------------------------------------------------------------------- /gtci/spermutations_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a set of distinct numbers, find all of its permutations. 4 | 5 | Example: 6 | - Input: [1 2] 7 | Output: [[2 1] [1 2]] 8 | - Input: [1 2 3] 9 | Output: [[3 2 1] [2 3 1] [2 1 3] [3 1 2] [1 3 2] [1 2 3]] 10 | 11 | Approach: 12 | - For every existing permutations, create new permutations by adding the number to 13 | all of its positions. 14 | 15 | Cost: 16 | - O(n*n!) time, O(n*n!) space since n! is the total of permutations of a n-number set. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestFindSPermutations(t *testing.T) { 28 | tests := []struct { 29 | in []int 30 | expected [][]int 31 | }{ 32 | {[]int{}, [][]int{}}, 33 | {[]int{1}, [][]int{{1}}}, 34 | {[]int{1, 2}, [][]int{{2, 1}, {1, 2}}}, 35 | {[]int{1, 2, 3}, [][]int{{3, 2, 1}, {2, 3, 1}, {2, 1, 3}, {3, 1, 2}, {1, 3, 2}, {1, 2, 3}}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | common.Equal( 40 | t, 41 | tt.expected, 42 | findSPermutations(tt.in), 43 | ) 44 | } 45 | } 46 | 47 | func findSPermutations(nums []int) [][]int { 48 | permutations := [][]int{} 49 | subsets := [][]int{} 50 | subsets = append(subsets, []int{}) 51 | 52 | for _, num := range nums { 53 | n := len(subsets) 54 | for i := 0; i < n; i++ { 55 | current := subsets[i] 56 | // add the current number at every position of a new subset. 57 | for j := 0; j < len(current)+1; j++ { 58 | news := current 59 | 60 | news = append(news, 0) 61 | copy(news[j+1:], news[j:]) 62 | news[j] = num 63 | 64 | // if the length of the new set is equal to the length of the 65 | // given slice, we found a permutation. 66 | if len(news) == len(nums) { 67 | permutations = append(permutations, news) 68 | } 69 | 70 | subsets = append(subsets, news) 71 | } 72 | } 73 | } 74 | 75 | return permutations 76 | } 77 | -------------------------------------------------------------------------------- /leetcode/strstr_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement strstr() that finds the first occurrence of the substring 4 | needle in the string haystack. It returns -1 if needle is not part of the 5 | haystack. 6 | 7 | Example: 8 | - Input: haystack = "aaabacd", needle = "ba" 9 | Output: 3, because needle "ba" starts at index 3 in the haystack. 10 | 11 | Approach: 12 | - Scan the needle with the haystack from its first position and start matching 13 | all subsequent letters one by one. 14 | - If one letter does not match, start again with the next position in the 15 | haystack. 16 | 17 | Cost: 18 | - O(nm) time, O(1) space, where n is the length of haystack while m is the 19 | length of needle. 20 | */ 21 | 22 | package leetcode 23 | 24 | import ( 25 | "testing" 26 | 27 | "github.com/hoanhan101/algo/common" 28 | ) 29 | 30 | func TestStrstr(t *testing.T) { 31 | tests := []struct { 32 | in1 string 33 | in2 string 34 | expected int 35 | }{ 36 | {"", "", -1}, 37 | {"", "a", -1}, 38 | {"dani", "a", 1}, 39 | {"danidani", "a", 1}, 40 | {"xxxdani", "dani", 3}, 41 | {"xxxdanixxx", "dani", 3}, 42 | } 43 | 44 | for _, tt := range tests { 45 | result := strstr(tt.in1, tt.in2) 46 | common.Equal(t, tt.expected, result) 47 | } 48 | } 49 | 50 | func strstr(haystack string, needle string) int { 51 | for i := 0; i < len(haystack); i++ { 52 | for j := 0; j < len(haystack); j++ { 53 | // if the needle matches the haystack, meaning the index pointer 54 | // reaches the end of the needle, return the index pointer. 55 | if j == len(needle) { 56 | return i 57 | } 58 | 59 | // return -1 if needle's length is greater than haystack's. 60 | if i+j == len(haystack) { 61 | return -1 62 | } 63 | 64 | // start again if one of the letter in the needles does not match 65 | // one in the haystack. 66 | if []rune(needle)[j] != []rune(haystack)[i+j] { 67 | break 68 | } 69 | } 70 | } 71 | 72 | return -1 73 | } 74 | -------------------------------------------------------------------------------- /interviewcake/inflight_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of movie lengths (integer) and a flight length (integer), determine if 4 | there exist two movies that add up to the total length. Assume that an user 5 | watch exactly two movies, but not the same one twice. 6 | 7 | Example: 8 | - Input: list=[]int{2, 3, 4}, length=6 9 | Output: true, because there exists 2 and 4 that add up to 6 10 | 11 | Approach: 12 | - Could use hashmap to keep track of movie lengths that we've seen so far and 13 | look it up as we iterate through the list. 14 | 15 | Solution: 16 | - Use a hashmap to keep track of movie lengths that we've seen so far. 17 | - As we iterate through the list, we calculate the difference for each value 18 | (total length - current movie length) and check if the difference is equal 19 | to the movie length that we've seen. 20 | - Return true if that's the case. 21 | 22 | Cost: 23 | - O(n) time, O(n) space. 24 | */ 25 | 26 | package interviewcake 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestFillFlight(t *testing.T) { 35 | tests := []struct { 36 | in1 []int 37 | in2 int 38 | expected bool 39 | }{ 40 | {[]int{}, 1, false}, 41 | {[]int{0}, 1, false}, 42 | {[]int{1}, 1, false}, 43 | {[]int{0, 1}, 1, true}, 44 | {[]int{1, 1}, 2, true}, 45 | {[]int{2, 3, 4}, 6, true}, 46 | {[]int{2, 3, 4}, 8, false}, 47 | } 48 | 49 | for _, tt := range tests { 50 | result := fillFlight(tt.in1, tt.in2) 51 | common.Equal(t, tt.expected, result) 52 | } 53 | } 54 | 55 | func fillFlight(movieLengths []int, flightLength int) bool { 56 | movies := map[int]int{} 57 | 58 | for _, v := range movieLengths { 59 | // return true if we've seen the movie. else add the movie in the 60 | // watched list with a count 1. 61 | matchLength := flightLength - v 62 | if _, ok := movies[matchLength]; ok { 63 | return true 64 | } 65 | 66 | movies[v] = 1 67 | } 68 | 69 | return false 70 | } 71 | -------------------------------------------------------------------------------- /leetcode/min_stack_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Design a stack that supports push, pop, top, and retrieving the minimum element 4 | in constant time. 5 | 6 | Approach: 7 | - We use two stack implementation themselves: one holds all the items and the 8 | other holds all the minimum values after each push() and pop(). 9 | 10 | Cost: 11 | - O(n) time, O(n) space. 12 | */ 13 | 14 | package leetcode 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/hoanhan101/algo/common" 20 | ) 21 | 22 | func TestMinStack(t *testing.T) { 23 | s := newMinStack() 24 | 25 | s.push(4) 26 | common.Equal(t, 4, s.getMin()) 27 | 28 | s.push(8) 29 | common.Equal(t, 4, s.getMin()) 30 | 31 | s.push(6) 32 | common.Equal(t, 4, s.getMin()) 33 | 34 | s.push(2) 35 | common.Equal(t, 2, s.getMin()) 36 | 37 | common.Equal(t, 2, s.pop()) 38 | common.Equal(t, 4, s.getMin()) 39 | common.Equal(t, 6, s.pop()) 40 | common.Equal(t, 4, s.getMin()) 41 | common.Equal(t, 8, s.pop()) 42 | common.Equal(t, 4, s.getMin()) 43 | common.Equal(t, 4, s.pop()) 44 | } 45 | 46 | type minStack struct { 47 | stack *common.Stack 48 | minStack *common.Stack 49 | } 50 | 51 | func newMinStack() *minStack { 52 | s := common.NewStack() 53 | ms := common.NewStack() 54 | 55 | return &minStack{ 56 | stack: s, 57 | minStack: ms, 58 | } 59 | } 60 | 61 | func (s *minStack) push(i int) { 62 | // push the item on top of our stack. 63 | s.stack.Push(i) 64 | 65 | // only push the item to the minStack if it smaller or equal to the last 66 | // item in minStack. 67 | if s.minStack.Size() == 0 || i <= s.minStack.Top().(int) { 68 | s.minStack.Push(i) 69 | } 70 | } 71 | 72 | func (s *minStack) pop() int { 73 | // pop the item of our stack. 74 | i := s.stack.Pop().(int) 75 | 76 | // if popped item is the smallest item, also remove that from minStack. 77 | if i == s.minStack.Top().(int) { 78 | s.minStack.Pop() 79 | } 80 | 81 | return i 82 | } 83 | 84 | func (s *minStack) getMin() int { 85 | return s.minStack.Top().(int) 86 | } 87 | -------------------------------------------------------------------------------- /interviewcake/permutation_palindrome_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, check if its permutation is a palindrome. 4 | 5 | Example: 6 | - Input: "ivicc" 7 | Output: true 8 | - Input: "civic" 9 | Output: true 10 | 11 | Approach: 12 | - To determine if a permutation is a palindrome, need to check if each 13 | character in the string appears an even number of times, allowing for 14 | only one character to appear an odd time, that is the middle one. 15 | - Could use a hashmap store the characters and their number of occurrences. 16 | 17 | Solution: 18 | - As we iterate through the string, use a hashmap to add a character if 19 | we haven't seen it and remove it if it's already there. 20 | - After the iteration, if we're left with less or equal than a character in 21 | the map, we have a palindrome. 22 | 23 | Cost: 24 | - O(n) time, O(1) space. 25 | - The space complexity is O(n) due to the hashmap, but since there are 26 | only a constant number of characters in Unicode, we could treat it 27 | as O(1). 28 | */ 29 | 30 | package interviewcake 31 | 32 | import ( 33 | "testing" 34 | 35 | "github.com/hoanhan101/algo/common" 36 | ) 37 | 38 | func TestHasPalindromePermutation(t *testing.T) { 39 | tests := []struct { 40 | in string 41 | expected bool 42 | }{ 43 | {"", true}, 44 | {"c", true}, 45 | {"cc", true}, 46 | {"ca", false}, 47 | {"civic", true}, 48 | {"ivicc", true}, 49 | {"civil", false}, 50 | {"livci", false}, 51 | } 52 | 53 | for _, tt := range tests { 54 | result := hasPalindromePermutation(tt.in) 55 | common.Equal(t, tt.expected, result) 56 | } 57 | } 58 | 59 | func hasPalindromePermutation(in string) bool { 60 | m := map[string]int{} 61 | 62 | // delete if we've seen it, else add it with a count 1. 63 | for _, v := range in { 64 | if _, ok := m[string(v)]; ok { 65 | delete(m, string(v)) 66 | } else { 67 | m[string(v)] = 1 68 | } 69 | } 70 | 71 | // if we're left with less or equal than a pair, we have a palindrome. 72 | return len(m) <= 1 73 | } 74 | -------------------------------------------------------------------------------- /leetcode/valid_palindrome_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, determine if it is a palindrome, considering only 4 | alphanumeric characters and ignoring cases. 5 | 6 | Example: 7 | - Input: "A man, a plan, a canal: Panama" 8 | Output: true 9 | - Input: "race a car" 10 | Output: false 11 | 12 | Approach: 13 | - Use two pointers approach that have one point to the start of the string and 14 | the other point at the end. 15 | - Move them toward each other and compare if they're the same characters while 16 | skipping non-alphanumeric characters and ignoring cases. 17 | 18 | Solution: 19 | - Have a pointer point to the start of the string and the other point at the end. 20 | - Make the string lower case. 21 | - While the start one is less the end one, ignore non-alphanumeric characters 22 | and move them toward each other. 23 | 24 | Cost: 25 | - O(n) time, O(1) space. 26 | */ 27 | 28 | package leetcode 29 | 30 | import ( 31 | "strings" 32 | "testing" 33 | "unicode" 34 | 35 | "github.com/hoanhan101/algo/common" 36 | ) 37 | 38 | func TestIsPalindrome(t *testing.T) { 39 | tests := []struct { 40 | in string 41 | expected bool 42 | }{ 43 | {"A man, a plan, a canal: Panama", true}, 44 | {"race a car", false}, 45 | } 46 | 47 | for _, tt := range tests { 48 | result := isPalindrome(tt.in) 49 | common.Equal(t, tt.expected, result) 50 | } 51 | } 52 | 53 | func isPalindrome(s string) bool { 54 | start := 0 55 | end := len(s) - 1 56 | 57 | // make the input string lower case. 58 | s = strings.ToLower(s) 59 | 60 | for start < end { 61 | // if the letter at the start and end pointers are non-alphanumeric 62 | // then skip them. 63 | for !unicode.IsLetter([]rune(s)[start]) { 64 | start++ 65 | } 66 | for !unicode.IsLetter([]rune(s)[end]) { 67 | end-- 68 | } 69 | 70 | // return false immediately if two corresponding characters are not 71 | // matching. 72 | if []rune(s)[start] != []rune(s)[end] { 73 | return false 74 | } 75 | 76 | start++ 77 | end-- 78 | } 79 | 80 | return true 81 | } 82 | -------------------------------------------------------------------------------- /leetcode/two_sum_ii_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a sorted array of integers, return indices of the two numbers such 4 | that they add up to a specific target. 5 | 6 | Example: 7 | - Input: nums = []int{2, 3, 4}, target = 6 8 | Output: [0, 2] since nums[0] + nums[2] = 2 + 4 = 6 9 | 10 | Approach 11 | - Since the array is sorted, can use two-pointer approach that has one point 12 | to the start of the list while the other point at the end and move the 13 | toward each other. 14 | 15 | Solution: 16 | - Have one pointer point to the start of the list and the other point at the end. 17 | - Iterate through the list and check their sum. 18 | - If it is smaller than the target, increment the start pointer to make the 19 | sum bigger little by little. 20 | - Otherwise, decrement the end pointer to make the sum smaller. 21 | - When the sum is equal to the target, we found the answer. 22 | 23 | Cost: 24 | - O(n) time, O(1) space. 25 | */ 26 | 27 | package leetcode 28 | 29 | import ( 30 | "testing" 31 | 32 | "github.com/hoanhan101/algo/common" 33 | ) 34 | 35 | func TestTwoSumII(t *testing.T) { 36 | tests := []struct { 37 | in1 []int 38 | in2 int 39 | expected []int 40 | }{ 41 | {[]int{}, 6, []int{0, 0}}, 42 | {[]int{1}, 6, []int{0, 0}}, 43 | {[]int{2, 4}, 6, []int{0, 1}}, 44 | {[]int{2, 5}, 6, []int{0, 0}}, 45 | {[]int{2, 3, 4}, 6, []int{0, 2}}, 46 | {[]int{2, 5, 8}, 6, []int{0, 0}}, 47 | {[]int{2, 3, 4, 10}, 6, []int{0, 2}}, 48 | } 49 | 50 | for _, tt := range tests { 51 | result := twoSumII(tt.in1, tt.in2) 52 | common.Equal(t, tt.expected, result) 53 | } 54 | } 55 | 56 | func twoSumII(nums []int, target int) []int { 57 | // start and end points to the start and end index of the list. 58 | start := 0 59 | end := len(nums) - 1 60 | out := make([]int, 2) 61 | 62 | for start < end { 63 | sum := nums[start] + nums[end] 64 | if sum < target { 65 | start++ 66 | } else if sum > target { 67 | end-- 68 | } else { 69 | out[0] = start 70 | out[1] = end 71 | break 72 | } 73 | } 74 | 75 | return out 76 | } 77 | -------------------------------------------------------------------------------- /gtci/path_sum_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a binary tree and a sum, find if the tree has a path from 4 | root-to-leaf such that the sum of all the node values of that path 5 | equals to the sum. 6 | 7 | Example: 8 | - Input: sum=8 9 | 1 10 | 2 3 11 | 4 5 6 7 12 | Output: true 13 | Explanation: The path with sum 8 goes through 1, 2 and 5. 14 | - Input: sum=16 15 | 1 16 | 2 3 17 | 4 5 6 7 18 | Output: false 19 | Explanation: There is no path with sum 16. 20 | 21 | Approach: 22 | - Traverse the tree in a depth first search fashion. 23 | - At each step, update the new sum by subtracting it with the current 24 | value of the node's we're visiting. 25 | - Once we hit the leaf node, return true if the sum is equal to its 26 | value. 27 | 28 | Cost: 29 | - O(n) time, O(n) space. 30 | */ 31 | 32 | package gtci 33 | 34 | import ( 35 | "testing" 36 | 37 | "github.com/hoanhan101/algo/common" 38 | ) 39 | 40 | func TestPathSum(t *testing.T) { 41 | t1 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 42 | t1.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 43 | t1.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 44 | t1.Left.Left = &common.TreeNode{Left: nil, Value: 4, Right: nil} 45 | t1.Left.Right = &common.TreeNode{Left: nil, Value: 5, Right: nil} 46 | t1.Right.Left = &common.TreeNode{Left: nil, Value: 6, Right: nil} 47 | t1.Right.Right = &common.TreeNode{Left: nil, Value: 7, Right: nil} 48 | 49 | tests := []struct { 50 | in1 *common.TreeNode 51 | in2 int 52 | expected bool 53 | }{ 54 | {t1, 7, true}, 55 | {t1, 8, true}, 56 | {t1, 10, true}, 57 | {t1, 11, true}, 58 | {t1, 16, false}, 59 | } 60 | 61 | for _, tt := range tests { 62 | common.Equal( 63 | t, 64 | tt.expected, 65 | pathSum(tt.in1, tt.in2), 66 | ) 67 | } 68 | } 69 | 70 | func pathSum(root *common.TreeNode, sum int) bool { 71 | if root == nil { 72 | return false 73 | } 74 | 75 | if root.Value == sum && root.Left == nil && root.Right == nil { 76 | return true 77 | } 78 | 79 | return pathSum(root.Left, sum-root.Value) || pathSum(root.Right, sum-root.Value) 80 | } 81 | -------------------------------------------------------------------------------- /leetcode/valid_parentheses_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of parentheses, determine if it is valid. 4 | 5 | Example: 6 | - Input: []string{"(", ")", "[", "]", "{", "}"} 7 | Output: true 8 | - Input: []string{"(", "[", ")", "]"} 9 | Output: false 10 | 11 | Approach: 12 | - Use a stack push the opening parenthesis and pop the last inserted one when 13 | we encounter a closing parenthesis and check if it is a valid match. 14 | 15 | Cost: 16 | - O(n) time, O(n) space. 17 | */ 18 | 19 | package leetcode 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestIsValid(t *testing.T) { 28 | tests := []struct { 29 | in []string 30 | expected bool 31 | }{ 32 | {[]string{}, true}, 33 | } 34 | 35 | for _, tt := range tests { 36 | common.Equal( 37 | t, 38 | tt.expected, 39 | isValid(tt.in), 40 | ) 41 | } 42 | } 43 | 44 | func isValid(s []string) bool { 45 | m := map[string]string{ 46 | "(": ")", 47 | "{": "}", 48 | "[": "]", 49 | } 50 | 51 | stack := newStack() 52 | 53 | for _, p := range s { 54 | // if p is an opening parenthesis, push it to the stack. otherwise, if 55 | // is a closing parenthesis, pop the stack and check if they're matching 56 | if _, ok := m[p]; ok { 57 | stack.push(p) 58 | } else if stack.size() == 0 || m[stack.pop()] != p { 59 | return false 60 | } 61 | } 62 | 63 | return stack.size() == 0 64 | } 65 | 66 | // stack represent a stack data structure. 67 | type stack struct { 68 | items []string 69 | } 70 | 71 | // newStack return a new Stack. 72 | func newStack() *stack { 73 | items := []string{} 74 | return &stack{items: items} 75 | } 76 | 77 | // push a new item onto the stack. 78 | func (s *stack) push(v string) { 79 | s.items = append(s.items, v) 80 | } 81 | 82 | // pop remove and return the last item. 83 | func (s *stack) pop() string { 84 | // get the last item. 85 | i := len(s.items) - 1 86 | top := s.items[i] 87 | 88 | // remove the last item. 89 | s.items = s.items[:i] 90 | 91 | return top 92 | } 93 | 94 | // size return the stack's size. 95 | func (s *stack) size() int { 96 | return len(s.items) 97 | } 98 | -------------------------------------------------------------------------------- /gtci/reorder_list_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given the head of a singly linked list, write a function to reorder it 4 | such that nodes from the second half are inserted alternately to the 5 | nodes from the first half in reverse order. 6 | 7 | Approach: 8 | - Similar to palindrome linked list problem, can also use a trick to 9 | reverse the second half and rearrange them in the required order 10 | using fast and slow pointers. 11 | 12 | Cost: 13 | - O(n) time, O(1) space. 14 | */ 15 | 16 | package gtci 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/hoanhan101/algo/common" 22 | ) 23 | 24 | func TestReorder(t *testing.T) { 25 | t1 := common.NewListNode(1) 26 | t1.AddNext(1) 27 | t1.AddNext(1) 28 | t1.AddNext(1) 29 | t1.AddNext(1) 30 | 31 | t2 := common.NewListNode(1) 32 | t2.AddNext(2) 33 | t2.AddNext(3) 34 | t2.AddNext(4) 35 | t2.AddNext(5) 36 | 37 | t3 := common.NewListNode(1) 38 | t3.AddNext(2) 39 | t3.AddNext(3) 40 | t3.AddNext(4) 41 | t3.AddNext(5) 42 | t3.AddNext(6) 43 | 44 | tests := []struct { 45 | in *common.ListNode 46 | expected []int 47 | }{ 48 | {t1, []int{1, 1, 1, 1, 1}}, 49 | {t2, []int{1, 5, 2, 4, 3}}, 50 | {t3, []int{1, 6, 2, 5, 3, 4}}, 51 | } 52 | 53 | for _, tt := range tests { 54 | reorder(tt.in) 55 | common.Equal( 56 | t, 57 | tt.expected, 58 | common.LinkedListToSlice(tt.in), 59 | ) 60 | } 61 | } 62 | 63 | func reorder(head *common.ListNode) { 64 | slow, fast := head, head 65 | 66 | // find the middle of the linked list. 67 | for fast != nil && fast.Next != nil { 68 | slow = slow.Next 69 | fast = fast.Next.Next 70 | 71 | } 72 | 73 | // reverse a half. 74 | headSecondHalf := reverse(slow) 75 | headFirstHalf := head 76 | 77 | // compare the first and second half. 78 | for headFirstHalf != nil && headSecondHalf != nil { 79 | tmp := headFirstHalf.Next 80 | headFirstHalf.Next = headSecondHalf 81 | headFirstHalf = tmp 82 | 83 | tmp = headSecondHalf.Next 84 | headSecondHalf.Next = headFirstHalf 85 | headSecondHalf = tmp 86 | } 87 | 88 | // nil the last node's next pointer. 89 | if headFirstHalf != nil { 90 | headFirstHalf.Next = nil 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /interviewcake/kth_to_last_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Find the kth to last node in a linked list. 4 | 5 | Example: 6 | - Input: list = 1 -> 2 -> 3 -> 4 -> 5 -> 6, k = 2 7 | Output: 5, because 5 is the 2nd to the last node (6) 8 | 9 | Approach: 10 | - Use two pointers such that one starts at the beginning and the other one 11 | starts at k distance apart. 12 | - Walk both at the same speed toward the end. 13 | - When one hits the tail, the other one is on the target node. 14 | 15 | Solution: 16 | - Start both nodes, a left one and a right one, at the beginning. 17 | - Move the right one to the kth node. 18 | - Move both of them until the right one hits the end. 19 | - Return the left one. 20 | 21 | Cost: 22 | - O(n) time and O(1) space. 23 | */ 24 | 25 | package interviewcake 26 | 27 | import ( 28 | "testing" 29 | 30 | "github.com/hoanhan101/algo/common" 31 | ) 32 | 33 | func TestKthToLast(t *testing.T) { 34 | // define tests input. 35 | 36 | t1 := common.NewListNode(1) 37 | 38 | t2 := common.NewListNode(1) 39 | t2.AddNext(2) 40 | 41 | t3 := common.NewListNode(1) 42 | t3.AddNext(2) 43 | t3.AddNext(3) 44 | 45 | t4 := common.NewListNode(1) 46 | for i := 2; i <= 6; i++ { 47 | t4.AddNext(i) 48 | } 49 | 50 | // define tests output. 51 | tests := []struct { 52 | in1 *common.ListNode 53 | in2 int 54 | expected int 55 | }{ 56 | {t1, 1, 1}, 57 | {t2, 1, 2}, 58 | {t2, 2, 1}, 59 | {t3, 1, 3}, 60 | {t3, 2, 2}, 61 | {t3, 3, 1}, 62 | {t4, 1, 6}, 63 | {t4, 2, 5}, 64 | {t4, 3, 4}, 65 | {t4, 4, 3}, 66 | {t4, 5, 2}, 67 | {t4, 6, 1}, 68 | } 69 | 70 | for _, tt := range tests { 71 | node := kthToLast(tt.in1, tt.in2) 72 | common.Equal(t, tt.expected, node.Value) 73 | } 74 | } 75 | 76 | func kthToLast(node *common.ListNode, k int) *common.ListNode { 77 | // start both node in the beginning. 78 | left, right := node, node 79 | 80 | // move the right one to the kth node. 81 | for i := 0; i < k-1; i++ { 82 | right = right.Next 83 | } 84 | 85 | // move both pointers until the right one hits the end. 86 | for right.Next != nil { 87 | left = left.Next 88 | right = right.Next 89 | } 90 | 91 | return left 92 | } 93 | -------------------------------------------------------------------------------- /interviewcake/largest_stack_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement a stack with a method getMax() that returns the largest element in 4 | the stack in O(1) time. 5 | 6 | Approach: 7 | - We use two stack implementation themselves: one holds all the items and the 8 | other holds all the maximum values after each push() and pop(). 9 | - That way, we could keep track of your maximum value up to date in constant 10 | time. 11 | 12 | Cost: 13 | - O(1) time, O(m) space where m is the number of operations performed on the 14 | stack. 15 | */ 16 | 17 | package interviewcake 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/hoanhan101/algo/common" 23 | ) 24 | 25 | func TestMaxStack(t *testing.T) { 26 | s := newMaxStack() 27 | 28 | s.push(2) 29 | common.Equal(t, 2, s.getMax()) 30 | 31 | s.push(6) 32 | common.Equal(t, 6, s.getMax()) 33 | 34 | s.push(8) 35 | common.Equal(t, 8, s.getMax()) 36 | 37 | s.push(4) 38 | common.Equal(t, 8, s.getMax()) 39 | 40 | common.Equal(t, 4, s.pop()) 41 | common.Equal(t, 8, s.getMax()) 42 | common.Equal(t, 8, s.pop()) 43 | common.Equal(t, 6, s.getMax()) 44 | common.Equal(t, 6, s.pop()) 45 | common.Equal(t, 2, s.getMax()) 46 | common.Equal(t, 2, s.pop()) 47 | } 48 | 49 | type maxStack struct { 50 | stack *common.Stack 51 | maxStack *common.Stack 52 | } 53 | 54 | func newMaxStack() *maxStack { 55 | s := common.NewStack() 56 | ms := common.NewStack() 57 | 58 | return &maxStack{ 59 | stack: s, 60 | maxStack: ms, 61 | } 62 | } 63 | 64 | func (s *maxStack) push(i int) { 65 | // push the item on top of our stack. 66 | s.stack.Push(i) 67 | 68 | // only push the item to the maxStack if it greater or equal to the last 69 | // item in maxStack. 70 | if s.maxStack.Size() == 0 || i >= s.maxStack.Top().(int) { 71 | s.maxStack.Push(i) 72 | } 73 | } 74 | 75 | func (s *maxStack) pop() int { 76 | // pop the item of our stack. 77 | i := s.stack.Pop().(int) 78 | 79 | // if popped item is the largest item, also remove that from maxStack. 80 | if i == s.maxStack.Top().(int) { 81 | s.maxStack.Pop() 82 | } 83 | 84 | return i 85 | } 86 | 87 | func (s *maxStack) getMax() int { 88 | return s.maxStack.Top().(int) 89 | } 90 | -------------------------------------------------------------------------------- /lab/merge_sort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement merge sort. 4 | 5 | Approach: 6 | - Split the input in half, recursively sorts each half, then merge the 7 | sorted halves back together. 8 | 9 | Cost: 10 | - O(nlogn) time and O(n) space. 11 | */ 12 | 13 | package lab 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestMergeSort(t *testing.T) { 22 | tests := []struct { 23 | in []int 24 | expected []int 25 | }{ 26 | {[]int{}, []int{}}, 27 | {[]int{1}, []int{1}}, 28 | {[]int{1, 2}, []int{1, 2}}, 29 | {[]int{2, 1}, []int{1, 2}}, 30 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 31 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 32 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 33 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 34 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | result := mergeSort(tt.in) 40 | common.Equal(t, tt.expected, result) 41 | } 42 | } 43 | 44 | func mergeSort(in []int) []int { 45 | // base case 46 | if len(in) <= 1 { 47 | return in 48 | } 49 | 50 | // split the input in half. 51 | middleIndex := len(in) / 2 52 | left := in[:middleIndex] 53 | right := in[middleIndex:] 54 | 55 | // sort each half. 56 | leftSorted := mergeSort(left) 57 | rightSorted := mergeSort(right) 58 | 59 | // merge the sorted halves. 60 | return mergeSortedArray(leftSorted, rightSorted) 61 | } 62 | 63 | func mergeSortedArray(a1, a2 []int) []int { 64 | out := []int{} 65 | 66 | // keep two "pointer" at index 0 and move up accordingly as one get 67 | // merged in. 68 | i, j := 0, 0 69 | for i < len(a1) && j < len(a2) { 70 | if a1[i] < a2[j] { 71 | out = append(out, a1[i]) 72 | i++ 73 | } else { 74 | out = append(out, a2[j]) 75 | j++ 76 | } 77 | } 78 | 79 | // if we get here, one array must have bigger size than the other. could 80 | // figure out which one is it then copy the rest of its to our final one. 81 | if i < len(a1) { 82 | out = append(out, a1[i:]...) 83 | } 84 | 85 | if j < len(a2) { 86 | out = append(out, a2[j:]...) 87 | } 88 | 89 | return out 90 | } 91 | -------------------------------------------------------------------------------- /gtci/smallest_subarray_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of positive numbers and a positive number s, find the length 4 | of the smallest contiguous subarray whose sum is greater than or equal to s. 5 | 6 | Example: 7 | - Input: array=int{2, 1, 5, 2, 3, 2}, s=7 8 | Output: 2 9 | Explanation: Smallest subarray with a sum great than or equal to 7 is [5, 2] 10 | with length=2. 11 | 12 | Approach: 13 | - The difference between the previous problem and this one is that the size of 14 | the sliding window is not fixed. 15 | - Can still use the similar strategy to add up elements until their sum is greater 16 | than equal to s and view them as our sliding window. 17 | - Shrink the window until the window's sum is smaller than s again while keep 18 | updating the minimum length. 19 | 20 | Cost: 21 | - O(n) time, O(1) space. 22 | */ 23 | 24 | package gtci 25 | 26 | import ( 27 | "math" 28 | "testing" 29 | 30 | "github.com/hoanhan101/algo/common" 31 | ) 32 | 33 | func TestMinSubarray(t *testing.T) { 34 | tests := []struct { 35 | in1 []int 36 | in2 int 37 | expected int 38 | }{ 39 | {[]int{}, 1, 0}, 40 | {[]int{2, 1, 5, 2, 3, 2}, 7, 2}, 41 | {[]int{2, 1, 5, 2, 3, 2}, 8, 3}, 42 | } 43 | 44 | for _, tt := range tests { 45 | common.Equal( 46 | t, 47 | tt.expected, 48 | minSubarray(tt.in1, tt.in2), 49 | ) 50 | } 51 | } 52 | 53 | func minSubarray(a []int, s int) int { 54 | minLength := math.MaxInt64 55 | 56 | // sum keeps track of the sum of a window while start keeps track of 57 | // its start index. 58 | sum, start := 0, 0 59 | 60 | for end := range a { 61 | sum += a[end] 62 | 63 | // shrink the window until the window's sum is smaller than the 64 | // target sum. 65 | for sum >= s { 66 | // update the minimum length at each step. 67 | minLength = common.Min(minLength, end-start+1) 68 | 69 | // subtract the start element and increase the start index to move 70 | // the window ahead by one element. 71 | sum -= a[start] 72 | start++ 73 | } 74 | } 75 | 76 | // let min=0 if there is no such subarray exists. 77 | if minLength == math.MaxInt64 { 78 | minLength = 0 79 | } 80 | 81 | return minLength 82 | } 83 | -------------------------------------------------------------------------------- /gtci/intervals_intersection_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given two sorted lists of intervals, find the intersection between them. 4 | 5 | Example: 6 | - Input: []interval{{1, 3}, {5, 6}, {7, 9}}, []interval{{2, 3}, {5, 7}} 7 | Output: []interval{{2, 3}, {5, 6}, {7, 7}} 8 | 9 | Approach: 10 | - Iterate through both list at the same time and check if two intervals 11 | are overlapping at each step. 12 | - Opposite to the merging process, an overlapped interval has: 13 | - a bigger start between the two intervals 14 | - a smaller end between the two intervals 15 | 16 | Cost: 17 | - O(n) time, O(n) space. 18 | */ 19 | 20 | package gtci 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestIntersectIntervals(t *testing.T) { 29 | tests := []struct { 30 | in1 []interval 31 | in2 []interval 32 | expected []interval 33 | }{ 34 | {[]interval{}, []interval{}, []interval{}}, 35 | {[]interval{{1, 3}, {5, 6}, {7, 9}}, []interval{{2, 3}, {5, 7}}, []interval{{2, 3}, {5, 6}, {7, 7}}}, 36 | {[]interval{{1, 3}, {5, 7}, {9, 12}}, []interval{{5, 10}}, []interval{{5, 7}, {9, 10}}}, 37 | } 38 | 39 | for _, tt := range tests { 40 | result := intersectIntervals(tt.in1, tt.in2) 41 | common.Equal(t, tt.expected, result) 42 | } 43 | } 44 | 45 | func intersectIntervals(intervals1, intervals2 []interval) []interval { 46 | out := []interval{} 47 | i, j := 0, 0 48 | 49 | for i < len(intervals1) && j < len(intervals2) { 50 | // if two intervals are overlapping, insert an interval that has a 51 | // bigger start between the two intervals and a smaller end between them. 52 | if overlap(intervals1[i], intervals2[j]) || overlap(intervals2[j], intervals1[i]) { 53 | out = append( 54 | out, 55 | interval{ 56 | common.Max(intervals1[i].start, intervals2[j].start), 57 | common.Min(intervals1[i].end, intervals2[j].end), 58 | }, 59 | ) 60 | } 61 | 62 | // move the pointer up accordingly. 63 | if intervals1[i].end < intervals2[j].end { 64 | i++ 65 | } else { 66 | j++ 67 | } 68 | } 69 | 70 | return out 71 | } 72 | 73 | func overlap(in1, in2 interval) bool { 74 | return in1.start >= in2.start && in1.start <= in2.end 75 | } 76 | -------------------------------------------------------------------------------- /interviewcake/bracket_validator_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, determine if its brackets are properly nested. 4 | 5 | Example: 6 | - Input: "{[]()}" 7 | Output: true 8 | - Input: "{[(])}" 9 | Output: false 10 | - Input: "{[}" 11 | Output: false 12 | 13 | Approach: 14 | - Use a stack to keep track of matching parenthesis as we iterate 15 | through the string. 16 | 17 | Solution: 18 | - Iterate through the string, keep a stack to keep track of closing and 19 | opening parenthesis. 20 | - If we see an opener, push it to the stack. 21 | - If we see an closer, pop from the stack if is the one for the opener at the 22 | stop of stack. 23 | 24 | Cost: 25 | - O(n) time and O(n) space, where n is the number of operations. 26 | */ 27 | 28 | package interviewcake 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestValidateBracket(t *testing.T) { 37 | // define test cases. 38 | tests := []struct { 39 | sentence string 40 | expected bool 41 | }{ 42 | {"{[]()}", true}, 43 | {"{[(])}", false}, 44 | {"{[}", false}, 45 | } 46 | 47 | for _, tt := range tests { 48 | result := validateBracket(tt.sentence) 49 | common.Equal(t, tt.expected, result) 50 | } 51 | } 52 | 53 | func validateBracket(sentence string) bool { 54 | // mapping of opening and closing brackets. 55 | m := map[string]string{ 56 | "(": ")", 57 | "{": "}", 58 | "[": "]", 59 | } 60 | 61 | stack := []string{} 62 | 63 | for _, char := range sentence { 64 | // if it is an opener, push to the stack. 65 | if common.ContainString([]string{"(", "{", "["}, string(char)) { 66 | stack = append(stack, string(char)) 67 | } else if common.ContainString([]string{")", "}", "]"}, string(char)) { 68 | // return false if the stack is empty. 69 | if len(stack) == 0 { 70 | return false 71 | } 72 | 73 | // pop to get the last item from the stack. 74 | lastUnclosed := stack[len(stack)-1] 75 | stack = stack[:len(stack)-1] 76 | 77 | // return false if the last opening bracket doesn't match the 78 | // current closing one. 79 | if m[lastUnclosed] != string(char) { 80 | return false 81 | } 82 | } 83 | } 84 | 85 | return len(stack) == 0 86 | } 87 | -------------------------------------------------------------------------------- /interviewcake/product_of_others_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of integers, return a corresponding list where every index 4 | holds the product of every other values except the value in that index. 5 | And, you can't use division! 6 | 7 | Example: 8 | - Input: []int{1, 7, 3, 4} 9 | Output: []int{84, 12, 28, 21} 10 | 11 | Approach: 12 | - Iterate through the list and at each step, calculate the product of all 13 | the integers before each index and the product of all the integers after 14 | each index. 15 | 16 | Solution: 17 | - Iterate through the list from left to right and get the product of all 18 | other numbers before their indices. 19 | - Iterate through the list from right to left and get the product of all 20 | other numbers after their indices. 21 | 22 | Example walkthrough: 23 | - After the first iteration, we end up with: 24 | []int{1*1, 1*1, 1*7, 1*7*3} or []int{1, 1, 7, 21} 25 | - After the second iteration, we end up with: 26 | []int{1*4*3*7, 1*4*3, 7*4, 21*1} or []{84, 12, 28, 21} 27 | 28 | Cost: 29 | - O(n) time, O(n) space. 30 | */ 31 | 32 | package interviewcake 33 | 34 | import ( 35 | "testing" 36 | 37 | "github.com/hoanhan101/algo/common" 38 | ) 39 | 40 | func TestGetProductOfOthers(t *testing.T) { 41 | tests := []struct { 42 | in []int 43 | expected []int 44 | }{ 45 | {[]int{}, []int{}}, 46 | {[]int{1, 7, 3, 4}, []int{84, 12, 28, 21}}, 47 | {[]int{2, 4, 10}, []int{40, 20, 8}}, 48 | {[]int{2, 4, 0}, []int{0, 0, 8}}, 49 | } 50 | 51 | for _, tt := range tests { 52 | result := getProductOfOthers(tt.in) 53 | common.Equal(t, tt.expected, result) 54 | } 55 | } 56 | 57 | func getProductOfOthers(list []int) []int { 58 | if len(list) < 2 { 59 | return []int{} 60 | } 61 | 62 | out := make([]int, len(list)) 63 | 64 | // get product of all other numbers before their indices. 65 | start1 := 1 66 | for i := 0; i < len(list); i++ { 67 | out[i] = start1 68 | start1 *= list[i] 69 | } 70 | 71 | // get product of all other numbers after their indices then multiply them 72 | // with their corresponding values. 73 | start2 := 1 74 | for i := len(list) - 1; i > -1; i-- { 75 | out[i] *= start2 76 | start2 *= list[i] 77 | } 78 | 79 | return out 80 | } 81 | -------------------------------------------------------------------------------- /interviewcake/top_scores_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an unsorted list scores (integer) and a highest possible score 4 | (integer), return a sorted list utilizing that fact. 5 | 6 | Example: 7 | - Input: []int{37, 89, 41, 65, 91, 53}, 100 8 | Output: []int{91, 89, 65, 53, 41, 37} 9 | 10 | Approach: 11 | - Utilize the highest score to allocate a fix-sized list ahead of time where 12 | where its indices represent the scores themselves and its values represent 13 | how many time these scores appear in the list. 14 | 15 | Solution: 16 | - Utilize the highest score to allocate a fix-sized list ahead of time. 17 | - Iterate through the unsorted list and populate the scores where indices 18 | represent scores and values represent how many time the score appears. 19 | - Iterate backward and add the score to the output list so that it is 20 | sorted from highest to lowest. 21 | 22 | Cost: 23 | - O(n) time, O(n) space. 24 | */ 25 | 26 | package interviewcake 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestSortScores(t *testing.T) { 35 | tests := []struct { 36 | in1 []int 37 | in2 int 38 | expected []int 39 | }{ 40 | {[]int{}, 0, []int{}}, 41 | {[]int{37, 89, 41, 65, 91, 53}, 100, []int{91, 89, 65, 53, 41, 37}}, 42 | {[]int{37, 89, 41, 65, 91, 53, 65}, 100, []int{91, 89, 65, 65, 53, 41, 37}}, 43 | } 44 | for _, tt := range tests { 45 | result := sortScores(tt.in1, tt.in2) 46 | common.Equal(t, tt.expected, result) 47 | } 48 | } 49 | 50 | func sortScores(scores []int, highestPossibleScore int) []int { 51 | // utilize highest score to create a fix-sized list of score counts. 52 | counts := make([]int, highestPossibleScore+1) 53 | out := make([]int, 0) 54 | 55 | // populate the scores where indices represent scores and values represent 56 | // how many time the score appears. 57 | for _, score := range scores { 58 | counts[score]++ 59 | } 60 | 61 | // iterate backward and add the score to the output list so that it is 62 | // sorted from highest to lowest. 63 | for i := len(counts) - 1; i > -1; i-- { 64 | count := counts[i] 65 | 66 | for j := 0; j < count; j++ { 67 | out = append(out, i) 68 | } 69 | } 70 | 71 | return out 72 | } 73 | -------------------------------------------------------------------------------- /leetcode/reverse_integer_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a 64-bit integer, reverse its digits. 4 | 5 | Assumption: 6 | - Negative numbers are also valid. 7 | - Must handle the case where the reversed integer is overflow. 8 | 9 | Example: 10 | - Input: 123 11 | Output: 321 12 | - Input: -123 13 | Output: -321 14 | - Input: 8085774586302733229 Output: 0 15 | Explanation: The reversed integer 9223372036854775808 overflows by 1 so we return 0. 16 | 17 | Approach: 18 | - Use modulo by 10 to get a digit at ones' place of the input and 19 | dividing by 10 to shift it to the right (eliminate the ones' place). 20 | 21 | Solution: 22 | - Initialize the output as a 64-bit integer. 23 | - If the input is not zero yet, keep multiply the output and diving the 24 | input by 10 so that: output = (output * 10) + (input % 10) and 25 | input = input / 10. 26 | - Also, remember to check the overflow/underflow of the output and return 27 | 0 if necessary. 28 | 29 | Cost: 30 | - O(m) time, O(1) space, where m is log10 of the input. 31 | */ 32 | 33 | package leetcode 34 | 35 | import ( 36 | "math" 37 | "testing" 38 | 39 | "github.com/hoanhan101/algo/common" 40 | ) 41 | 42 | func TestReverseInteger(t *testing.T) { 43 | tests := []struct { 44 | in int64 45 | expected int64 46 | }{ 47 | {0, 0}, 48 | {1, 1}, 49 | {10, 1}, 50 | {12, 21}, 51 | {123, 321}, 52 | {-123, -321}, 53 | {8085774586302733229, 0}, 54 | {7085774586302733229, 9223372036854775807}, 55 | {1085774586302733229, 9223372036854775801}, 56 | {-8085774586302733229, -9223372036854775808}, 57 | {-9085774586302733229, 0}, 58 | } 59 | 60 | for _, tt := range tests { 61 | result := reverseInteger(tt.in) 62 | common.Equal(t, tt.expected, result) 63 | } 64 | } 65 | 66 | func reverseInteger(in int64) int64 { 67 | var out, remainder int64 68 | 69 | for in != 0 { 70 | remainder = in % 10 71 | 72 | // check for overflow/underflow before multiplying by 10. 73 | if out > math.MaxInt64/10 || (out == math.MaxInt64/10 && remainder > 7) { 74 | return 0 75 | } 76 | if out < math.MinInt64/10 || (out == math.MinInt64/10 && remainder < -8) { 77 | return 0 78 | } 79 | 80 | out = out*10 + remainder 81 | in /= 10 82 | } 83 | 84 | return out 85 | } 86 | -------------------------------------------------------------------------------- /interviewcake/apple_stocks_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of stock prices (integer) in chronological order, return the 4 | max profit from buying at earlier time and selling at later time. 5 | 6 | Example: 7 | - Input: []int{10, 7, 5, 8, 11, 9} 8 | Output: 6, because one can buy at 5 and sell at 11 9 | 10 | Approach: 11 | - Use a greedy approach to keep track of the minimum price and the maximum 12 | profit for each value while iterating through the list. 13 | 14 | Solution: 15 | - Initialize a minimum price to be the first price in the list and a maximum 16 | profit to be the first possible profit that we could trade. 17 | - Iterate through the list and keep track of: 18 | - the current price 19 | - the potential profit: current price - minimum price 20 | - the maximum profit: the larger profit between the current maximum and the potential one 21 | - the minimum price: the smaller price between the current minimum and the current price. 22 | - Return the maximum profit we found in the end. 23 | 24 | Cost: 25 | - O(n) time, O(1) space. 26 | */ 27 | 28 | package interviewcake 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestGetMaxProfit(t *testing.T) { 37 | tests := []struct { 38 | in []int 39 | expected int 40 | }{ 41 | {[]int{}, 0}, 42 | {[]int{10}, 0}, 43 | {[]int{10, 10}, 0}, 44 | {[]int{10, 7, 5, 8, 11, 9}, 6}, 45 | {[]int{10, 2, 5, 4, 7, 1}, 5}, 46 | {[]int{10, 7, 2, 1}, -1}, 47 | } 48 | 49 | for _, tt := range tests { 50 | result := getMaxProfit(tt.in) 51 | common.Equal(t, tt.expected, result) 52 | } 53 | } 54 | 55 | func getMaxProfit(stocks []int) int { 56 | // return 0 if the input is invalid 57 | if len(stocks) <= 2 { 58 | return 0 59 | } 60 | 61 | // initialize minPrice to be the first price in the list and maxProfit to 62 | // be the first possible profit that we could trade. 63 | minPrice := stocks[0] 64 | maxProfit := stocks[1] - stocks[0] 65 | 66 | for i := 1; i < len(stocks); i++ { 67 | currentPrice := stocks[i] 68 | potentialProfit := currentPrice - minPrice 69 | maxProfit = common.Max(potentialProfit, maxProfit) 70 | minPrice = common.Min(minPrice, currentPrice) 71 | } 72 | 73 | return maxProfit 74 | } 75 | -------------------------------------------------------------------------------- /gtci/fibonacci_numbers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Calculate the nth Fibonacci numbers. 4 | 5 | Example: 6 | - Input: 5 7 | Output: 5 8 | - Input: 6 9 | Output: 8 10 | - Input: 7 11 | Output: 13 12 | 13 | Cost: 14 | - Brute-force: O(n^2) time, O(n) space. 15 | - Top-down: O(n) time, O(n) space. 16 | - Bottom-up: O(n) time, O(1) space. 17 | */ 18 | 19 | package gtci 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/hoanhan101/algo/common" 25 | ) 26 | 27 | func TestCalculateFibonacci(t *testing.T) { 28 | tests := []struct { 29 | in int 30 | expected int 31 | }{ 32 | {0, 0}, 33 | {1, 1}, 34 | {2, 1}, 35 | {3, 2}, 36 | {4, 3}, 37 | {5, 5}, 38 | {6, 8}, 39 | {7, 13}, 40 | {8, 21}, 41 | } 42 | 43 | for _, tt := range tests { 44 | common.Equal( 45 | t, 46 | tt.expected, 47 | calculateFibBF(tt.in), 48 | ) 49 | 50 | common.Equal( 51 | t, 52 | tt.expected, 53 | calculateFibTD(tt.in), 54 | ) 55 | 56 | common.Equal( 57 | t, 58 | tt.expected, 59 | calculateFibBU(tt.in), 60 | ) 61 | } 62 | } 63 | 64 | func calculateFibBF(n int) int { 65 | if n < 2 { 66 | return n 67 | } 68 | 69 | return calculateFibBF(n-1) + calculateFibBF(n-2) 70 | } 71 | 72 | func calculateFibTD(n int) int { 73 | // memo caches the calculated values in an array where each index represents 74 | // the nth number and its index represents the calculated value. 75 | memo := make([]int, n+1) 76 | return memoRecur(memo, n) 77 | } 78 | 79 | func memoRecur(memo []int, n int) int { 80 | if n < 2 { 81 | return n 82 | } 83 | 84 | // return the value immediately if we've already calculated it 85 | if memo[n] != 0 { 86 | return memo[n] 87 | } 88 | 89 | memo[n] = memoRecur(memo, n-1) + memoRecur(memo, n-2) 90 | 91 | return memo[n] 92 | } 93 | 94 | func calculateFibBU(n int) int { 95 | if n < 2 { 96 | return n 97 | } 98 | 99 | // initialize n1, n2 as the first 2 numbers in the Fibonacci sequence and 100 | // use them to cache the last two as we go. 101 | n1, n2, tmp := 0, 1, 0 102 | for i := 2; i < n+1; i++ { 103 | tmp = n1 + n2 104 | n1 = n2 105 | n2 = tmp 106 | } 107 | 108 | return n2 109 | } 110 | -------------------------------------------------------------------------------- /interviewcake/parenthesis_matching_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a sentence as string, and the position of an opening parenthesis 4 | position, find the matching closing one position. 5 | 6 | Example: 7 | - Input: "I ((like) (nesting) parenthesis)", opening parenthesis position = 2 8 | Output: 31, because the matching parenthesis of the one in position 2 is at 9 | index 31. 10 | 11 | Approach: 12 | - Iterate through the string and keep a count of matching parenthesis at each 13 | step. 14 | 15 | Solution: 16 | - Return -1 if the string is empty or the opening parenthesis position is 17 | larger than the string size. 18 | - Iterate through the string, starting at the opening parenthesis position and 19 | keep a count of it. 20 | - Increase the count as we meet the opening parenthesis and decrement it as we 21 | meet the closing parenthesis. 22 | - When the count if 0, it is the matching end. 23 | 24 | Cost: 25 | - O(n) time, O(1) space. 26 | */ 27 | 28 | package interviewcake 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestMatchParenthesis(t *testing.T) { 37 | // define test cases.. 38 | tests := []struct { 39 | sentence string 40 | startIndex int 41 | expected int 42 | }{ 43 | {"", 0, -1}, 44 | {"", 1, -1}, 45 | {"(", 0, -1}, 46 | {"()", 0, 1}, 47 | {"())", 0, 1}, 48 | {"(()", 0, -1}, 49 | {"I (like (nesting) parenthesis)", 8, 16}, 50 | {"I ((like) (nesting) parenthesis)", 2, 31}, 51 | } 52 | 53 | for _, tt := range tests { 54 | common.Equal(t, tt.expected, matchParenthesis(tt.sentence, tt.startIndex)) 55 | } 56 | } 57 | 58 | func matchParenthesis(sentence string, startIndex int) int { 59 | // return -1 if the string is empty or the opening parenthesis position is 60 | // larger than the string size. 61 | if len(sentence) == 0 || startIndex > len(sentence) { 62 | return -1 63 | } 64 | 65 | count := 0 66 | for i := startIndex + 1; i < len(sentence); i++ { 67 | char := sentence[i] 68 | 69 | if string(char) == "(" { 70 | count++ 71 | } else if string(char) == ")" { 72 | if count == 0 { 73 | return i 74 | } 75 | 76 | count-- 77 | } 78 | } 79 | 80 | // return -1 if there is no matching parenthesis. 81 | return -1 82 | } 83 | -------------------------------------------------------------------------------- /leetcode/plus_one_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a number represented as an array of digits, plus one to the number. 4 | 5 | Assumption: 6 | - The input are non-negative. 7 | - The digits are stored such that the most significant digit is at the head of the list. 8 | - The number does not contain leading zeros. 9 | 10 | Example: 11 | - Input: []int{1, 2, 5} 12 | Output: []int{1, 2, 6} 13 | - Input: []int{1, 2, 9} 14 | Output: []int{1, 3, 0} 15 | - Input: []int{1, 9, 9} 16 | Output: []int{2, 0, 0} 17 | 18 | Solution: 19 | - Iterate through the list from right to left and add 1 to the current digit accordingly. 20 | - If the current digit is less than 9, add 1 and update it. 21 | - Otherwise, set it to 0. 22 | - If all the digits are 9, append an 0 in the end and update the first digit to 1. 23 | 24 | Cost: 25 | - O(n) time, O(1) space, where n is the length of the list. 26 | */ 27 | 28 | package leetcode 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestPlusOne(t *testing.T) { 37 | tests := []struct { 38 | in []int 39 | expected []int 40 | }{ 41 | {[]int{}, []int{}}, 42 | {[]int{0}, []int{1}}, 43 | {[]int{1}, []int{2}}, 44 | {[]int{9}, []int{1, 0}}, 45 | {[]int{1, 2, 5}, []int{1, 2, 6}}, 46 | {[]int{1, 2, 9}, []int{1, 3, 0}}, 47 | {[]int{1, 9, 9}, []int{2, 0, 0}}, 48 | {[]int{9, 9, 9}, []int{1, 0, 0, 0}}, 49 | } 50 | 51 | for _, tt := range tests { 52 | result := plusOne(tt.in) 53 | common.Equal(t, tt.expected, result) 54 | } 55 | } 56 | 57 | func plusOne(digits []int) []int { 58 | // check edge case to see if the input is empty. 59 | if len(digits) == 0 { 60 | return digits 61 | } 62 | 63 | // counter counts the number of 9 appears in the original input. 64 | counter := 0 65 | 66 | // iterate from right to left and add 1 to the current digit accordingly. 67 | for i := len(digits) - 1; i > -1; i-- { 68 | d := digits[i] 69 | 70 | if d < 9 { 71 | digits[i] = d + 1 72 | break 73 | } else { 74 | digits[i] = 0 75 | counter++ 76 | } 77 | } 78 | 79 | // if all digits are 9, add an 0 in the end and change the digit to 1. 80 | if counter == len(digits) { 81 | digits = append(digits, 0) 82 | digits[0] = 1 83 | } 84 | 85 | return digits 86 | } 87 | -------------------------------------------------------------------------------- /leetcode/reverse_words_string_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, reverse it word by word. 4 | 5 | Example: 6 | - Input: "hard so be to have not does interview coding" 7 | Output: "coding interview does not have to be so hard" 8 | 9 | Approach: 10 | - Approach with a two-pass solution. 11 | - The first pass is to split the string into an array of words separated by 12 | spaces. 13 | - The second pass is to reverse the order of words in the array by using 14 | two-pointer approach: swap two values on both ends as we move toward the 15 | middle. 16 | - Concatenate the values of ordered array to create a final string. 17 | 18 | Cost: 19 | - O(n) time, O(n) space. 20 | */ 21 | 22 | package leetcode 23 | 24 | import ( 25 | "strings" 26 | "testing" 27 | 28 | "github.com/hoanhan101/algo/common" 29 | ) 30 | 31 | func TestReverseWordsString(t *testing.T) { 32 | tests := []struct { 33 | in string 34 | expected string 35 | }{ 36 | {"", ""}, 37 | {"a", "a"}, 38 | {" a", "a"}, 39 | {"a ", "a"}, 40 | {"b a", "a b"}, 41 | {"c b a", "a b c"}, 42 | {"d c b a", "a b c d"}, 43 | {"hard so be to have not does interview coding", "coding interview does not have to be so hard"}, 44 | } 45 | 46 | for _, tt := range tests { 47 | result := reverseWordsString(tt.in) 48 | common.Equal(t, tt.expected, result) 49 | } 50 | } 51 | 52 | func reverseWordsString(in string) string { 53 | // if the input is an empty string, return itself. 54 | if in == "" { 55 | return in 56 | } 57 | 58 | // slice input string into a list of words separated by spaces. 59 | words := strings.Split(in, " ") 60 | 61 | // clean up leading and trailing space if there is any. 62 | if words[0] == "" { 63 | words = words[1:] 64 | } 65 | 66 | if words[len(words)-1] == "" { 67 | words = words[:len(words)-1] 68 | } 69 | 70 | // start reversing the order of words by first initializing the start and 71 | // end index pointer. 72 | start := 0 73 | end := len(words) - 1 74 | 75 | for start < end { 76 | // swap 2 character using a temp variable. 77 | common.SwapString(words, start, end) 78 | 79 | // move the cursor toward the middle. 80 | start++ 81 | end-- 82 | } 83 | 84 | // return the concatenated string created from words. 85 | return strings.Join(words, " ") 86 | } 87 | -------------------------------------------------------------------------------- /gtci/sum_path_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a binary tree where each node can only have a digit (0-9) value, 4 | each root-to-leaf path will represent a number. Find the total sum of 5 | all the numbers represented by all paths. 6 | 7 | Example: 8 | - Input: 9 | 1 10 | 2 3 11 | 4 5 6 7 12 | Output: 522 (= 124 + 125 + 136 + 137) 13 | 14 | Approach: 15 | - Traverse the tree in a depth first search fashion. 16 | - At each level, the sum is equal to the result of the last sum times 10 17 | plus the current's node value. 18 | 19 | Cost: 20 | - O(n) time, O(n) space. 21 | */ 22 | 23 | package gtci 24 | 25 | import ( 26 | "testing" 27 | 28 | "github.com/hoanhan101/algo/common" 29 | ) 30 | 31 | func TestSumPath(t *testing.T) { 32 | t1 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 33 | t1.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 34 | t1.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 35 | t1.Left.Left = &common.TreeNode{Left: nil, Value: 4, Right: nil} 36 | t1.Left.Right = &common.TreeNode{Left: nil, Value: 5, Right: nil} 37 | t1.Right.Left = &common.TreeNode{Left: nil, Value: 6, Right: nil} 38 | t1.Right.Right = &common.TreeNode{Left: nil, Value: 7, Right: nil} 39 | 40 | t2 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 41 | t2.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 42 | t2.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 43 | t2.Left.Left = &common.TreeNode{Left: nil, Value: 4, Right: nil} 44 | t2.Right.Right = &common.TreeNode{Left: nil, Value: 7, Right: nil} 45 | 46 | tests := []struct { 47 | in *common.TreeNode 48 | expected int 49 | }{ 50 | {t1, 522}, 51 | {t2, 261}, 52 | } 53 | 54 | for _, tt := range tests { 55 | common.Equal( 56 | t, 57 | tt.expected, 58 | sumPath(tt.in), 59 | ) 60 | } 61 | } 62 | 63 | func sumPath(root *common.TreeNode) int { 64 | return recurSumPath(root, 0) 65 | } 66 | 67 | func recurSumPath(node *common.TreeNode, sum int) int { 68 | if node == nil { 69 | return 0 70 | } 71 | 72 | sum = 10*sum + node.Value 73 | 74 | // return the current sum if the current node is the leaf node. 75 | if node.Left == nil && node.Right == nil { 76 | return sum 77 | } 78 | 79 | return recurSumPath(node.Left, sum) + recurSumPath(node.Right, sum) 80 | } 81 | -------------------------------------------------------------------------------- /leetcode/swap_nodes_in_pairs_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a linked list, swap every two adjacent nodes and return its head. 4 | 5 | Assumption: 6 | - If the length of the linked list is odd, the last node should not be swapped. 7 | - The solution should use constant space. 8 | 9 | Example: 10 | - Input: 1 -> 3-> 5 -> 2 -> 4-> 6 11 | Output: 3 -> 1-> 2 -> 5 -> 6 -> 4 12 | - Input: 1 -> 3-> 5 -> 2 -> 4 13 | Output: 3 -> 1-> 2 -> 5 -> 4 14 | 15 | Approach: 16 | - Traverse the list and swap the nodes pairwise by adjusting where it's pointing next. 17 | 18 | Solution: 19 | - Initialize dummy heads to keep track of the head nodes. 20 | - Traverse the list while the current node and its next node is not nil. 21 | - Swap the pairs by adjusting where it's pointing next. 22 | - Cache the current node before advancing it to the next's next node. 23 | 24 | Cost: 25 | - O(n) time, O(1) space where n is the length of a linked list. 26 | */ 27 | 28 | package leetcode 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestSwapPairs(t *testing.T) { 37 | t11 := common.NewListNode(1) 38 | for i := 2; i <= 6; i++ { 39 | t11.AddNext(i) 40 | } 41 | 42 | t12 := common.NewListNode(2) 43 | t12.AddNext(1) 44 | t12.AddNext(4) 45 | t12.AddNext(3) 46 | t12.AddNext(6) 47 | t12.AddNext(5) 48 | 49 | tests := []struct { 50 | in *common.ListNode 51 | expected *common.ListNode 52 | }{ 53 | {t11, t12}, 54 | } 55 | 56 | for _, tt := range tests { 57 | common.Equal( 58 | t, 59 | tt.expected, 60 | swapPairs(tt.in), 61 | ) 62 | } 63 | } 64 | 65 | func swapPairs(l *common.ListNode) *common.ListNode { 66 | // dummy holds the head node. 67 | dummy := &common.ListNode{} 68 | dummy.Next = l 69 | 70 | // current holds the current node where prev holds the previous node. 71 | current := l 72 | prev := dummy 73 | 74 | for current != nil && current.Next != nil { 75 | next := current.Next 76 | nextNext := current.Next.Next 77 | 78 | prev.Next = next 79 | 80 | // swap the pairs by adjusting where it's pointing next. 81 | next.Next = current 82 | current.Next = nextNext 83 | 84 | // cache the current node before advancing it to the next's next node. 85 | prev = current 86 | current = nextNext 87 | } 88 | 89 | return dummy.Next 90 | } 91 | -------------------------------------------------------------------------------- /gtci/palindrome_list_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given the head of a singly linked list, write a function to determine 4 | if it is a palindrome in constant space. 5 | 6 | Approach: 7 | - Find the middle of the linked list and reverse a half list 8 | - After comparing the first half with the reversed half to check if it's 9 | a palindrome, revert to the half to original form. 10 | 11 | Cost: 12 | - O(n) time, O(1) space. 13 | */ 14 | 15 | package gtci 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/hoanhan101/algo/common" 21 | ) 22 | 23 | func TestIsPalindromeList(t *testing.T) { 24 | t1 := common.NewListNode(1) 25 | t1.AddNext(1) 26 | t1.AddNext(1) 27 | t1.AddNext(1) 28 | t1.AddNext(1) 29 | 30 | t2 := common.NewListNode(1) 31 | t2.AddNext(2) 32 | t2.AddNext(3) 33 | t2.AddNext(2) 34 | t2.AddNext(1) 35 | 36 | t3 := common.NewListNode(1) 37 | t3.AddNext(2) 38 | t3.AddNext(3) 39 | t3.AddNext(4) 40 | t3.AddNext(5) 41 | 42 | tests := []struct { 43 | in *common.ListNode 44 | expected bool 45 | }{ 46 | {t1, true}, 47 | {t2, true}, 48 | {t3, false}, 49 | } 50 | 51 | for _, tt := range tests { 52 | common.Equal( 53 | t, 54 | tt.expected, 55 | isPalindrome(tt.in), 56 | ) 57 | } 58 | } 59 | 60 | func isPalindrome(head *common.ListNode) bool { 61 | slow, fast := head, head 62 | 63 | // find the middle of the linked list. 64 | for fast != nil && fast.Next != nil { 65 | slow = slow.Next 66 | fast = fast.Next.Next 67 | 68 | } 69 | 70 | // reverse a half. 71 | headHalf := reverse(slow) 72 | copyHalf := headHalf 73 | 74 | // compare the first and second half. 75 | for head != nil && headHalf != nil { 76 | if head.Value != headHalf.Value { 77 | return false 78 | } 79 | 80 | head = head.Next 81 | headHalf = headHalf.Next 82 | } 83 | 84 | // revert the half to its original form. 85 | _ = reverse(copyHalf) 86 | 87 | if head == nil && headHalf == nil { 88 | return true 89 | } 90 | 91 | return false 92 | } 93 | 94 | func reverse(head *common.ListNode) *common.ListNode { 95 | var prev *common.ListNode 96 | 97 | for head != nil { 98 | next := head.Next 99 | head.Next = prev 100 | prev = head 101 | head = next 102 | } 103 | 104 | return prev 105 | } 106 | -------------------------------------------------------------------------------- /lab/quicksort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement quicksort. 4 | 5 | Approach: 6 | - Recursively divide the input into two smaller arrays around a pivot, where 7 | one half has items smaller than the pivot, other half has items bigger than 8 | the pivot. 9 | 10 | Cost: 11 | - O(nlogn) time and O(nlogn) space. 12 | */ 13 | 14 | package lab 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/hoanhan101/algo/common" 20 | ) 21 | 22 | func TestQuicksort(t *testing.T) { 23 | tests := []struct { 24 | in []int 25 | expected []int 26 | }{ 27 | {[]int{}, []int{}}, 28 | {[]int{1}, []int{1}}, 29 | {[]int{1, 2}, []int{1, 2}}, 30 | {[]int{2, 1}, []int{1, 2}}, 31 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 32 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 33 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 34 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 36 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 37 | } 38 | 39 | for _, tt := range tests { 40 | quicksort(tt.in, 0, len(tt.in)-1) 41 | common.Equal(t, tt.expected, tt.in) 42 | } 43 | } 44 | 45 | func quicksort(in []int, start, end int) { 46 | if start < end { 47 | // pi is the pivot/partition index. 48 | pi := partition(in, start, end) 49 | 50 | // sort the items before and after partition. 51 | quicksort(in, start, pi-1) 52 | quicksort(in, pi+1, end) 53 | } 54 | } 55 | 56 | func partition(in []int, start, end int) int { 57 | pivot := in[end] 58 | 59 | left := start 60 | right := end - 1 61 | 62 | for left <= right { 63 | // keep going until we find something on the left that belongs to the 64 | // right. 65 | for left <= end && in[left] < pivot { 66 | left++ 67 | } 68 | 69 | // keep going until we find something on the right that belongs to the 70 | // left. 71 | for right >= start && in[right] >= pivot { 72 | right-- 73 | } 74 | 75 | // by swapping the item at left and right index, we move the item that 76 | // is smaller than the pivot to the left half and vice versa. 77 | if left < right { 78 | common.Swap(in, left, right) 79 | } else { 80 | // once the partition is finished, move the pivot back to its final 81 | // position by swapping the item at left and end index. 82 | common.Swap(in, left, end) 83 | } 84 | } 85 | 86 | return left 87 | } 88 | -------------------------------------------------------------------------------- /leetcode/longest_substring_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, find the length of the longest substring without repeating characters. 4 | 5 | Example: 6 | - Input: "abcabcbb" 7 | Output: 3 8 | Explanation: The longest substring is "abc" with the length of 3. 9 | - Input: "bbbbb" 10 | Output: 1 11 | Explanation: The longest substring is "b" with the length of 1. 12 | 13 | Approach: 14 | - Iterate through the string and keep track of the maximum length of non-repeating 15 | characters using a hashmap that maps characters to their indices. 16 | - Could skip characters immediately if we found a repeating character. 17 | 18 | Solution: 19 | - Initialize a map that maps characters to their indices. 20 | - Initialize a start index and end index to keep track of the start and end of 21 | a substring. 22 | - Iterate through the string and check if we have seen the current character 23 | before in the map. 24 | - If so, update the start index. 25 | - Otherwise, cache the current index and update the maximum length if we found 26 | a larger one. 27 | - Return the maximum length in the end. 28 | 29 | Cost: 30 | - O(n) time, O(m) cost where m < n and n is the length of the string. 31 | */ 32 | 33 | package leetcode 34 | 35 | import ( 36 | "testing" 37 | 38 | "github.com/hoanhan101/algo/common" 39 | ) 40 | 41 | func TestCalculateLongestSubstring(t *testing.T) { 42 | tests := []struct { 43 | in string 44 | expected int 45 | }{ 46 | {"", 0}, 47 | {"abcabcbb", 3}, 48 | {"bbbbb", 1}, 49 | {"danixxxdaniiii", 5}, 50 | } 51 | 52 | for _, tt := range tests { 53 | result := calculateLongestSubstring(tt.in) 54 | common.Equal(t, tt.expected, result) 55 | } 56 | } 57 | 58 | func calculateLongestSubstring(s string) int { 59 | // m maps characters to their indices. 60 | m := map[string]int{} 61 | 62 | start := 0 63 | maxLength := 0 64 | 65 | for end := 0; end < len(s); end++ { 66 | // if we have seen the character before, update the start index to 67 | // skip visited characters. 68 | if m[string(s[end])] >= start { 69 | start = m[string(s[end])] + 1 70 | } 71 | 72 | // cache the current character's index at every step. 73 | m[string(s[end])] = end 74 | 75 | // similar to greedy approach, update max length at each step. 76 | maxLength = common.Max(end-start+1, maxLength) 77 | } 78 | 79 | return maxLength 80 | } 81 | -------------------------------------------------------------------------------- /gtci/longest_substring_k_replacement_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, if you are allowed to replace no more than k letters with 4 | any letter, find the length of the longest substring having the same letters 5 | after replacement. 6 | 7 | Example: 8 | - Input: string="aabccbb", k=2 9 | Output: 5 10 | Explanation: Longest substring is "bbbbb" after replacing 2 c with b. 11 | - Input: string="abbcb", k=1 12 | Output: 4 13 | Explanation: Longest substring is "bbbb" after replacing 1 c with b. 14 | 15 | Approach: 16 | - Use a hashmap to remember the frequency of each character we have seen. 17 | - As we iterate through the string and add character to the window, we 18 | also keep track of the maximum repeating character count. 19 | - Shrink the window accordingly as we are not allowed to replace more than 20 | k characters. 21 | 22 | Cost: 23 | - O(n) time, O(1) space since there are only 26 characters in the alphabet. 24 | */ 25 | 26 | package gtci 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestLongestSubstringKReplacement(t *testing.T) { 35 | tests := []struct { 36 | in1 string 37 | in2 int 38 | expected int 39 | }{ 40 | {"aabccbb", 2, 5}, 41 | {"abbcb", 1, 4}, 42 | } 43 | 44 | for _, tt := range tests { 45 | common.Equal( 46 | t, 47 | tt.expected, 48 | longestSubstringKReplacement(tt.in1, tt.in2), 49 | ) 50 | } 51 | } 52 | 53 | func longestSubstringKReplacement(s string, k int) int { 54 | maxLength, start, maxRepeatCharCount := 0, 0, 0 55 | 56 | // char keeps track of characters' frequencies. 57 | char := map[string]int{} 58 | 59 | for end := range s { 60 | // insert characters into the frequencies map. 61 | endChar := string(s[end]) 62 | if _, ok := char[endChar]; !ok { 63 | char[endChar] = 0 64 | } 65 | char[endChar]++ 66 | 67 | // update max repeating character count. 68 | maxRepeatCharCount = common.Max(maxRepeatCharCount, char[endChar]) 69 | 70 | // shrink the window as we are not allowed to replace more than k 71 | // characters. 72 | if end-start+1-maxRepeatCharCount > k { 73 | startChar := string(s[start]) 74 | char[startChar]-- 75 | start++ 76 | } 77 | 78 | // update the maximum length at each step. 79 | maxLength = common.Max(maxLength, end-start+1) 80 | } 81 | 82 | return maxLength 83 | } 84 | -------------------------------------------------------------------------------- /gtci/median_number_stream_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Find the median of a number stream. 4 | 5 | Example: 6 | 1. insert(1) 7 | 2. findMedian() -> 1 8 | 3. insert(6) 9 | 2. findMedian() -> 3.5 10 | 3. insert(2) 11 | 5. findMedian() -> 2 12 | 6. insert(5) 13 | 7. findMedian() -> 3.5 14 | 8. insert(3) 15 | 9. findMedian() -> 3 16 | 17 | Approach: 18 | - Divide the stream into 2 lists where one holds all numbers that are less 19 | than the median and vice versa. 20 | - Since the median is either be the largest of the smaller list (maxHeap) or 21 | the smallest one of the larger list (minHeap), can use a min heap and max heap 22 | respectively. That said, if the largest value of the smaller list is >= inserted 23 | value, insert to the smaller list. Else, insert to the larger list. 24 | - At every insertion, balance the numbers in both heap. 25 | 26 | Cost: 27 | - O(1) time, O(n) space. 28 | */ 29 | 30 | package gtci 31 | 32 | import ( 33 | "testing" 34 | 35 | "github.com/hoanhan101/algo/common" 36 | ) 37 | 38 | func TestMedianInStream(t *testing.T) { 39 | s := newMStream() 40 | s.insert(1) 41 | common.Equal(t, float64(1), s.findMedian()) 42 | s.insert(6) 43 | common.Equal(t, float64(3.5), s.findMedian()) 44 | s.insert(2) 45 | common.Equal(t, float64(2), s.findMedian()) 46 | s.insert(5) 47 | common.Equal(t, float64(3.5), s.findMedian()) 48 | s.insert(3) 49 | common.Equal(t, float64(3), s.findMedian()) 50 | s.insert(4) 51 | common.Equal(t, float64(3.5), s.findMedian()) 52 | } 53 | 54 | type mstream struct { 55 | minHeap *common.MinHeap 56 | maxHeap *common.MaxHeap 57 | } 58 | 59 | func newMStream() *mstream { 60 | return &mstream{ 61 | minHeap: common.NewMinHeap(), 62 | maxHeap: common.NewMaxHeap(), 63 | } 64 | } 65 | 66 | func (s *mstream) insert(n int) { 67 | if s.maxHeap.Len() == 0 || s.maxHeap.Peek() >= n { 68 | s.maxHeap.Push(n) 69 | } else { 70 | s.minHeap.Push(n) 71 | } 72 | 73 | // maxHeap can have 1 more number than minHeap. 74 | if s.maxHeap.Len() > s.minHeap.Len()+1 { 75 | s.minHeap.Push(s.maxHeap.Pop()) 76 | } else if s.maxHeap.Len() < s.minHeap.Len() { 77 | s.maxHeap.Push(s.minHeap.Pop()) 78 | } 79 | } 80 | 81 | func (s *mstream) findMedian() float64 { 82 | if s.maxHeap.Len() == s.minHeap.Len() { 83 | return float64(s.maxHeap.Peek()+s.minHeap.Peek()) / 2.0 84 | } 85 | 86 | return float64(s.maxHeap.Peek()) 87 | } 88 | -------------------------------------------------------------------------------- /interviewcake/highest_product_of_three_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of integers, return the highest product of three numbers. 4 | 5 | Example: 6 | - Input: []int{-10, -10, 1, 3, 2} 7 | Output: 300, because -10.-10.3 gives the highest product 8 | 9 | Approach: 10 | - Use a greedy approach to keep track of the current highest, current lowest, 11 | highest of three, highest of two and lowest of two for every value as we 12 | iterate through the list. 13 | 14 | Solution: 15 | - Initialize a highest number, a lowest number, a highest of two numbers, 16 | a lowest of two numbers, and a highest of three numbers from the first 17 | 2-3 numbers in the list. 18 | - Iterate through the list and update each value in accordingly. 19 | - Make sure to update these in the right order, in which the highest product 20 | of three must be calculated first using the highest and lowest product of 21 | two, then the highest product of two, lowest product of two, current highest 22 | and current lowest. 23 | 24 | Cost: 25 | - O(n) time, O(1) space. 26 | */ 27 | 28 | package interviewcake 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestHighestProductOfThree(t *testing.T) { 37 | tests := []struct { 38 | in []int 39 | expected int 40 | }{ 41 | {[]int{}, 0}, 42 | {[]int{-10, -10, 1, 3, 2}, 300}, 43 | {[]int{1, 10, -5, 1, -100}, 5000}, 44 | } 45 | 46 | for _, tt := range tests { 47 | result := highestProductOfThree(tt.in) 48 | common.Equal(t, tt.expected, result) 49 | } 50 | } 51 | 52 | func highestProductOfThree(list []int) int { 53 | if len(list) <= 3 { 54 | return 0 55 | } 56 | 57 | highest := common.Max(list[0], list[1]) 58 | lowest := common.Min(list[0], list[1]) 59 | highestTwo := list[0] * list[1] 60 | lowestTwo := list[0] * list[1] 61 | highestThree := list[0] * list[1] * list[2] 62 | 63 | for i := 2; i < len(list); i++ { 64 | current := list[i] 65 | 66 | // make sure to update each variable in the right order. 67 | highestThree = common.Max(highestThree, current*highestTwo, current*lowestTwo) 68 | highestTwo = common.Max(highestTwo, current*highest, current*lowest) 69 | lowestTwo = common.Min(lowestTwo, current*highest, current*lowest) 70 | highest = common.Max(highest, current) 71 | lowest = common.Min(lowest, current) 72 | } 73 | 74 | return highestThree 75 | } 76 | -------------------------------------------------------------------------------- /leetcode/merge_sorted_linked_list_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Merge two sorted linked lists and return it as a new list. 4 | 5 | Example: 6 | - Input: 1 -> 3-> 5 & 2 -> 4-> 6 7 | Output: 1 -> 2-> 3 -> 4 -> 5 -> 6 8 | 9 | Approach: 10 | - Traverse both list at the same time, compare their values at each step and 11 | add the smaller one to a new list. 12 | 13 | Solution: 14 | - Initialize a dummy head to keep track of the head node. 15 | - Traverse both lists while they are are not empty. 16 | - Compare their values at each step and add the smaller one to a new list. 17 | - Remember to check if we have reached the end for a list faster than the 18 | other. 19 | - If that is the case, simply add the rest of the other list to the new list 20 | since it is already sorted. 21 | 22 | Cost: 23 | - O(n|m) time, O(n+m) space where n and m are lengths of these two linked lists. 24 | */ 25 | 26 | package leetcode 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestMergeSortedLinkedList(t *testing.T) { 35 | t11 := common.NewListNode(1) 36 | t11.AddNext(3) 37 | t11.AddNext(5) 38 | 39 | t12 := common.NewListNode(2) 40 | t12.AddNext(4) 41 | t12.AddNext(6) 42 | 43 | t13 := common.NewListNode(1) 44 | for i := 2; i <= 6; i++ { 45 | t13.AddNext(i) 46 | } 47 | 48 | tests := []struct { 49 | in1 *common.ListNode 50 | in2 *common.ListNode 51 | expected *common.ListNode 52 | }{ 53 | {t11, t12, t13}, 54 | } 55 | 56 | for _, tt := range tests { 57 | common.Equal( 58 | t, 59 | tt.expected, 60 | mergeSortedLinkedList(tt.in1, tt.in2), 61 | ) 62 | } 63 | } 64 | 65 | func mergeSortedLinkedList(l1, l2 *common.ListNode) *common.ListNode { 66 | // initialize a dummy head to keep track of the head node. 67 | dummyHead := &common.ListNode{} 68 | current := dummyHead 69 | 70 | // while both linked list are not empty. 71 | for l1 != nil && l2 != nil { 72 | if l1.Value < l2.Value { 73 | current.Next = l1 74 | l1 = l1.Next 75 | } else { 76 | current.Next = l2 77 | l2 = l2.Next 78 | } 79 | 80 | current = current.Next 81 | } 82 | 83 | // if we are here, it means that we have reached the end of a linked list. 84 | // simply append the rest of the other list since it's already sorted. 85 | if l1 != nil { 86 | current.Next = l1 87 | } 88 | 89 | if l2 != nil { 90 | current.Next = l2 91 | } 92 | 93 | return dummyHead.Next 94 | } 95 | -------------------------------------------------------------------------------- /gtci/insert_interval_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of non-overlapping intervals sorted by their start time, insert 4 | a given interval at the correct position and merge all necessary intervals to 5 | produce a list that has only mutually exclusive intervals. 6 | 7 | Example: 8 | - Input: []interval{{1, 3}, {5, 7}, {8, 12}}, interval{4, 6} 9 | Output: []interval{{1, 3}, {4, 7}, {8, 12}} 10 | 11 | Approach: 12 | - Since the list is sorted, can skip all the intervals where their end time is 13 | less than the new interval's start time. 14 | - The merging process is similar to the one in merge interval problem. 15 | 16 | Cost: 17 | - O(n) time, O(n) space. 18 | */ 19 | 20 | package gtci 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestInsertInterval(t *testing.T) { 29 | tests := []struct { 30 | in1 []interval 31 | in2 interval 32 | expected []interval 33 | }{ 34 | {[]interval{}, interval{}, []interval{}}, 35 | {[]interval{{5, 7}, {8, 12}}, interval{1, 3}, []interval{{1, 3}, {5, 7}, {8, 12}}}, 36 | {[]interval{{1, 3}, {5, 7}}, interval{8, 12}, []interval{{1, 3}, {5, 7}, {8, 12}}}, 37 | {[]interval{{1, 3}, {5, 7}, {8, 12}}, interval{4, 6}, []interval{{1, 3}, {4, 7}, {8, 12}}}, 38 | {[]interval{{1, 3}, {5, 7}, {8, 12}}, interval{3, 8}, []interval{{1, 12}}}, 39 | } 40 | 41 | for _, tt := range tests { 42 | result := insertInterval(tt.in1, tt.in2) 43 | common.Equal(t, tt.expected, result) 44 | } 45 | } 46 | 47 | func insertInterval(intervals []interval, newInterval interval) []interval { 48 | if len(intervals) == 0 { 49 | return intervals 50 | } 51 | 52 | merged := []interval{} 53 | i := 0 54 | 55 | // skip all the intervals that comes before the new interval. 56 | for i < len(intervals) && intervals[i].end < newInterval.start { 57 | merged = append(merged, intervals[i]) 58 | i++ 59 | } 60 | 61 | // merge all the intervals that overlap with the new interval. 62 | for i < len(intervals) && intervals[i].start <= newInterval.end { 63 | newInterval.start = common.Min(intervals[i].start, newInterval.start) 64 | newInterval.end = common.Max(intervals[i].end, newInterval.end) 65 | i++ 66 | } 67 | 68 | merged = append(merged, newInterval) 69 | 70 | // add all the remaining intervals to the output. 71 | for i < len(intervals) { 72 | merged = append(merged, intervals[i]) 73 | i++ 74 | } 75 | 76 | return merged 77 | } 78 | -------------------------------------------------------------------------------- /leetcode/single_number_ii_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of integer where every element appears three time except for 4 | one, find that unique one in O(1) space. 5 | 6 | Example: 7 | - Input: []int{1, 1, 2, 4, 2, 6, 4, 2, 1, 4} 8 | Output: 6, because 6 appears 1 time only. 9 | 10 | Approach: 11 | - Use 3 bitmask values to represent the number that appears 1 time, 2 times and 12 | 3 times. 13 | - When the element appears for the third times, clear it of both the 14 | 1-time-appeared and 2-time-appeared bitmask values. 15 | - The final result of 1-time-appeared bitmask value is the unique one. 16 | 17 | Solution: 18 | - Initialize 3 bitmask values (ones, twos, threes) to represent the number that 19 | appears 1 time, 2 times and 3 times. 20 | - Iterate through the list and update these values accordingly: 21 | - twos can be calculated by AND-ing ones and the current value. 22 | - XOR the current value with the previous ones to keep track of the 23 | number that appears 1 time only. 24 | - when the current value appears for the third times, clear it of both 25 | ones and twos. 26 | - Return unique ones. 27 | 28 | Cost: 29 | - O(n) time, O(1) space. 30 | */ 31 | 32 | package leetcode 33 | 34 | import ( 35 | "testing" 36 | 37 | "github.com/hoanhan101/algo/common" 38 | ) 39 | 40 | func TestFindUniqueID(t *testing.T) { 41 | tests := []struct { 42 | in []int 43 | expected int 44 | }{ 45 | {[]int{}, 0}, 46 | {[]int{6}, 6}, 47 | {[]int{6, 2, 2, 2}, 6}, 48 | {[]int{6, 2, 2, 2, 4, 4, 4, 1, 1, 1}, 6}, 49 | {[]int{1, 1, 2, 4, 2, 1, 4, 6, 4, 2}, 6}, 50 | {[]int{1, 1, 2, 4, 2, 6, 4, 2, 1, 4}, 6}, 51 | } 52 | 53 | for _, tt := range tests { 54 | result := findUniqueID(tt.in) 55 | common.Equal(t, tt.expected, result) 56 | } 57 | } 58 | 59 | func findUniqueID(list []int) int { 60 | // ones, twos, threes represent the number that appears 1 time, 2 times and 61 | // 3 times accordingly. 62 | ones, twos, threes := 0, 0, 0 63 | 64 | for _, id := range list { 65 | // twos can be calculated by AND-ing ones and the current id. 66 | twos |= ones & id 67 | 68 | // XOR the current id with the previous ones to keep track of the 69 | // number that appears 1 time only. 70 | ones ^= id 71 | 72 | // when the current id appears for the third times, clear it of both 73 | // ones and twos. 74 | threes = ones & twos 75 | ones &= ^threes 76 | twos &= ^threes 77 | } 78 | 79 | return ones 80 | } 81 | -------------------------------------------------------------------------------- /leetcode/palindrome_number_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Determine whether an integer is a palindrome. 4 | 5 | Assumption: 6 | - Do this without extra space. 7 | - Define negative integers as non-palindrome. 8 | 9 | Example: 10 | - Input: 101 11 | Output: true 12 | - Input: 106 13 | Output: false 14 | 15 | Approach: 16 | - Use two-pointer approach where one starts at the first digit and one starts 17 | at the last digit, have them walk toward the middle and compare them at each 18 | step. 19 | 20 | Solution: 21 | - Calculate the division factor for the number to divide by to get its first digit. 22 | - While the number is not equal to 0: 23 | - Get the first digit by diving the number by the division factor above. 24 | - Get the last digit by applying modulo the number by 10. 25 | - Return false immediately if they are equal. 26 | - Otherwise, chop of both digit by applying modulo the number by the division factor 27 | divide the result by 10. 28 | - Update the division factor by dividing it by 100 since we already chopped of the first 29 | and last digits. 30 | 31 | Cost: 32 | - O(n) time, O(1) space. 33 | */ 34 | 35 | package leetcode 36 | 37 | import ( 38 | "testing" 39 | 40 | "github.com/hoanhan101/algo/common" 41 | ) 42 | 43 | func TestIsIntPalindrome(t *testing.T) { 44 | tests := []struct { 45 | in int 46 | expected bool 47 | }{ 48 | {-1, false}, 49 | {0, true}, 50 | {1, true}, 51 | {10, false}, 52 | {11, true}, 53 | {101, true}, 54 | {111, true}, 55 | {110, false}, 56 | {-101, false}, 57 | } 58 | 59 | for _, tt := range tests { 60 | result := isIntPalindrome(tt.in) 61 | common.Equal(t, tt.expected, result) 62 | } 63 | } 64 | 65 | func isIntPalindrome(x int) bool { 66 | // return false immediately if the input is negative. 67 | if x < 0 { 68 | return false 69 | } 70 | 71 | // calculate the division factor for the number to divide by to get its 72 | // first digit. 73 | div := 1 74 | for x/div >= 10 { 75 | div *= 10 76 | } 77 | 78 | for x != 0 { 79 | l := x / div 80 | r := x % 10 81 | 82 | // return false immediately if two corresponding digits are the not 83 | // equal. 84 | if l != r { 85 | return false 86 | } 87 | 88 | // chop off the first and last digits. 89 | x = (x % div) / 10 90 | 91 | // update the division factor by dividing it by 100 since we already 92 | // chopped of the first and last digits. 93 | div /= 100 94 | } 95 | 96 | return true 97 | } 98 | -------------------------------------------------------------------------------- /leetcode/integer_palindrome_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Determine whether an integer is a palindrome. 4 | 5 | Assumption: 6 | - Do this without extra space. 7 | - Define negative integers as non-palindrome. 8 | 9 | Example: 10 | - Input: 101 11 | Output: true 12 | - Input: 106 13 | Output: false 14 | 15 | Approach: 16 | - Use two-pointer approach where one starts at the first digit and one starts 17 | at the last digit, have them walk toward the middle and compare them at each 18 | step. 19 | 20 | Solution: 21 | - Calculate the division factor for the number to divide by to get its first digit. 22 | - While the number is not equal to 0: 23 | - Get the first digit by diving the number by the division factor above. 24 | - Get the last digit by applying modulo the number by 10. 25 | - Return false immediately if they are equal. 26 | - Otherwise, chop of both digit by applying modulo the number by the division factor 27 | divide the result by 10. 28 | - Update the division factor by dividing it by 100 since we already chopped of the first 29 | and last digits. 30 | 31 | Cost: 32 | - O(n) time, O(1) space. 33 | */ 34 | 35 | package leetcode 36 | 37 | import ( 38 | "testing" 39 | 40 | "github.com/hoanhan101/algo/common" 41 | ) 42 | 43 | func TestIsIntegerPalindrome(t *testing.T) { 44 | tests := []struct { 45 | in int 46 | expected bool 47 | }{ 48 | {-1, false}, 49 | {0, true}, 50 | {1, true}, 51 | {10, false}, 52 | {11, true}, 53 | {101, true}, 54 | {111, true}, 55 | {110, false}, 56 | {-101, false}, 57 | } 58 | 59 | for _, tt := range tests { 60 | result := isIntegerPalindrome(tt.in) 61 | common.Equal(t, tt.expected, result) 62 | } 63 | } 64 | 65 | func isIntegerPalindrome(x int) bool { 66 | // return false immediately if the input is negative. 67 | if x < 0 { 68 | return false 69 | } 70 | 71 | // calculate the division factor for the number to divide by to get its 72 | // first digit. 73 | div := 1 74 | for x/div >= 10 { 75 | div *= 10 76 | } 77 | 78 | for x != 0 { 79 | l := x / div 80 | r := x % 10 81 | 82 | // return false immediately if two corresponding digits are the not 83 | // equal. 84 | if l != r { 85 | return false 86 | } 87 | 88 | // chop off the first and last digits. 89 | x = (x % div) / 10 90 | 91 | // update the division factor by dividing it by 100 since we already 92 | // chopped of the first and last digits. 93 | div /= 100 94 | } 95 | 96 | return true 97 | } 98 | -------------------------------------------------------------------------------- /gtci/staircase_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a stair with n steps, count how many possible ways to reach the top 4 | where at each step you can either take 1, 2 or 3 steps. 5 | 6 | Example: 7 | - Input: 3 8 | Output: 4 9 | Explanation: 4 ways are 1-1-1, 1-2, 2-1, 3 10 | - Input: 4 11 | Output: 7 12 | Explanation: 7 ways are 1-1-1-1, 1-1-2, 1-2-1, 2-1-1, 2-2, 1-3, 3-1 13 | 14 | Approach: 15 | - Similar to Fibonacci numbers problem, every count is a sum of three 16 | preceding numbers. 17 | 18 | Cost: 19 | - Brute-force: O(n^3) time, O(n) space. 20 | - Top-down: O(n) time, O(n) space. 21 | - Bottom-up: O(n) time, O(1) space. 22 | */ 23 | 24 | package gtci 25 | 26 | import ( 27 | "testing" 28 | 29 | "github.com/hoanhan101/algo/common" 30 | ) 31 | 32 | func TestCountSteps(t *testing.T) { 33 | tests := []struct { 34 | in int 35 | expected int 36 | }{ 37 | {3, 4}, 38 | {4, 7}, 39 | {5, 13}, 40 | } 41 | 42 | for _, tt := range tests { 43 | common.Equal( 44 | t, 45 | tt.expected, 46 | countStepsBF(tt.in), 47 | ) 48 | 49 | common.Equal( 50 | t, 51 | tt.expected, 52 | countStepsTD(tt.in), 53 | ) 54 | 55 | common.Equal( 56 | t, 57 | tt.expected, 58 | countStepsBU(tt.in), 59 | ) 60 | } 61 | } 62 | 63 | func countStepsBF(n int) int { 64 | // if there is less than 2 steps, there must only be 1 way. 65 | if n < 2 { 66 | return 1 67 | } 68 | 69 | // if there are 2 steps, then there are 2 ways that is 1-1 or 2. 70 | if n == 2 { 71 | return 2 72 | } 73 | 74 | return countStepsBF(n-1) + countStepsBF(n-2) + countStepsBF(n-3) 75 | } 76 | 77 | func countStepsTD(n int) int { 78 | memo := make([]int, n+1) 79 | return countStepsMemoRecur(memo, n) 80 | } 81 | 82 | func countStepsMemoRecur(memo []int, n int) int { 83 | if n < 2 { 84 | return 1 85 | } 86 | 87 | if n == 2 { 88 | return 2 89 | } 90 | 91 | if memo[n] != 0 { 92 | return memo[n] 93 | } 94 | 95 | memo[n] = countStepsMemoRecur(memo, n-1) + countStepsMemoRecur(memo, n-2) + countStepsMemoRecur(memo, n-3) 96 | return memo[n] 97 | } 98 | 99 | func countStepsBU(n int) int { 100 | if n < 2 { 101 | return 1 102 | } 103 | 104 | if n == 2 { 105 | return 2 106 | } 107 | 108 | n1, n2, n3, tmp := 1, 1, 2, 0 109 | for i := 3; i < n+1; i++ { 110 | tmp = n1 + n2 + n3 111 | n1 = n2 112 | n2 = n3 113 | n3 = tmp 114 | } 115 | 116 | return n3 117 | } 118 | -------------------------------------------------------------------------------- /gtci/number_factors_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a number n, count how many possible ways to calculate n 4 | as the sum of 1, 3, 4. 5 | 6 | Example: 7 | - Input: 4 8 | Output: 4 9 | Explanation: 4 ways are 1-1-1-1, 1-3, 3-1, 4 10 | - Input: 5 11 | Output: 6 12 | Explanation: 4 ways are 1-1-1-1-1-1, 1-1-3, 1-3-1, 3-1-1, 1-4, 4-1 13 | 14 | Approach: 15 | - For every number, we can either subtract 1, 3, or 4 in a recursive way. 16 | 17 | Cost: 18 | - Brute-force: O(n^3) time, O(n) space. 19 | - Top-down: O(n) time, O(n) space. 20 | - Bottom-up: O(n) time, O(n) space. 21 | */ 22 | 23 | package gtci 24 | 25 | import ( 26 | "testing" 27 | 28 | "github.com/hoanhan101/algo/common" 29 | ) 30 | 31 | func TestCountNumberFactors(t *testing.T) { 32 | tests := []struct { 33 | in int 34 | expected int 35 | }{ 36 | {4, 4}, 37 | {5, 6}, 38 | {6, 9}, 39 | } 40 | 41 | for _, tt := range tests { 42 | common.Equal( 43 | t, 44 | tt.expected, 45 | countNumberFactorsBF(tt.in), 46 | ) 47 | 48 | common.Equal( 49 | t, 50 | tt.expected, 51 | countNumberFactorsTD(tt.in), 52 | ) 53 | 54 | common.Equal( 55 | t, 56 | tt.expected, 57 | countNumberFactorsBU(tt.in), 58 | ) 59 | } 60 | } 61 | 62 | func countNumberFactorsBF(n int) int { 63 | // if n is less and equal than 2, there is only 1 way to subtract 1. 64 | if n <= 2 { 65 | return 1 66 | } 67 | 68 | // if n is 3, there are 2 ways as 1-1-1, 3 would work. 69 | if n == 3 { 70 | return 2 71 | } 72 | 73 | return countNumberFactorsBF(n-1) + countNumberFactorsBF(n-3) + countNumberFactorsBF(n-4) 74 | } 75 | 76 | func countNumberFactorsTD(n int) int { 77 | memo := make([]int, n+1) 78 | return countNumberFactorsMemoRecur(memo, n) 79 | } 80 | 81 | func countNumberFactorsMemoRecur(memo []int, n int) int { 82 | if n <= 2 { 83 | return 1 84 | } 85 | 86 | if n == 3 { 87 | return 2 88 | } 89 | 90 | if memo[n] != 0 { 91 | return memo[n] 92 | } 93 | 94 | memo[n] = countNumberFactorsMemoRecur(memo, n-1) + countNumberFactorsMemoRecur(memo, n-3) + countNumberFactorsMemoRecur(memo, n-4) 95 | return memo[n] 96 | } 97 | 98 | func countNumberFactorsBU(n int) int { 99 | tabu := make([]int, n+1) 100 | tabu[0] = 1 101 | tabu[1] = 1 102 | tabu[2] = 1 103 | tabu[3] = 2 104 | 105 | for i := 4; i < n+1; i++ { 106 | tabu[i] = tabu[i-1] + tabu[i-3] + tabu[i-4] 107 | } 108 | 109 | return tabu[n] 110 | } 111 | -------------------------------------------------------------------------------- /common/list.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | // List implements a linked list. 8 | type List struct { 9 | elements *list.List 10 | } 11 | 12 | // NewList returns a new list. 13 | func NewList() *List { 14 | return &List{elements: list.New()} 15 | } 16 | 17 | // Back returns the last element of the list. 18 | func (l *List) Back() interface{} { 19 | return l.elements.Back().Value 20 | } 21 | 22 | // Front returns the first element of the list. 23 | func (l *List) Front() interface{} { 24 | return l.elements.Front().Value 25 | } 26 | 27 | // Len returns the number of elements of the list. 28 | func (l *List) Len() interface{} { 29 | return l.elements.Len() 30 | } 31 | 32 | // InsertAfter inserts an element after another. 33 | func (l *List) InsertAfter(v, mark interface{}) { 34 | // TODO 35 | } 36 | 37 | // InsertBefore inserts an element before another. 38 | func (l *List) InsertBefore(v, mark interface{}) { 39 | // TODO 40 | } 41 | 42 | // MoveAfter moves an element after another. 43 | func (l *List) MoveAfter(v, mark interface{}) { 44 | // TODO 45 | } 46 | 47 | // MoveBefore moves an element before another. 48 | func (l *List) MoveBefore(v, mark interface{}) { 49 | } 50 | 51 | // MoveBack moves an element to the back of the list. 52 | func (l *List) MoveBack(v interface{}) { 53 | // TODO 54 | } 55 | 56 | // MoveFront moves an element to the front of the list. 57 | func (l *List) MoveFront(v interface{}) { 58 | // TODO 59 | } 60 | 61 | // PushBack inserts an element at the back of the list. 62 | func (l *List) PushBack(v interface{}) interface{} { 63 | return l.elements.PushBack(v).Value 64 | } 65 | 66 | // PushFront inserts an element at the front of the list. 67 | func (l *List) PushFront(v interface{}) interface{} { 68 | return l.elements.PushFront(v).Value 69 | } 70 | 71 | // RemoveFront removes an element at the front of the list. 72 | func (l *List) RemoveFront() interface{} { 73 | return l.elements.Remove(l.elements.Front()) 74 | } 75 | 76 | // RemoveBack removes an element at the back of the list. 77 | func (l *List) RemoveBack() interface{} { 78 | return l.elements.Remove(l.elements.Back()) 79 | } 80 | 81 | // Remove removes an element from the list. 82 | // func (l *List) Remove(v interface{}) interface{} { 83 | // } 84 | 85 | // Slice returns a slice of all elements in the list. 86 | func (l *List) Slice() []interface{} { 87 | out := []interface{}{} 88 | 89 | for e := l.elements.Front(); e != nil; e = e.Next() { 90 | out = append(out, e.Value) 91 | } 92 | 93 | return out 94 | } 95 | -------------------------------------------------------------------------------- /gtci/fruits_baskets_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of characters where each character represents a fruit tree, 4 | you are given two baskets and your goal is to put maximum number of fruits 5 | in each basket. 6 | 7 | Constraints: 8 | - Each basket can have only one type of fruit. 9 | - You can start with any tree, but once you have started you can’t skip a tree. 10 | You will pick one fruit from each tree until you cannot, i.e., you will stop 11 | when you have to pick from a third fruit type. 12 | 13 | Example: 14 | - Input: fruits=["apple", "orange", "coconut", "apple", "coconut"] 15 | Output: 3 16 | Explanation: Can put 2 "cocunut" in 1 basket and 1 "apple" in other from 17 | subarray ["coconut", "apple", "coconut"] 18 | 19 | Approach: 20 | - Similar to "longest substring with k distinct characters" with k=2. 21 | 22 | Cost: 23 | - O(n) time, O(k) space where k is the number of characters in the map. 24 | */ 25 | 26 | package gtci 27 | 28 | import ( 29 | "testing" 30 | 31 | "github.com/hoanhan101/algo/common" 32 | ) 33 | 34 | func TestFruitsIntoBaskets(t *testing.T) { 35 | tests := []struct { 36 | in []string 37 | expected int 38 | }{ 39 | {[]string{"apple", "orange", "coconut", "apple", "coconut"}, 3}, 40 | {[]string{"apple", "orange", "coconut", "orange", "coconut", "orange", "coconut"}, 6}, 41 | } 42 | 43 | for _, tt := range tests { 44 | common.Equal( 45 | t, 46 | tt.expected, 47 | fruitsIntoBaskets(tt.in), 48 | ) 49 | } 50 | } 51 | 52 | func fruitsIntoBaskets(fruits []string) int { 53 | maxLength, start := 0, 0 54 | 55 | // fruitsMap keeps track of fruits' frequencies. 56 | fruitsMap := map[string]int{} 57 | 58 | for end := range fruits { 59 | // insert fruits until we have 2 distinct fruits. 60 | endFruit := fruits[end] 61 | if _, ok := fruitsMap[endFruit]; !ok { 62 | fruitsMap[endFruit] = 0 63 | } 64 | fruitsMap[endFruit]++ 65 | 66 | // shrink the window until there is no more than 2 distinct fruits. 67 | for len(fruitsMap) > 2 { 68 | startFruit := fruits[start] 69 | 70 | // decrement the frequency of the one going out of the window and 71 | // remove if its frequency is zero. 72 | fruitsMap[startFruit]-- 73 | if fruitsMap[startFruit] == 0 { 74 | delete(fruitsMap, startFruit) 75 | } 76 | 77 | // increase the start index to move the window ahead by one element. 78 | start++ 79 | } 80 | 81 | // update the maximum length at each step. 82 | maxLength = common.Max(maxLength, end-start+1) 83 | } 84 | 85 | return maxLength 86 | } 87 | -------------------------------------------------------------------------------- /gtci/string_anagrams_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string and a pattern, find all anagrams of the pattern in the given string 4 | - Return them as a list of anagrams' starting indices. 5 | 6 | Example: 7 | - Input: string="ppqp", pattern="pq" 8 | Output: []int{1, 2} 9 | - Input: string="abbcabc", pattern="abc" 10 | Output: []int{2, 3, 4} 11 | 12 | Approach: 13 | - Similar to permutation in string problem, except we will store the starting indices 14 | of the anagrams of the pattern. 15 | 16 | Cost: 17 | - O(n) time, O(m) space where m is the number of distinct characters in the map. 18 | */ 19 | 20 | package gtci 21 | 22 | import ( 23 | "testing" 24 | 25 | "github.com/hoanhan101/algo/common" 26 | ) 27 | 28 | func TestFindStringAnagrams(t *testing.T) { 29 | tests := []struct { 30 | in1 string 31 | in2 string 32 | expected []int 33 | }{ 34 | {"ppqp", "pq", []int{1, 2}}, 35 | {"abbcabc", "abc", []int{2, 3, 4}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | common.Equal( 40 | t, 41 | tt.expected, 42 | findStringAnagrams(tt.in1, tt.in2), 43 | ) 44 | } 45 | } 46 | 47 | func findStringAnagrams(s string, pattern string) []int { 48 | out := []int{} 49 | matched, start := 0, 0 50 | 51 | // char keeps track of characters' frequencies. 52 | char := map[string]int{} 53 | 54 | // calculate the frequencies of all characters in the pattern. 55 | for _, c := range pattern { 56 | if _, ok := char[string(c)]; !ok { 57 | char[string(c)] = 0 58 | } 59 | 60 | char[string(c)]++ 61 | } 62 | 63 | for end := range s { 64 | // if the character matches one in the map, decrease its frequency. 65 | // if its frequency becomes 0, we have a complete match. 66 | endChar := string(s[end]) 67 | if _, ok := char[endChar]; ok { 68 | char[endChar]-- 69 | 70 | if char[endChar] == 0 { 71 | matched++ 72 | } 73 | } 74 | 75 | // return true immediately if we have all complete matches. 76 | if matched == len(char) { 77 | out = append(out, start) 78 | } 79 | 80 | // shrink the window by one character at a time so that its size is 81 | // equal to the length of the pattern. 82 | if end >= len(pattern)-1 { 83 | startChar := string(s[start]) 84 | start++ 85 | 86 | // if the character going out is part of the pattern, put it back in. 87 | if _, ok := char[startChar]; ok { 88 | if char[startChar] == 0 { 89 | matched-- 90 | } 91 | 92 | char[startChar]++ 93 | } 94 | } 95 | 96 | } 97 | 98 | return out 99 | } 100 | -------------------------------------------------------------------------------- /gtci/subsets_duplicates_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a set with duplicate elements, find all the distinct subsets. 4 | 5 | Example: 6 | - Input: [1 2] 7 | Output: [[] [1] [2] [1 2]] 8 | - Input: [1 2 5] 9 | Output: [[] [1] [2] [1 2] [3] [1 3] [2 3] [1 2 3]] 10 | 11 | Approach: 12 | - Similar to the previous problem, but we do two more steps. 13 | - First, sort the set so that duplicates are next to each other. 14 | - Second, when we encounter a duplicate while iterating the set, 15 | only create subsets from the subsets that added previously. 16 | - Can use a two-pointer approach to update their start and end window 17 | accordingly. 18 | 19 | Cost: 20 | - O(2^n) time, O(2^n) space since we would have a total of 2^n subsets. 21 | */ 22 | 23 | package gtci 24 | 25 | import ( 26 | "sort" 27 | "testing" 28 | 29 | "github.com/hoanhan101/algo/common" 30 | ) 31 | 32 | func TestFindSubsetsWithDuplicates(t *testing.T) { 33 | tests := []struct { 34 | in []int 35 | expected [][]int 36 | }{ 37 | {[]int{}, [][]int{{}}}, 38 | {[]int{1}, [][]int{{}, {1}}}, 39 | {[]int{1, 2}, [][]int{{}, {1}, {2}, {1, 2}}}, 40 | {[]int{1, 2, 2}, [][]int{{}, {1}, {2}, {1, 2}, {2, 2}, {1, 2, 2}}}, 41 | {[]int{2, 1, 2}, [][]int{{}, {1}, {2}, {1, 2}, {2, 2}, {1, 2, 2}}}, 42 | {[]int{2, 2, 1}, [][]int{{}, {1}, {2}, {1, 2}, {2, 2}, {1, 2, 2}}}, 43 | {[]int{1, 2, 3}, [][]int{{}, {1}, {2}, {1, 2}, {3}, {1, 3}, {2, 3}, {1, 2, 3}}}, 44 | {[]int{3, 2, 1}, [][]int{{}, {1}, {2}, {1, 2}, {3}, {1, 3}, {2, 3}, {1, 2, 3}}}, 45 | } 46 | 47 | for _, tt := range tests { 48 | common.Equal( 49 | t, 50 | tt.expected, 51 | findSubsetsWithDuplicates(tt.in), 52 | ) 53 | } 54 | } 55 | 56 | func findSubsetsWithDuplicates(nums []int) [][]int { 57 | // sort the set in ascending order. 58 | sort.Slice(nums, func(i, j int) bool { 59 | return nums[i] < nums[j] 60 | }) 61 | 62 | // start with an empty set. 63 | subsets := [][]int{} 64 | subsets = append(subsets, []int{}) 65 | 66 | start, end := 0, 0 67 | 68 | // for each number in the set, add it to all existing sets. 69 | for i := 0; i < len(nums); i++ { 70 | // if the current item and the previous one are the same, update the 71 | // start index accordingly so that we only create subset from the one 72 | // that added in the previous step. 73 | start = 0 74 | if i > 0 && nums[i] == nums[i-1] { 75 | start = end + 1 76 | } 77 | 78 | end = len(subsets) - 1 79 | 80 | for j := start; j < end+1; j++ { 81 | set := subsets[j] 82 | set = append(set, nums[i]) 83 | subsets = append(subsets, set) 84 | } 85 | } 86 | 87 | return subsets 88 | } 89 | -------------------------------------------------------------------------------- /interviewcake/reverse_word_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of string that is made up of word but in reverse, return the 4 | correct order in-place. 5 | 6 | Example: 7 | - Input: []string{"w", "o", "r", "l", "d", "", "h", "e", "l", "l", "o", "", "s", "a", "y"} 8 | Output: []string{"s", "a", "y", "", "h", "e", "l", "l", "o", "", "w", "o", "r", "l", "d"} 9 | 10 | Approach: 11 | - Similar to reversing string, use the same idea to reverse all the characters 12 | in the list first so that we could have a list of words in the right order, not 13 | its characters. 14 | - Iterate through the list again and reverse its characters. 15 | 16 | Solution: 17 | - Reverse all the characters to get a list of words in the right order using 18 | same idea as reversing a string. 19 | - Iterate through the list again the reverse its characters by first keeping 20 | track of the start index for each word since they are separated by an empty 21 | string. 22 | - Once we fine an empty string, update the start word index and use the same 23 | idea to reverse the characters order. 24 | 25 | Cost: 26 | - O(n) time, O(1) space. 27 | */ 28 | 29 | package interviewcake 30 | 31 | import ( 32 | "testing" 33 | 34 | "github.com/hoanhan101/algo/common" 35 | ) 36 | 37 | func TestReverseWord(t *testing.T) { 38 | tests := []struct { 39 | in []string 40 | expected []string 41 | }{ 42 | { 43 | []string{"w", "o", "r", "l", "d", "", "h", "e", "l", "l", "o", "", "s", "a", "y"}, 44 | []string{"s", "a", "y", "", "h", "e", "l", "l", "o", "", "w", "o", "r", "l", "d"}, 45 | }, 46 | } 47 | 48 | for _, tt := range tests { 49 | result := reverseWord(tt.in) 50 | common.Equal(t, tt.expected, result) 51 | } 52 | } 53 | 54 | func reverseWord(list []string) []string { 55 | // by reversing all character in the list, we end up with a list of words 56 | // in the right order but not its characters. 57 | reverseChar(list, 0, len(list)-1) 58 | 59 | // start keeps track of the start index for each word. it starts with 0 but 60 | // then gets updated once we find the empty string. then reverse the words 61 | // characters. 62 | start := 0 63 | for i := range list { 64 | if i == len(list)-1 { 65 | reverseChar(list, start, i) 66 | } 67 | 68 | if list[i] == "" { 69 | reverseChar(list, start, i-1) 70 | start = i + 1 71 | } 72 | } 73 | 74 | return list 75 | } 76 | 77 | // reverseChar reverses the list of character for a given start and end index. 78 | func reverseChar(list []string, start int, end int) { 79 | for start < end { 80 | common.Swap(list, start, end) 81 | 82 | start++ 83 | end-- 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /interviewcake/word_cloud_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a sentence (string), return its word count map. 4 | 5 | Example: 6 | - Input: "Cliff finished his cake and paid the bill. Bill finished his cake at the edge of the cliff." 7 | Output: map[string]int{"cliff": 1, "Cliff": 1, "finished": 2, "his": 2, "cake": 2, "and": 1, "paid": 1, "the": 3, "bill": 1, "Bill": 1, "at": 1, "edge": 1, "of": 1} 8 | 9 | Approach: 10 | - First get rid of special characters, then use a hashmap to keep counts of words 11 | as we iterate through the string. 12 | 13 | Solution: 14 | - Get rid of special characters using regex then split the sentence by space. 15 | - Choose to count the uppercase word only if it is uppercase in the original 16 | string. It's a reasonable approach, but not perfect one since proper nouns 17 | like "Bill" and "bill" point to the same meaning and are placed in a 18 | different place of a sentence - one in the front, other in the middle. 19 | - Use a hashmap to keep counts of words. 20 | 21 | Cost: 22 | - O(n) time, O(n) space. 23 | */ 24 | 25 | package interviewcake 26 | 27 | import ( 28 | "regexp" 29 | "strings" 30 | "testing" 31 | 32 | "github.com/hoanhan101/algo/common" 33 | ) 34 | 35 | func TestBuildWordCloud(t *testing.T) { 36 | tests := []struct { 37 | in string 38 | expected map[string]int 39 | }{ 40 | {"", map[string]int{}}, 41 | {" ", map[string]int{}}, 42 | {".,#", map[string]int{}}, 43 | {"1, 2, 3, 4", map[string]int{}}, 44 | {"Bill.bill.", map[string]int{"bill": 1, "Bill": 1}}, 45 | { 46 | "Cliff finished his cake and paid the bill. Bill finished his cake at the edge of the cliff.", 47 | map[string]int{"cliff": 1, "Cliff": 1, "finished": 2, "his": 2, "cake": 2, "and": 1, "paid": 1, "the": 3, "bill": 1, "Bill": 1, "at": 1, "edge": 1, "of": 1}, 48 | }, 49 | } 50 | 51 | for _, tt := range tests { 52 | result := buildWordCloud(tt.in) 53 | common.Equal(t, tt.expected, result) 54 | } 55 | } 56 | 57 | func buildWordCloud(in string) map[string]int { 58 | m := map[string]int{} 59 | 60 | // get rid of all special characters and numbers using regex. 61 | s := regexp.MustCompile(`[^a-zA-Z]+`).ReplaceAllString(in, " ") 62 | 63 | // immediately return if the string contains no word but special 64 | // characters and numbers. 65 | if s == " " { 66 | return m 67 | } 68 | 69 | // split it by space. 70 | words := strings.Split(s, " ") 71 | 72 | // iterate through the word list and update its count. 73 | for _, v := range words[:len(words)-1] { 74 | if _, ok := m[v]; ok { 75 | m[v]++ 76 | } else { 77 | m[v] = 1 78 | } 79 | } 80 | 81 | return m 82 | } 83 | -------------------------------------------------------------------------------- /leetcode/group_anagrams_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given an array of strings, group anagrams together. 4 | - All inputs will be in lowercase. 5 | - The order of your output does not matter. 6 | 7 | Example: 8 | - Input: []string{"eat", "tea", "tan", "ate", "nat", "bat"} 9 | Output: [][]string{ 10 | []string{"ate", "eat", "tea"}, 11 | []string{"nat","tan"}, 12 | []string{"bat"}, 13 | } 14 | 15 | Approach: 16 | - Two strings are anagrams if and only if their character counts 17 | (respective number of occurrences of each character) are the same. 18 | 19 | Cost: 20 | - O(n) time, O(n) space. 21 | */ 22 | 23 | package leetcode 24 | 25 | import ( 26 | "testing" 27 | 28 | "github.com/hoanhan101/algo/common" 29 | ) 30 | 31 | // func TestGroupAnagrams(t *testing.T) { 32 | // tests := []struct { 33 | // in []string 34 | // expected [][]string 35 | // }{ 36 | // { 37 | // []string{"eat", "tea", "tan", "ate", "nat", "bat"}, 38 | // [][]string{ 39 | // []string{"ate", "eat", "tea"}, 40 | // []string{"nat", "tan"}, 41 | // []string{"bat"}, 42 | // }, 43 | // }, 44 | // } 45 | 46 | // for _, tt := range tests { 47 | // result := groupAnagrams(tt.in) 48 | // common.Equal(t, tt.expected, result) 49 | // } 50 | // } 51 | 52 | func TestCountCharacters(t *testing.T) { 53 | tests := []struct { 54 | in string 55 | expected map[string]int 56 | }{ 57 | {"", map[string]int{}}, 58 | {"ab", map[string]int{"a": 1, "b": 1}}, 59 | {"aba", map[string]int{"a": 2, "b": 1}}, 60 | {"ababb", map[string]int{"a": 2, "b": 3}}, 61 | } 62 | 63 | for _, tt := range tests { 64 | result := countCharacters(tt.in) 65 | common.Equal(t, tt.expected, result) 66 | } 67 | } 68 | 69 | // countCharacters counts the number of occurrences of each character for a 70 | // given word. 71 | func countCharacters(word string) map[string]int { 72 | m := map[string]int{} 73 | 74 | for _, c := range word { 75 | if _, ok := m[string(c)]; ok { 76 | m[string(c)]++ 77 | } else { 78 | m[string(c)] = 1 79 | } 80 | } 81 | 82 | return m 83 | } 84 | 85 | // FIXME: go doesn't allow to have a map of map[string]int to []string. 86 | // func groupAnagrams(anagrams []string) [][]string { 87 | // m := map[map[string]int][]string{} 88 | 89 | // for _, word := range anagrams { 90 | // m[countCharacters(word)] = append(m[countCharacters(word)], word) 91 | // } 92 | 93 | // out := []string{} 94 | // for _, v := range m { 95 | // out := append(out, v) 96 | // } 97 | 98 | // return out 99 | // } 100 | -------------------------------------------------------------------------------- /leetcode/max_depth_binary_tree_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a binary tree, find its maximum depth. 4 | 5 | Approach: 6 | - The maximum depth of the current node is the greater of the max height of the left 7 | subtree and the right subtree plus one. 8 | 9 | Cost: 10 | - O(n) time, O(n) space. 11 | */ 12 | 13 | package leetcode 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestGetMaxDepth(t *testing.T) { 22 | t1 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 23 | 24 | t2 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 25 | t2.Right = &common.TreeNode{Left: nil, Value: 2, Right: nil} 26 | 27 | t3 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 28 | t3.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 29 | 30 | t4 := &common.TreeNode{Left: nil, Value: 5, Right: nil} 31 | t4.Left = &common.TreeNode{Left: nil, Value: 3, Right: nil} 32 | t4.Right = &common.TreeNode{Left: nil, Value: 8, Right: nil} 33 | 34 | t5 := &common.TreeNode{Left: nil, Value: 5, Right: nil} 35 | t5.Left = &common.TreeNode{Left: nil, Value: 3, Right: nil} 36 | t5.Right = &common.TreeNode{Left: nil, Value: 8, Right: nil} 37 | t5.Right.Left = &common.TreeNode{Left: nil, Value: 7, Right: nil} 38 | t5.Right.Right = &common.TreeNode{Left: nil, Value: 9, Right: nil} 39 | 40 | t6 := &common.TreeNode{Left: nil, Value: 5, Right: nil} 41 | t6.Left = &common.TreeNode{Left: nil, Value: 3, Right: nil} 42 | t6.Left.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 43 | t6.Left.Left.Left = &common.TreeNode{Left: nil, Value: 1, Right: nil} 44 | t6.Left.Right = &common.TreeNode{Left: nil, Value: 4, Right: nil} 45 | t6.Right = &common.TreeNode{Left: nil, Value: 8, Right: nil} 46 | t6.Right.Left = &common.TreeNode{Left: nil, Value: 7, Right: nil} 47 | t6.Right.Right = &common.TreeNode{Left: nil, Value: 9, Right: nil} 48 | t6.Right.Right.Right = &common.TreeNode{Left: nil, Value: 11, Right: nil} 49 | 50 | tests := []struct { 51 | in *common.TreeNode 52 | expected int 53 | }{ 54 | {t1, 1}, 55 | {t2, 2}, 56 | {t3, 2}, 57 | {t4, 2}, 58 | {t5, 3}, 59 | {t6, 4}, 60 | } 61 | 62 | for _, tt := range tests { 63 | common.Equal( 64 | t, 65 | tt.expected, 66 | getMaxDepth(tt.in), 67 | ) 68 | } 69 | } 70 | 71 | func getMaxDepth(t *common.TreeNode) int { 72 | // for the base case, the height is 0. 73 | if t == nil { 74 | return 0 75 | } 76 | 77 | // calculate the max depth of left subtree and right subtree. 78 | maxLeft := getMaxDepth(t.Left) 79 | maxRight := getMaxDepth(t.Right) 80 | 81 | // the max depth of the tree is the greater one plus one. 82 | return common.Max(maxLeft, maxRight) + 1 83 | } 84 | -------------------------------------------------------------------------------- /gtci/longest_substring_k_distinct_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string, find the length of the longest substring in it with no more 4 | than k distinct characters. 5 | 6 | Example: 7 | - Input: string="araaci", k=1 8 | Output: 2 9 | Explanation: Longest substring with no more than 1 distinct characters is "aa". 10 | - Input: string="araaci", k=2 11 | Output: 4 12 | Explanation: Longest substring with no more than 2 distinct characters is "araa". 13 | - Input: string="araaci", k=3 14 | Output: 5 15 | Explanation: Longest substring with no more than 3 distinct characters is "araac". 16 | 17 | Approach: 18 | - Use a hashmap to remember the frequency of each character we have seen. 19 | - Insert characters until we have k distinct characters in the map to be consider a 20 | window. 21 | - Shrink the window until there is no more k distinct characters in the map and keep 22 | updating the maximum window length at each step. 23 | 24 | Cost: 25 | - O(n) time, O(k) space where k is the number of characters in the map. 26 | */ 27 | 28 | package gtci 29 | 30 | import ( 31 | "testing" 32 | 33 | "github.com/hoanhan101/algo/common" 34 | ) 35 | 36 | func TestLongestSubstringKDistinct(t *testing.T) { 37 | tests := []struct { 38 | in1 string 39 | in2 int 40 | expected int 41 | }{ 42 | {"", 0, 0}, 43 | {"", 1, 0}, 44 | {"a", 0, 0}, 45 | {"a", 1, 1}, 46 | {"aa", 1, 2}, 47 | {"aa", 2, 2}, 48 | {"ab", 1, 1}, 49 | {"ab", 2, 2}, 50 | {"araaci", 1, 2}, 51 | {"araaci", 2, 4}, 52 | {"araaci", 3, 5}, 53 | } 54 | 55 | for _, tt := range tests { 56 | common.Equal( 57 | t, 58 | tt.expected, 59 | longestSubstringKDistinct(tt.in1, tt.in2), 60 | ) 61 | } 62 | } 63 | 64 | func longestSubstringKDistinct(s string, k int) int { 65 | maxLength, start := 0, 0 66 | 67 | // char keeps track of characters' frequencies. 68 | char := map[string]int{} 69 | 70 | for end := range s { 71 | // insert characters until we have k distinct characters. 72 | endChar := string(s[end]) 73 | if _, ok := char[endChar]; !ok { 74 | char[endChar] = 0 75 | } 76 | char[endChar]++ 77 | 78 | // shrink the window until there is no more than k distinct characters. 79 | for len(char) > k { 80 | startChar := string(s[start]) 81 | 82 | // decrement the frequency of the one going out of the window and 83 | // remove if its frequency is zero. 84 | char[startChar]-- 85 | if char[startChar] == 0 { 86 | delete(char, startChar) 87 | } 88 | 89 | // increase the start index to move the window ahead by one element. 90 | start++ 91 | } 92 | 93 | // update the maximum length at each step. 94 | maxLength = common.Max(maxLength, end-start+1) 95 | } 96 | 97 | return maxLength 98 | } 99 | -------------------------------------------------------------------------------- /interviewcake/merge_sorted_arrays_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Merge two sorted arrays. 4 | 5 | Example: 6 | - Input: []int{1, 3, 5}, []int{2, 4, 6} 7 | Output: []int{1, 2, 3, 4, 5, 6} 8 | - Input: []int{1, 3, 5}, []int{2, 4, 6, 7} 9 | Output: []int{1, 2, 3, 4, 5, 6, 7}, 10 | 11 | Approach: 12 | - Since these arrays are sorted, can use two pointers approach to iterate 13 | through both of them and append the smaller value to a new merged list at 14 | each step. 15 | 16 | Solution: 17 | - Have two pointers start at the beginning of these two arrays. 18 | - While both of them does not reach the end, compare two current values 19 | at each step and append the smaller one two a new merged list. 20 | - Move the two pointers up accordingly as values get merged in. 21 | - In the case where one of these pointers reach the end first and the 22 | other one is still in the middle of the array, simply add the rest of 23 | its values to the merged list since they are all sorted and guaranteed 24 | to be in ascending order. 25 | 26 | Cost: 27 | - O(n) time, O(n) space. 28 | */ 29 | 30 | package interviewcake 31 | 32 | import ( 33 | "testing" 34 | 35 | "github.com/hoanhan101/algo/common" 36 | ) 37 | 38 | func TestMergeSortedArray(t *testing.T) { 39 | tests := []struct { 40 | in1 []int 41 | in2 []int 42 | expected []int 43 | }{ 44 | { 45 | []int{}, 46 | []int{}, 47 | []int{}, 48 | }, 49 | { 50 | []int{}, 51 | []int{1}, 52 | []int{1}, 53 | }, 54 | { 55 | []int{1, 3, 5}, 56 | []int{2, 4, 6}, 57 | []int{1, 2, 3, 4, 5, 6}, 58 | }, 59 | { 60 | []int{1, 3, 5}, 61 | []int{2, 4, 6, 7}, 62 | []int{1, 2, 3, 4, 5, 6, 7}, 63 | }, 64 | { 65 | []int{1, 2, 3, 4, 5}, 66 | []int{6}, 67 | []int{1, 2, 3, 4, 5, 6}, 68 | }, 69 | } 70 | 71 | for _, tt := range tests { 72 | result := mergeSortedArray(tt.in1, tt.in2) 73 | common.Equal(t, tt.expected, result) 74 | } 75 | } 76 | 77 | func mergeSortedArray(a1, a2 []int) []int { 78 | out := []int{} 79 | 80 | // keep two "pointers" at index 0 and move up accordingly as one get 81 | // merged in. 82 | i, j := 0, 0 83 | for i < len(a1) && j < len(a2) { 84 | if a1[i] < a2[j] { 85 | out = append(out, a1[i]) 86 | i++ 87 | } else { 88 | out = append(out, a2[j]) 89 | j++ 90 | } 91 | } 92 | 93 | // if we get here, one array must have bigger size than the other. could 94 | // figure out which one is it then copy the rest of its to our final one. 95 | if i < len(a1) { 96 | out = append(out, a1[i:]...) 97 | } 98 | 99 | if j < len(a2) { 100 | out = append(out, a2[j:]...) 101 | } 102 | 103 | return out 104 | } 105 | -------------------------------------------------------------------------------- /gtci/merge_intervals_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a list of intervals, merge all the overlapping intervals to produce 4 | a list that has only mutually exclusive intervals. 5 | 6 | Example: 7 | - Input: []interval{{1, 2}, {2, 3}, {4, 5}} 8 | Output: []interval{{1, 3}, {4, 5}} 9 | - Input: []interval{{1, 5}, {2, 3}} 10 | Output: []interval{{1, 5}} 11 | 12 | Approach: 13 | - Sort the list in ascending order so that intervals that might need to be 14 | merged are next to each other. 15 | - Can merge two intervals together if the first one's end time is greater or 16 | or equal than the second one's start time. 17 | 18 | Cost: 19 | - O(nlogn) time, O(n) space. 20 | - Because we sort all intervals first, the runtime is O(nlogn). We create a new 21 | list of merged interval times, so the space cost is O(n). 22 | */ 23 | 24 | package gtci 25 | 26 | import ( 27 | "sort" 28 | "testing" 29 | 30 | "github.com/hoanhan101/algo/common" 31 | ) 32 | 33 | func TestMergeIntervals(t *testing.T) { 34 | tests := []struct { 35 | in []interval 36 | expected []interval 37 | }{ 38 | {[]interval{}, []interval{}}, 39 | {[]interval{{1, 2}}, []interval{{1, 2}}}, 40 | {[]interval{{1, 2}, {2, 3}}, []interval{{1, 3}}}, 41 | {[]interval{{1, 5}, {2, 3}}, []interval{{1, 5}}}, 42 | {[]interval{{1, 2}, {4, 5}}, []interval{{1, 2}, {4, 5}}}, 43 | {[]interval{{1, 5}, {2, 3}, {4, 5}}, []interval{{1, 5}}}, 44 | {[]interval{{1, 2}, {2, 3}, {4, 5}}, []interval{{1, 3}, {4, 5}}}, 45 | {[]interval{{1, 6}, {2, 3}, {4, 5}}, []interval{{1, 6}}}, 46 | {[]interval{{4, 5}, {2, 3}, {1, 6}}, []interval{{1, 6}}}, 47 | } 48 | 49 | for _, tt := range tests { 50 | result := mergeIntervals(tt.in) 51 | common.Equal(t, tt.expected, result) 52 | } 53 | } 54 | 55 | // interval has a start and end time. 56 | type interval struct { 57 | start int 58 | end int 59 | } 60 | 61 | func mergeIntervals(intervals []interval) []interval { 62 | // sort the intervals in ascending order. 63 | sort.Slice(intervals, func(i, j int) bool { 64 | return intervals[i].start < intervals[j].start 65 | }) 66 | 67 | merged := []interval{} 68 | for i := range intervals { 69 | // push the first interval to the list so we can have a start. 70 | if i == 0 { 71 | merged = append(merged, intervals[i]) 72 | continue 73 | } 74 | 75 | // if the last merged interval's end time is greater or equal than the current 76 | // one's start time, merge them by using the larger ending time. else, 77 | // leave them separate and push it to the merged list. 78 | if merged[len(merged)-1].end >= intervals[i].start { 79 | merged[len(merged)-1].end = common.Max(intervals[i].end, merged[len(merged)-1].end) 80 | } else { 81 | merged = append(merged, intervals[i]) 82 | } 83 | } 84 | 85 | return merged 86 | } 87 | -------------------------------------------------------------------------------- /gtci/permutation_string_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a string and a pattern, find out if the string contains any permutation 4 | of the pattern. 5 | 6 | Example: 7 | - Input: string="oidbcaf", pattern="abc" 8 | Output: true 9 | Explanation: The string contains "bca" which is a permutation of the given pattern. 10 | - Input: string="odicf", pattern="dc" 11 | Output: false 12 | 13 | Approach: 14 | - Use a hashmap to calculate the frequencies of all characters in the patterns. 15 | - Iterate through the string and add characters in the sliding window. 16 | - At each step, 17 | - check if we got a complete match 18 | - shrink the window so that its size is equal to the length of the pattern. 19 | 20 | Cost: 21 | - O(n) time, O(m) space where m is the number of distinct characters in the map. 22 | */ 23 | 24 | package gtci 25 | 26 | import ( 27 | "testing" 28 | 29 | "github.com/hoanhan101/algo/common" 30 | ) 31 | 32 | func TestFindPermutation(t *testing.T) { 33 | tests := []struct { 34 | in1 string 35 | in2 string 36 | expected bool 37 | }{ 38 | {"oidbcaf", "abc", true}, 39 | {"oidbafc", "abc", false}, 40 | {"odicf", "dc", false}, 41 | } 42 | 43 | for _, tt := range tests { 44 | common.Equal( 45 | t, 46 | tt.expected, 47 | findPermutation(tt.in1, tt.in2), 48 | ) 49 | } 50 | } 51 | 52 | func findPermutation(s string, pattern string) bool { 53 | matched, start := 0, 0 54 | 55 | // char keeps track of characters' frequencies. 56 | char := map[string]int{} 57 | 58 | // calculate the frequencies of all characters in the pattern. 59 | for _, c := range pattern { 60 | if _, ok := char[string(c)]; !ok { 61 | char[string(c)] = 0 62 | } 63 | 64 | char[string(c)]++ 65 | } 66 | 67 | for end := range s { 68 | // if the character matches one in the map, decrease its frequency. 69 | // if its frequency becomes 0, we have a complete match. 70 | endChar := string(s[end]) 71 | if _, ok := char[endChar]; ok { 72 | char[endChar]-- 73 | 74 | if char[endChar] == 0 { 75 | matched++ 76 | } 77 | } 78 | 79 | // return true immediately if we have all complete matches. 80 | if matched == len(char) { 81 | return true 82 | } 83 | 84 | // shrink the window by one character at a time so that its size is 85 | // equal to the length of the pattern. 86 | if end >= len(pattern)-1 { 87 | startChar := string(s[start]) 88 | start++ 89 | 90 | // if the character going out is part of the pattern, put it back in. 91 | if _, ok := char[startChar]; ok { 92 | if char[startChar] == 0 { 93 | matched-- 94 | } 95 | 96 | char[startChar]++ 97 | } 98 | } 99 | 100 | } 101 | 102 | return false 103 | } 104 | -------------------------------------------------------------------------------- /gtci/level_avg_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a binary tree, populate an array to represent the averages of all of 4 | its levels. 5 | 6 | Example: 7 | - Input: 8 | 1 9 | 2 3 10 | 4 5 11 | Output: []float64{1, 2.5, 4.5} 12 | 13 | Approach: 14 | - Similar to level order traversal problem, except we keep track of the sum 15 | at each level and return the average in the end. 16 | 17 | Cost: 18 | - O(n) time, O(n) space. 19 | */ 20 | 21 | package gtci 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/hoanhan101/algo/common" 27 | ) 28 | 29 | func TestLevelAvg(t *testing.T) { 30 | t1 := &common.TreeNode{} 31 | 32 | t2 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 33 | 34 | t3 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 35 | t3.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 36 | t3.Left.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 37 | 38 | t4 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 39 | t4.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 40 | t4.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 41 | 42 | t5 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 43 | t5.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 44 | t5.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 45 | t5.Left.Left = &common.TreeNode{Left: nil, Value: 4, Right: nil} 46 | t5.Right.Right = &common.TreeNode{Left: nil, Value: 5, Right: nil} 47 | 48 | tests := []struct { 49 | in *common.TreeNode 50 | expected []float64 51 | }{ 52 | {t1, []float64{0}}, 53 | {t2, []float64{1}}, 54 | {t3, []float64{1, 2, 3}}, 55 | {t4, []float64{1, 2.5}}, 56 | {t5, []float64{1, 2.5, 4.5}}, 57 | } 58 | 59 | for _, tt := range tests { 60 | common.Equal( 61 | t, 62 | tt.expected, 63 | levelAvg(tt.in), 64 | ) 65 | } 66 | } 67 | 68 | func levelAvg(root *common.TreeNode) []float64 { 69 | out := []float64{} 70 | 71 | if root == nil { 72 | return out 73 | } 74 | 75 | // initialize a linked list with the root. 76 | queue := common.NewQueue() 77 | queue.Push(root) 78 | 79 | for queue.Size() > 0 { 80 | levelSize := queue.Size() 81 | levelSum := 0 82 | 83 | for i := 0; i < levelSize; i++ { 84 | // pop the queue and cache that value to its current level. 85 | current := queue.Pop().(*common.TreeNode) 86 | 87 | levelSum += current.Value 88 | 89 | // push its left child. 90 | if current.Left != nil { 91 | queue.Push(current.Left) 92 | } 93 | 94 | // push its right child. 95 | if current.Right != nil { 96 | queue.Push(current.Right) 97 | } 98 | } 99 | 100 | out = append(out, float64(levelSum)/float64(levelSize)) 101 | } 102 | 103 | return out 104 | } 105 | -------------------------------------------------------------------------------- /lab/heapsort_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Implement heapsort. 4 | 5 | Approach: 6 | - Similar to selection sort, repeatedly choose the largest item and move it to 7 | the end of the array using a max heap. 8 | 9 | Cost: 10 | - O(nlogn) time and O(1) space. 11 | */ 12 | 13 | package lab 14 | 15 | import ( 16 | "testing" 17 | 18 | "github.com/hoanhan101/algo/common" 19 | ) 20 | 21 | func TestHeapsort(t *testing.T) { 22 | tests := []struct { 23 | in []int 24 | expected []int 25 | }{ 26 | {[]int{}, []int{}}, 27 | {[]int{1}, []int{1}}, 28 | {[]int{1, 2}, []int{1, 2}}, 29 | {[]int{2, 1}, []int{1, 2}}, 30 | {[]int{2, 1, 3}, []int{1, 2, 3}}, 31 | {[]int{1, 1, 1}, []int{1, 1, 1}}, 32 | {[]int{2, 1, 2}, []int{1, 2, 2}}, 33 | {[]int{1, 2, 4, 3, 6, 5}, []int{1, 2, 3, 4, 5, 6}}, 34 | {[]int{6, 2, 4, 3, 1, 5}, []int{1, 2, 3, 4, 5, 6}}, 35 | {[]int{6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6}}, 36 | } 37 | 38 | for _, tt := range tests { 39 | heapsort(tt.in) 40 | common.Equal(t, tt.expected, tt.in) 41 | } 42 | } 43 | 44 | func heapsort(in []int) { 45 | heapify(in) 46 | 47 | size := len(in) 48 | for size > 0 { 49 | // repeatedly remove the largest item. 50 | largest := removeLargest(in, size) 51 | 52 | // update the heap size. 53 | size-- 54 | 55 | // store the removed value at the end of the list. 56 | in[size] = largest 57 | } 58 | } 59 | 60 | // heapify transform the input into a max heap. 61 | func heapify(in []int) { 62 | for i := len(in) - 1; i > -1; i-- { 63 | bubbleDown(in, len(in), i) 64 | } 65 | } 66 | 67 | // bubbleDown allow larger values to reach the top. 68 | func bubbleDown(heap []int, heapSize int, index int) { 69 | for index < heapSize { 70 | // fast-calculate the children left and right index. 71 | left := index*2 + 1 72 | right := index*2 + 2 73 | 74 | // stop if there is no child node. 75 | if left >= heapSize { 76 | break 77 | } 78 | 79 | // find the larger index 80 | larger := left 81 | if right < heapSize && heap[left] < heap[right] { 82 | larger = right 83 | } 84 | 85 | // if the current item is larger than both children, we're done. 86 | // if not, swap with the larger child. 87 | if heap[index] < heap[larger] { 88 | common.Swap(heap, index, larger) 89 | } else { 90 | break 91 | } 92 | } 93 | } 94 | 95 | // removeLargest remove and return the largest item from the heap. 96 | func removeLargest(heap []int, heapSize int) int { 97 | // largest item is at the top of our max heap. 98 | largest := heap[0] 99 | 100 | // move the last item into the root position. 101 | heap[0] = heap[heapSize-1] 102 | 103 | // bubble down from the root to restore the heap. 104 | bubbleDown(heap, heapSize-1, 0) 105 | 106 | return largest 107 | } 108 | -------------------------------------------------------------------------------- /lab/balanced_binary_tree_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Determine if a binary tree is height-balanced. 4 | 5 | Example: 6 | - Input: 7 | 1 8 | 2 3 9 | 4 10 | Output: true 11 | 12 | - Input: 13 | 1 14 | 2 3 15 | 4 16 | 5 17 | Output: false 18 | 19 | Approach: 20 | A binary is balanced if 21 | - its left subtree if balanced 22 | - its right subtree if balanced 23 | - the difference between the heights of left subtree and right subtree is 24 | not more than 1. 25 | */ 26 | 27 | package lab 28 | 29 | import ( 30 | "testing" 31 | 32 | "github.com/hoanhan101/algo/common" 33 | ) 34 | 35 | func TestIsBalanced(t *testing.T) { 36 | t1 := &BinaryTree{nil, 1, nil} 37 | 38 | t2 := &BinaryTree{nil, 1, nil} 39 | t2.left = &BinaryTree{nil, 2, nil} 40 | 41 | t3 := &BinaryTree{nil, 1, nil} 42 | t3.left = &BinaryTree{nil, 2, nil} 43 | t3.right = &BinaryTree{nil, 3, nil} 44 | t3.right.right = &BinaryTree{nil, 4, nil} 45 | 46 | t4 := &BinaryTree{nil, 1, nil} 47 | t4.right = &BinaryTree{nil, 2, nil} 48 | t4.right.right = &BinaryTree{nil, 3, nil} 49 | t4.right.right.right = &BinaryTree{nil, 4, nil} 50 | 51 | t5 := &BinaryTree{nil, 1, nil} 52 | t5.left = &BinaryTree{nil, 2, nil} 53 | t5.right = &BinaryTree{nil, 3, nil} 54 | t5.left.left = &BinaryTree{nil, 4, nil} 55 | 56 | t6 := &BinaryTree{nil, 1, nil} 57 | t6.left = &BinaryTree{nil, 2, nil} 58 | t6.right = &BinaryTree{nil, 3, nil} 59 | t6.left.left = &BinaryTree{nil, 4, nil} 60 | t6.left.left.right = &BinaryTree{nil, 5, nil} 61 | 62 | tests := []struct { 63 | in *BinaryTree 64 | height int 65 | balanced bool 66 | }{ 67 | {t1, 1, true}, 68 | {t2, 2, true}, 69 | {t3, 3, true}, 70 | {t4, 4, false}, 71 | {t5, 3, true}, 72 | {t6, 4, false}, 73 | } 74 | 75 | for _, tt := range tests { 76 | h := height(tt.in) 77 | b := isBalanced(tt.in) 78 | common.Equal(t, tt.height, h) 79 | common.Equal(t, tt.balanced, b) 80 | } 81 | } 82 | 83 | func isBalanced(t *BinaryTree) bool { 84 | if t == nil { 85 | return true 86 | } 87 | 88 | // calculate the left and right subtrees' heights. 89 | leftHeight := height(t.left) 90 | rightHeight := height(t.right) 91 | 92 | // if the difference between left and right subtrees' heights is less than 93 | // 1 apart while the left and right subtree are balanced then the tree must 94 | // be balanced. 95 | if common.IsLessThan1Apart(leftHeight, rightHeight) && isBalanced(t.left) && isBalanced(t.right) { 96 | return true 97 | } 98 | 99 | return false 100 | } 101 | 102 | // height returns the height of the binary tree. 103 | func height(t *BinaryTree) int { 104 | if t == nil { 105 | return 0 106 | } 107 | 108 | return common.Max(height(t.left), height(t.right)) + 1 109 | } 110 | -------------------------------------------------------------------------------- /gtci/cycle_start_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given the head of a singly linked list, write a function to find the 4 | starting node of the cycle. 5 | 6 | Approach: 7 | - Similar to finding a cycle in a linked list problem, can also determine 8 | the start of its cycle and calculate length k of the cycle. 9 | - Have one pointer at the beginning and one at kth node of the linked list. 10 | - Move both of them until they meet at the start.of the cycle. 11 | 12 | Cost: 13 | - O(n) time, O(1) space. 14 | */ 15 | 16 | package gtci 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/hoanhan101/algo/common" 22 | ) 23 | 24 | func TestFindCycleStart(t *testing.T) { 25 | t1 := common.NewListNode(1) 26 | t1.AddNext(2) 27 | t1.AddNext(3) 28 | t1.AddNext(4) 29 | t1.AddNext(5) 30 | t1.AddNext(6) 31 | t1.Next.Next.Next.Next.Next.Next = t1.Next.Next 32 | 33 | t2 := common.NewListNode(1) 34 | t2.AddNext(2) 35 | t2.AddNext(3) 36 | t2.AddNext(4) 37 | t2.AddNext(5) 38 | t2.AddNext(6) 39 | t2.Next.Next.Next.Next.Next.Next = t2.Next.Next.Next 40 | 41 | t3 := common.NewListNode(1) 42 | t3.AddNext(2) 43 | t3.AddNext(3) 44 | t3.AddNext(4) 45 | t3.AddNext(5) 46 | t3.AddNext(6) 47 | t3.Next.Next.Next.Next.Next.Next = t3 48 | 49 | t4 := common.NewListNode(1) 50 | t4.AddNext(2) 51 | t4.AddNext(3) 52 | t4.AddNext(4) 53 | t4.AddNext(5) 54 | t4.AddNext(6) 55 | 56 | tests := []struct { 57 | in *common.ListNode 58 | expected int 59 | }{ 60 | {t1, 3}, 61 | {t2, 4}, 62 | {t3, 1}, 63 | {t4, 1}, 64 | } 65 | 66 | for _, tt := range tests { 67 | common.Equal( 68 | t, 69 | tt.expected, 70 | findCycleStart(tt.in), 71 | ) 72 | } 73 | } 74 | 75 | func findCycleStart(head *common.ListNode) int { 76 | length := 0 77 | 78 | slow, fast := head, head 79 | for fast != nil && fast.Next != nil { 80 | fast = fast.Next.Next 81 | slow = slow.Next 82 | 83 | if slow == fast { 84 | length = cycleLength(slow) 85 | break 86 | } 87 | } 88 | 89 | start := findStart(head, length) 90 | return start.Value 91 | } 92 | 93 | // cycleLength loops around the cycle and calculate its length. 94 | func cycleLength(head *common.ListNode) int { 95 | length := 0 96 | 97 | current := head 98 | for { 99 | current = current.Next 100 | length++ 101 | 102 | if current == head { 103 | break 104 | } 105 | } 106 | 107 | return length 108 | } 109 | 110 | func findStart(head *common.ListNode, k int) *common.ListNode { 111 | slow, fast := head, head 112 | 113 | // move the fast pointer k distance ahead. 114 | for k > 0 { 115 | fast = fast.Next 116 | k-- 117 | } 118 | 119 | // increment both pointers until they meet at the start. 120 | for slow != fast { 121 | slow = slow.Next 122 | fast = fast.Next 123 | } 124 | 125 | return slow 126 | } 127 | -------------------------------------------------------------------------------- /leetcode/valid_bst_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a binary tree, determine if it is a valid binary search tree. 4 | 5 | Approach: 6 | - Traverse the tree and apply recursion to check at each step if: 7 | - the current node's value is greater than the lower bound 8 | - the current node's value is smaller than the upper bound 9 | - the current node's left child follows 10 | - the current node's left child follows 11 | 12 | Cost: 13 | - O(n) time and O(n) stack space. 14 | */ 15 | 16 | package leetcode 17 | 18 | import ( 19 | "math" 20 | "testing" 21 | 22 | "github.com/hoanhan101/algo/common" 23 | ) 24 | 25 | func TestIsBinarySearchTree(t *testing.T) { 26 | t1 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 27 | 28 | t2 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 29 | t2.Right = &common.TreeNode{Left: nil, Value: 2, Right: nil} 30 | 31 | t3 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 32 | t3.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 33 | 34 | t4 := &common.TreeNode{Left: nil, Value: 5, Right: nil} 35 | t4.Left = &common.TreeNode{Left: nil, Value: 3, Right: nil} 36 | t4.Right = &common.TreeNode{Left: nil, Value: 8, Right: nil} 37 | 38 | t5 := &common.TreeNode{Left: nil, Value: 5, Right: nil} 39 | t5.Left = &common.TreeNode{Left: nil, Value: 3, Right: nil} 40 | t5.Right = &common.TreeNode{Left: nil, Value: 8, Right: nil} 41 | t5.Right.Left = &common.TreeNode{Left: nil, Value: 7, Right: nil} 42 | t5.Right.Right = &common.TreeNode{Left: nil, Value: 9, Right: nil} 43 | 44 | t6 := &common.TreeNode{Left: nil, Value: 5, Right: nil} 45 | t6.Left = &common.TreeNode{Left: nil, Value: 3, Right: nil} 46 | t6.Left.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 47 | t6.Left.Left.Left = &common.TreeNode{Left: nil, Value: 1, Right: nil} 48 | t6.Left.Right = &common.TreeNode{Left: nil, Value: 4, Right: nil} 49 | t6.Right = &common.TreeNode{Left: nil, Value: 8, Right: nil} 50 | t6.Right.Left = &common.TreeNode{Left: nil, Value: 7, Right: nil} 51 | t6.Right.Right = &common.TreeNode{Left: nil, Value: 9, Right: nil} 52 | t6.Right.Right.Right = &common.TreeNode{Left: nil, Value: 11, Right: nil} 53 | 54 | // define their outputs. 55 | tests := []struct { 56 | in *common.TreeNode 57 | expected bool 58 | }{ 59 | {t1, true}, 60 | {t2, true}, 61 | {t3, false}, 62 | {t4, true}, 63 | {t5, true}, 64 | {t6, true}, 65 | } 66 | 67 | for _, tt := range tests { 68 | result := isBinarySearchTree(tt.in) 69 | common.Equal(t, tt.expected, result) 70 | } 71 | } 72 | 73 | func isBinarySearchTree(t *common.TreeNode) bool { 74 | return validate(t, math.MinInt64, math.MaxInt64) 75 | } 76 | 77 | func validate(t *common.TreeNode, lower, upper int) bool { 78 | if t == nil { 79 | return true 80 | } 81 | 82 | return t.Value > lower && t.Value < upper && validate(t.Left, lower, t.Value) && validate(t.Right, t.Value, upper) 83 | } 84 | -------------------------------------------------------------------------------- /ctci/bit_insertion_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given two numbers, m and n, and two bit positions, i and j, insert m into n 4 | such that m starts at bit j and ends at bit i. 5 | 6 | Example: 7 | - Input: n = 10000000000, m = 10011, i = 2, j = 6 8 | Output: n = 10001001100 9 | 10 | Approach: 11 | - Clear the bits j through i in n using a custom mask. 12 | - Shift m so that it lines up with bits j through i. 13 | - Merge them together. 14 | */ 15 | 16 | package ctci 17 | 18 | import ( 19 | "strconv" 20 | "testing" 21 | 22 | "github.com/hoanhan101/algo/common" 23 | ) 24 | 25 | func TestInsertBit(t *testing.T) { 26 | tests := []struct { 27 | in1 int64 28 | in2 int64 29 | in3 int64 30 | in4 int64 31 | expected int64 32 | }{ 33 | { 34 | binToInt("10000000000"), 35 | binToInt("10011"), 36 | 0, 37 | 4, 38 | binToInt("10000010011"), 39 | }, 40 | { 41 | binToInt("10000000000"), 42 | binToInt("10011"), 43 | 1, 44 | 5, 45 | binToInt("10000100110"), 46 | }, 47 | { 48 | binToInt("10000000000"), 49 | binToInt("10011"), 50 | 2, 51 | 6, 52 | binToInt("10001001100"), 53 | }, 54 | { 55 | binToInt("10000000000"), 56 | binToInt("10011"), 57 | 3, 58 | 7, 59 | binToInt("10010011000"), 60 | }, 61 | { 62 | binToInt("10000000000"), 63 | binToInt("10011"), 64 | 4, 65 | 8, 66 | binToInt("10100110000"), 67 | }, 68 | { 69 | binToInt("10000000000"), 70 | binToInt("10011"), 71 | 5, 72 | 9, 73 | binToInt("11001100000"), 74 | }, 75 | { 76 | binToInt("10000000000"), 77 | binToInt("10011"), 78 | 6, 79 | 10, 80 | binToInt("10011000000"), 81 | }, 82 | } 83 | 84 | for _, tt := range tests { 85 | result := insertBit(tt.in1, tt.in2, tt.in3, tt.in4) 86 | common.Equal(t, tt.expected, result) 87 | } 88 | } 89 | 90 | func insertBit(n, m, i, j int64) int64 { 91 | // clear the bits i through j by creating a bitmask that have all 1s, 92 | // except for 0s in the bits i through j. 93 | ones := ^0 // create a sequence of 1s. 94 | left := ones << uint(j+1) // have 1s before position j, then 0s. 95 | right := 1<= meetings[i].start { 88 | out[len(out)-1].end = common.Max(meetings[i].end, out[len(out)-1].end) 89 | } else { 90 | out = append(out, meetings[i]) 91 | } 92 | } 93 | 94 | return out 95 | } 96 | -------------------------------------------------------------------------------- /gtci/reverse_level_order_traversal_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Given a binary tree, populate the values of all nodes of each level 4 | in reverse order in separate sub-arrays. 5 | 6 | Example: 7 | - Input: 8 | 1 9 | 2 3 10 | 4 5 11 | Output: []interface{}{[]interface{}{4, 5}, []interface{}{2, 3}, []interface{}{1}} 12 | 13 | Approach: 14 | - Similar to level order reversal problem, except we append the current level's 15 | value at the beginning of the output list. 16 | 17 | Cost: 18 | - O(n) time, O(n) space. 19 | */ 20 | 21 | package gtci 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/hoanhan101/algo/common" 27 | ) 28 | 29 | func TestReverseLevelOrderTraverse(t *testing.T) { 30 | t1 := &common.TreeNode{} 31 | 32 | t2 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 33 | 34 | t3 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 35 | t3.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 36 | t3.Left.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 37 | 38 | t4 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 39 | t4.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 40 | t4.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 41 | 42 | t5 := &common.TreeNode{Left: nil, Value: 1, Right: nil} 43 | t5.Left = &common.TreeNode{Left: nil, Value: 2, Right: nil} 44 | t5.Right = &common.TreeNode{Left: nil, Value: 3, Right: nil} 45 | t5.Left.Left = &common.TreeNode{Left: nil, Value: 4, Right: nil} 46 | t5.Right.Right = &common.TreeNode{Left: nil, Value: 5, Right: nil} 47 | 48 | tests := []struct { 49 | in *common.TreeNode 50 | expected []interface{} 51 | }{ 52 | {t1, []interface{}{[]interface{}{0}}}, 53 | {t2, []interface{}{[]interface{}{1}}}, 54 | {t3, []interface{}{[]interface{}{3}, []interface{}{2}, []interface{}{1}}}, 55 | {t4, []interface{}{[]interface{}{2, 3}, []interface{}{1}}}, 56 | {t5, []interface{}{[]interface{}{4, 5}, []interface{}{2, 3}, []interface{}{1}}}, 57 | } 58 | 59 | for _, tt := range tests { 60 | common.Equal( 61 | t, 62 | tt.expected, 63 | reverseLevelOrderTraverse(tt.in), 64 | ) 65 | } 66 | } 67 | 68 | func reverseLevelOrderTraverse(root *common.TreeNode) []interface{} { 69 | out := common.NewList() 70 | 71 | if root == nil { 72 | return out.Slice() 73 | } 74 | 75 | // initialize a linked list with the root. 76 | queue := common.NewQueue() 77 | queue.Push(root) 78 | 79 | for queue.Size() > 0 { 80 | levelSize := queue.Size() 81 | currentLevel := common.NewList() 82 | 83 | for i := 0; i < levelSize; i++ { 84 | // pop the queue and cache that value to its current level. 85 | current := queue.Pop().(*common.TreeNode) 86 | currentLevel.PushBack(current.Value) 87 | 88 | // push its left child. 89 | if current.Left != nil { 90 | queue.Push(current.Left) 91 | } 92 | 93 | // push its right child. 94 | if current.Right != nil { 95 | queue.Push(current.Right) 96 | } 97 | } 98 | 99 | out.PushFront(currentLevel.Slice()) 100 | } 101 | 102 | return out.Slice() 103 | } 104 | -------------------------------------------------------------------------------- /interviewcake/recursive_string_permutation_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Problem: 3 | - Write a recursive function for generating all permutations of an input 4 | string. Assume that every character in the string is unique. 5 | 6 | Example: 7 | - Input: "cat" 8 | Output: []set{"cat", "cta", "act", "atc", "tca", "tac"} 9 | 10 | Approach: 11 | - Get all the permutations for all characters before the last one. 12 | - Put the last character in all possible position for each of these 13 | permutations. 14 | 15 | Solution: 16 | - Initialize permutations as a set. 17 | - If there is only one character in a string (or less than), return it 18 | immediately. 19 | - Get the last character and all the characters before it. 20 | - Get all permutations for all characters expect the last one. 21 | - Iterate through the list of permutations before the last character 22 | and put the last character in all possible position for each of these 23 | permutations. 24 | 25 | Cost: 26 | - O(n) time, O(n) space. 27 | */ 28 | 29 | package interviewcake 30 | 31 | import ( 32 | "strings" 33 | "testing" 34 | 35 | "github.com/hoanhan101/algo/common" 36 | ) 37 | 38 | func TestPermuteString(t *testing.T) { 39 | tests := []struct { 40 | in string 41 | expected map[string]bool 42 | }{ 43 | {"c", map[string]bool{"c": true}}, 44 | {"ca", map[string]bool{"ca": true, "ac": true}}, 45 | {"cat", map[string]bool{"cat": true, "cta": true, "act": true, "atc": true, "tca": true, "tac": true}}, 46 | {"caa", map[string]bool{"caa": true, "aca": true, "aac": true}}, 47 | } 48 | 49 | for _, tt := range tests { 50 | result := permuteString(tt.in) 51 | common.Equal(t, tt.expected, result) 52 | } 53 | } 54 | 55 | func permuteString(in string) map[string]bool { 56 | // initialize permutations as a set. 57 | permutations := make(map[string]bool) 58 | 59 | // if there is only one character in a string (or less than), return it 60 | // immediately. 61 | if len(in) <= 1 { 62 | permutations[in] = true 63 | return permutations 64 | } 65 | 66 | // get the last character and all the characters before it. 67 | lastChar := string(in[len(in)-1]) 68 | beforeLastChar := string(in[:len(in)-1]) 69 | 70 | // get all permutations for all characters expect the last one. 71 | permutationsBeforeLastChar := permuteString(beforeLastChar) 72 | 73 | // put the last character in all possible position for each of these 74 | // permutations. 75 | for permutation := range permutationsBeforeLastChar { 76 | for position := range beforeLastChar { 77 | // by keeping track of the position, we can put the last character 78 | // in between the permutation. 79 | s := []string{permutation[:position], lastChar, permutation[position:]} 80 | p := strings.Join(s, "") 81 | 82 | // put in the set. 83 | permutations[p] = true 84 | 85 | // at position 0, permutation[:0] is an empty string. hence, 86 | // just joining the permutation with the last character is 87 | // enough. 88 | if position == 0 { 89 | s := []string{permutation, lastChar} 90 | p := strings.Join(s, "") 91 | permutations[p] = true 92 | } 93 | } 94 | } 95 | 96 | return permutations 97 | } 98 | --------------------------------------------------------------------------------