# Go Leetcode: Array & Hash Table ###### tags: `leetcode` `go` ## Contains Duplicate ### #1 ```go= func containsDuplicate(nums []int) bool { // hash table // key -> value // if value > 1 return true idmap := make(map[int]int) // iterate through the given slice for i:=0; i < len(nums); i++ { idmap[nums[i]]++ if idmap[nums[i]] > 1 {return true} } return false } ``` ### #2 less memory ```go= func containsDuplicate(nums []int) bool { // struct comsumes less memory than integer and bool idmap := make(map[int]struct{}) // iterate through the given slice for _, v := range nums { if _, ok := idmap[v]; ok { return true } else { idmap[v] = struct{}{} // weird syntax } } return false } ``` ### Note - what is slice - how to iterate through slice - how to declare map - how to assign empty struct - consider what type to use ## Valid Anagram ### #1 ```go= func isAnagram(s string, t string) bool { if len(s) != len(t) {return false} hst := make(map[rune]int) for _, r := range s { hst[r]++ // init } for _, r := range t { if _, ok := hst[r]; ok { hst[r]-- if hst[r] < 0 {return false} } else { return false } } return true } ``` ### Note - what is rune - how to iterate through string ## Two Sum ### #1 ```go= func twoSum(nums []int, target int) []int { hst := make(map[int]int) for i, v := range nums { to_find := target - v // find if the other piece in hash table if j, ok := hst[to_find]; ok { return []int{i, j} } else { // add self to hash table hst[v] = i } } return []int{0, 0} } ``` ## Group Anagrams ### #1 ```go= import ( "sort" "fmt" ) func groupAnagrams(strs []string) [][]string { // hash each word hst := make(map[string][]string) for _, str := range strs { bytes := []byte(str) sort.SliceStable(bytes, func(i int, j int) bool{ return bytes[i] < bytes[j] }) key := string(bytes) hst[key] = append(hst[key], str) } res := make([][]string, 0, len(hst)) for _, v := range hst { res = append(res, v) } return res } ``` ## Top K Frequent Elements ### #1 ```go= func topKFrequent(nums []int, k int) []int { // hash table // key: num // value: counter hst := make(map[int]int) for _, n := range nums { hst[n]++ } // get the indexes of k max value // get the index of maximum value k times q := []int{} for i:=0; i<k; i++ { max_idx := 0 is_init := true for key, val := range hst { if is_init || hst[max_idx] < val { max_idx = key } is_init = false } // append q = append(q, max_idx) // remove from hst delete(hst, max_idx) } return q } ``` ### #2 custom map struct faster but takes more mrmory ```go= import "sort" type Pair struct { Key int Value int } type PairList []Pair func (p PairList) Len() int { return len(p) } func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p PairList) Less(i, j int) bool { return p[i].Value > p[j].Value } // sort desc func topKFrequent(nums []int, k int) []int { // hash table // key: num // value: counter hst := make(map[int]int) for _, n := range nums { hst[n]++ } plist := make(PairList, len(hst)) i := 0 for k, v := range hst { plist[i].Key = k plist[i].Value = v i++ } sort.Sort(plist) // get the indexes of k max value // get the index of maximum value k times q := []int{} for i:=0; i<k; i++ { // append q = append(q, plist[i].Key) } return q } ``` ### #3 faster than #2 ```go= // ... q := []int{} for _, item := range plist[:k] { // append q = append(q, item.Key) } return q ``` ### #4 cleaner way to sort ```go= func topKFrequent(nums []int, k int) []int { // key : value -> num : amount hst := make(map[int]int) for _, n := range nums { hst[n]++ } // collect keys keys := []int{} for num, _ := range hst { keys = append(keys, num) } // sort keys by values in hash table sort.Slice(keys, func(i, j int) bool { return hst[keys[i]] > hst[keys[j]] // desc }) return keys[:k] } ``` ### #5 use slice of slice as bucket use slice of slice as bucket to collect nums ```go= func topKFrequent(nums []int, k int) []int { // special case if len(nums) == k { return nums } // frequency map freqs := map[int]int{} for _, num := range nums { freqs[num]++ } // frequency as index // collect numbers with same frequency nums_len := len(nums) bucket := make([][]int, nums_len+1) // index: 0, 1 ... len(nums) for num, freq := range freqs { bucket[freq] = append(bucket[freq], num) } res := []int{} for i:=nums_len; i>0; i-- { if len(bucket[i]) > 0 { res = append(res, bucket[i]...) if len(res) >= k { return res } } } return res } ``` ### #6 manual quick sort (most complicated) ```go= func topKFrequent(nums []int, k int) []int { // special case if len(nums) == k { return nums } // frequency map freqs := map[int]int{} for _, num := range nums { freqs[num]++ } keys := []int{} for key, _ := range freqs { keys = append(keys, key) } // quick sort kQuickSort(keys, freqs, k) return keys[len(keys)-k:] } func kQuickSort(slice []int, m map[int]int, k int) { if len(slice) <= 1 { return } kQuickSortRecr(slice, m , 0, len(slice)-1, k) } func kQuickSortRecr(slice []int, m map[int]int, start_i, end_i, k int) { // end condition if start_i >= end_i { return } // main logic piv_i := getPiv(start_i, end_i) piv_final := sort(slice, m, start_i, end_i, piv_i) // divide - DFS right first kQuickSortRecr(slice, m, piv_final+1, end_i, k) // right if len(slice) - 1 == piv_final + k { return } kQuickSortRecr(slice, m, start_i, piv_final-1, k) // left } func getPiv(l, r int) int { return l + (r - l) / 2 } func sort(slice []int, m map[int]int, start_i, end_i, piv_i int) int { swap(slice, piv_i, end_i) curr_piv_i := end_i for true { for true { if start_i == end_i { break } if m[slice[start_i]] > m[slice[curr_piv_i]] { break } start_i++ } for true { if start_i == end_i { break } if m[slice[end_i]] < m[slice[curr_piv_i]] { break } end_i-- } if start_i == end_i { break } swap(slice, end_i, start_i) } if m[slice[start_i]] >= m[slice[curr_piv_i]] { swap(slice, start_i, curr_piv_i) curr_piv_i = start_i } return curr_piv_i } func swap(slice []int, i, j int) { slice[i], slice[j] = slice[j], slice[i] } ``` ### Note - how to use `sort` - sorting of map by key or value ## Product of Array Except Self solution: https://www.cnblogs.com/grandyang/p/4650187.html ```go= func productExceptSelf(nums []int) []int { length := len(nums) res := make([]int, length) // forward res[0] = 1 for i:=1; i < len(nums); i++ { res[i] = res[i-1] * nums[i-1] } // backward right := nums[length-1] for i:=length-2; i >= 0; i-- { res[i] *= right right *= nums[i] } return res } ``` ## Encode & Decode Strings solution: https://www.youtube.com/watch?v=B1k_sxOSgv8 ```go= package main import ( "fmt" "strconv" ) const SEP string = ":" func main() { list := []string{"#set", "666", "rat"} enco := encode(list) fmt.Println(enco) fmt.Println(decode(enco)) } func encode(list []string) string { str := "" for _, w := range list { str += fmt.Sprintf("%d%s%s", len(w), SEP, w) } return str } func decode(str string) []string { list, i := []string{}, 0 for i < len(str) { sep_i := i // find seperator for ; sep_i < len(str); sep_i++ { if str[sep_i] == SEP[0] { break } } // get word length length_str := str[i:sep_i] length, err := strconv.Atoi(length_str) if err != nil { panic("err") } list = append(list, str[sep_i+1:sep_i+1+length]) i = sep_i + 1 + length } return list } ``` ### Note - [efficient details of string append](https://shockerli.net/post/golang-concat-string/) ## Longest Consecutive ### find start and end of sequences ```go! func longestConsecutive(nums []int) int { set := make(map[int]struct{}) for _, n := range nums { if _, ok := set[n]; !ok { set[n] = struct{}{} } } res := 0 for _, n := range nums { // if not start number, continue if _, ok := set[n-1]; ok { continue } // else n is a start number end := n for { // no next, means end if _, ok := set[end+1]; !ok { break } end++ } res = max(res, end-n+1) } return res } func max(a, b int) int { if a > b { return a } return b } ``` ### Union Find ```go= type UnionFind struct { Roots map[int]int Lengths map[int]int } func (this *UnionFind) Union (a, b int) { root_a := this.FindRoot(a) root_b := this.FindRoot(b) if root_a != root_b { this.Roots[root_a] = root_b this.Lengths[root_b] += this.Lengths[root_a] } } func (this *UnionFind) FindRoot (a int) int { root := this.Roots[a] if root == a { return a } this.Roots[a] = this.FindRoot(root) return this.Roots[a] } func createUnionFind(nums []int) *UnionFind { roots, lengths := make(map[int]int), make(map[int]int) for _, n := range nums { roots[n] = n lengths[n] = 1 } return &UnionFind{roots, lengths} } func longestConsecutive(nums []int) int { uf := createUnionFind(nums) longest := 0 for _, n := range nums { if _, ok := uf.Roots[n-1]; ok { uf.Union(n, n-1) } if _, ok := uf.Roots[n+1]; ok { uf.Union(n, n+1) } // length := uf.Lengths[uf.FindRoot(n)] longest = max(longest, uf.Lengths[uf.FindRoot(n)]) } return longest } func max(a, b int) int { if a>b { return a } return b } ``` ### Simliar to unionfind but faster! ```go! func longestConsecutive(nums []int) int { if len(nums) == 0 { return 0 } max := 0 bk := make(map[int]int) for _, num := range nums { if _, ok := bk[num]; ok { continue } left, right := 0, 0 if val, ok := bk[num-1]; ok { left = val } if val, ok := bk[num+1]; ok { right = val } bk[num] = left + right + 1 if max < bk[num] { max = bk[num] } bk[num-left] = bk[num] bk[num+right] = bk[num] } return max } ``` ## Valid Sudoku ### #1 ```go= func isValidSudoku(board [][]byte) bool { for i:=0; i<9; i++ { // validate rows if !validSlice(board[i]) { return false } // validate cols buk := make([]byte, 9) for j:=0; j<9; j++ { buk[j] = board[j][i] } if !validSlice(buk) { return false } } // validate square r_i, c_i := 0, 0 for r_i < 9 { buk := []byte{} for _, row := range board[r_i:r_i+3] { for _, num := range row[c_i:c_i+3] { buk = append(buk, num) } } if !validSlice(buk) { return false } if c_i == 6 { c_i = 0 r_i += 3 } else { c_i += 3 } } return true } func validSlice(s []byte) bool { hst := make(map[byte]struct{}) for _, num := range s { if string(num) == "." { continue } if _, ok := hst[num]; ok { return false } else { hst[num] = struct{}{} } } return true } ```