From bd9b85248497a682ec5390d12e3f3607d3c56800 Mon Sep 17 00:00:00 2001 From: Anurag Singh Date: Mon, 8 Jul 2019 22:51:13 -0700 Subject: [PATCH 1/2] Up to unique-paths --- combination-sum-2.go | 62 ++++++++ ...ree-from-preorder-and-inorder-traversal.go | 85 ++++++++++ implement-queue-using-stacks.go | 73 +++++++++ knapsack-0-1.go | 69 ++++++++ longest-palindromic-substring.go | 69 ++++++++ maximum-subarray.go | 61 +++++++ partition-equal-subset-sum.go | 74 +++++++++ reconstruct-itinerary.go | 150 ++++++++++++++++++ ugly-number-2.go | 64 ++++++++ unique-paths.go | 61 +++++++ word-search-2.go | 141 ++++++++++++++++ 11 files changed, 909 insertions(+) create mode 100644 combination-sum-2.go create mode 100644 construct-binary-tree-from-preorder-and-inorder-traversal.go create mode 100644 implement-queue-using-stacks.go create mode 100644 knapsack-0-1.go create mode 100644 longest-palindromic-substring.go create mode 100644 maximum-subarray.go create mode 100644 partition-equal-subset-sum.go create mode 100644 reconstruct-itinerary.go create mode 100644 ugly-number-2.go create mode 100644 unique-paths.go create mode 100644 word-search-2.go diff --git a/combination-sum-2.go b/combination-sum-2.go new file mode 100644 index 0000000..338b3c4 --- /dev/null +++ b/combination-sum-2.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "sort" +) + +type Ints []int + +func (ints Ints) Len() int { + return len(ints) +} + +func (ints Ints) Less(i, j int) bool { + return ints[i] < ints[j] +} + +func (ints Ints) Swap(i, j int) { + ints[i], ints[j] = ints[j], ints[i] +} + +func findCombinations(candidates []int, target int, selectedCandidates []int, results [][]int) [][]int { + if target < 0 { + return results + } + + if target == 0 { + sel := make([]int, len(selectedCandidates)) + copy(sel, selectedCandidates) + results = append(results, sel) + + return results + } + + for i := 0; i < len(candidates); i++ { + if i > 0 && candidates[i] == candidates[i-1] { + continue + } + + selectedCandidates = append(selectedCandidates, candidates[i]) + results = findCombinations(candidates[i+1:], target-candidates[i], selectedCandidates, results) + selectedCandidates = selectedCandidates[:len(selectedCandidates)-1] + } + + return results +} + +func combinationSum2(candidates []int, target int) [][]int { + sort.Sort(Ints(candidates)) + + return findCombinations(candidates, target, []int{}, [][]int{}) +} + +func tests() { + fmt.Printf("%v\n", combinationSum2([]int{10, 1, 2, 7, 6, 1, 5}, 8)) + fmt.Printf("%v\n", combinationSum2([]int{2, 5, 2, 1, 2}, 5)) + fmt.Printf("%v\n", combinationSum2([]int{1, 1, 2, 2}, 5)) +} + +func main() { + tests() +} diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal.go b/construct-binary-tree-from-preorder-and-inorder-traversal.go new file mode 100644 index 0000000..9db9650 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal.go @@ -0,0 +1,85 @@ +package main + +import "fmt" + +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func getSubTree(preorder []int, preorderStart, preorderEnd int, inorder []int, inorderStart, inorderEnd int, preorderOffsetLUT, inorderOffsetLUT map[int]int) *TreeNode { + if len(preorder) == 0 || len(inorder) == 0 || preorderStart > preorderEnd || inorderStart > inorderEnd { + return nil + } + + root := preorder[preorderStart] + rootInorderOffset := inorderOffsetLUT[root] + lSubTreeLen := rootInorderOffset - inorderStart + + if lSubTreeLen < 0 { + fmt.Printf("****ERROR******\n") + fmt.Printf("root: %d; preorderStart: %d; inorderStart: %d; rootInorderOffset: %d; lSubTreeLen: %d\n", + root, preorderStart, inorderStart, rootInorderOffset, lSubTreeLen) + + return nil + } + + // fmt.Printf("getting lSubTree\n") + lSubTree := getSubTree(preorder, preorderStart+1, preorderStart+1+lSubTreeLen-1, + inorder, inorderStart, inorderStart+lSubTreeLen-1, preorderOffsetLUT, inorderOffsetLUT) + + // fmt.Printf("getting rSubTree\n") + rSubTree := getSubTree(preorder, preorderStart+lSubTreeLen+1, preorderEnd, + inorder, rootInorderOffset+1, inorderEnd, preorderOffsetLUT, inorderOffsetLUT) + + return &TreeNode{Val: root, Left: lSubTree, Right: rSubTree} +} + +func inorderTraversal(root *TreeNode) { + if root == nil { + return + } + + inorderTraversal(root.Left) + fmt.Printf("%d ", root.Val) + inorderTraversal(root.Right) +} + +func buildTree(preorder []int, inorder []int) *TreeNode { + preorderOffsetLUT := make(map[int]int) + inorderOffsetLUT := make(map[int]int) + + for i := 0; i < len(preorder); i++ { + preorderOffsetLUT[preorder[i]] = i + inorderOffsetLUT[inorder[i]] = i + } + + return getSubTree(preorder, 0, len(preorder)-1, inorder, 0, len(inorder)-1, preorderOffsetLUT, inorderOffsetLUT) +} + +func test1() { + // preorder := []int{3, 9, 20, 15, 7} + // inorder := []int{9, 3, 15, 20, 7} + preorder := []int{5, 1, 9} + inorder := []int{1, 5, 9} + // preorder := []int{1, 5} + // inorder := []int{1, 5} + + tree := buildTree(preorder, inorder) + + inorderTraversal(tree) +} + +func main() { + test1() +} diff --git a/implement-queue-using-stacks.go b/implement-queue-using-stacks.go new file mode 100644 index 0000000..93bb828 --- /dev/null +++ b/implement-queue-using-stacks.go @@ -0,0 +1,73 @@ +package main + +import "fmt" + +type MyQueue struct { + pushStack []int + popStack []int +} + +/** Initialize your data structure here. */ +func Constructor() MyQueue { + return MyQueue{ + pushStack: []int{}, + popStack: []int{}, + } +} + +/** Push element x to the back of queue. */ +func (this *MyQueue) Push(x int) { + this.pushStack = append(this.pushStack, x) +} + +/** Removes the element from in front of queue and returns that element. */ +func (this *MyQueue) Pop() int { + if len(this.popStack) == 0 { + this.TransferPushToPopStack() + } + + popped := this.popStack[len(this.popStack)-1] + this.popStack = this.popStack[:len(this.popStack)-1] + + return popped +} + +func (this *MyQueue) TransferPushToPopStack() { + if len(this.pushStack) == 0 { + return + } + + for i := len(this.pushStack) - 1; i >= 0; i-- { + this.popStack = append(this.popStack, this.pushStack[i]) + } + + this.pushStack = this.pushStack[0:0] +} + +/** Get the front element. */ +func (this *MyQueue) Peek() int { + if len(this.popStack) == 0 { + this.TransferPushToPopStack() + } + + return this.popStack[len(this.popStack)-1] +} + +/** Returns whether the queue is empty. */ +func (this *MyQueue) Empty() bool { + return len(this.pushStack) == 0 && len(this.popStack) == 0 +} + +func test() { + obj := Constructor() + obj.Push(5) + obj.Push(2) + param_2 := obj.Pop() + param_4 := obj.Empty() + + fmt.Printf("%v, %v\n", param_2, param_4) +} + +func main() { + test() +} diff --git a/knapsack-0-1.go b/knapsack-0-1.go new file mode 100644 index 0000000..fd8fd93 --- /dev/null +++ b/knapsack-0-1.go @@ -0,0 +1,69 @@ +package main + +import "fmt" + +func max(a, b int) int { + if a > b { + return a + } + + return b +} + +func maxKnapsackValue(w int, ws, vs []int) int { + dp := make([][]int, w+1) + n := len(ws) + + for i, _ := range dp { + dp[i] = make([]int, n+1) + } + + // dp[i][j] is the maximum value of the knapsack containing + // a subset of the items [0..(j - 1)] whose weights add up to + // less than or equal to i + for j := 0; j <= n; j++ { + dp[0][j] = 0 + } + + for i := 1; i <= w; i++ { + dp[i][0] = 0 + + for j := 1; j <= n; j++ { + dp[i][j] = dp[i][j-1] + + if i-ws[j-1] >= 0 { + dp[i][j] = max(dp[i][j], dp[i-ws[j-1]][j-1]+vs[j-1]) + } + } + } + + return dp[w][n] +} + +type testcase struct { + weights []int + values []int + w int + expectedMaxValue int +} + +func test() { + tcs := []testcase{ + testcase{weights: []int{3, 2, 4, 1}, values: []int{100, 20, 60, 40}, w: 5, expectedMaxValue: 140}, + testcase{weights: []int{10, 20, 30}, values: []int{60, 100, 120}, w: 50, expectedMaxValue: 220}, + } + + for i, tc := range tcs { + result := maxKnapsackValue(tc.w, tc.weights, tc.values) + + if result != tc.expectedMaxValue { + fmt.Printf("Test case %d FAILED (expected: %d, actual: %d)\n", i, tc.expectedMaxValue, result) + } else { + fmt.Printf("Test case %d PASSED\n", i) + } + } +} + +func main() { + test() +} diff --git a/longest-palindromic-substring.go b/longest-palindromic-substring.go new file mode 100644 index 0000000..e887bd5 --- /dev/null +++ b/longest-palindromic-substring.go @@ -0,0 +1,69 @@ +package main + +import "fmt" + +func longestPalindrome(s string) string { + sChars := []rune(s) + n := len(s) + dp := make([][]bool, n+1) + + if n == 0 { + return "" + } + + for i := 0; i < len(dp); i++ { + dp[i] = make([]bool, n) + } + + // All substrings of length 1 are palindromic by definition + maxPalindromeStartOffset := 0 + maxPalindromeLength := 0 + + for i := 0; i < n; i++ { + dp[1][i] = true + maxPalindromeLength = 1 + } + + for l := 2; l <= n; l++ { + for i := 0; i < n; i++ { + enclStart := i + 1 + enclEnd := i + l - 2 + + if enclEnd >= n || enclStart >= n || i+l-1 >= n { + break + } + + if enclStart >= enclEnd { + dp[l][i] = sChars[i] == sChars[i+l-1] + } else { + dp[l][i] = dp[l-2][i+1] && (sChars[i] == sChars[i+l-1]) + } + + if dp[l][i] { + maxPalindromeStartOffset = i + maxPalindromeLength = l + } + } + } + + return string(sChars[maxPalindromeStartOffset : maxPalindromeStartOffset+maxPalindromeLength]) +} + +func tests() { + testCases := []string{ + "babad", + "cbbd", + "", + "a", + "aaaaa", + "", + } + + for i := 0; i < len(testCases); i++ { + fmt.Printf("tc #%d: result: %s\n", i, longestPalindrome(testCases[i])) + } +} + +func main() { + tests() +} diff --git a/maximum-subarray.go b/maximum-subarray.go new file mode 100644 index 0000000..040f236 --- /dev/null +++ b/maximum-subarray.go @@ -0,0 +1,61 @@ +package main + +import "fmt" + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func maxSubArray(nums []int) int { + if len(nums) == 0 { + return 0 + } + + maxSum := nums[0] + sum := 0 + end := 0 + + for end < len(nums) { + if nums[end]+sum < 0 { + maxSum = max2(maxSum, sum+nums[end]) + end++ + sum = 0 + + continue + } + + maxSum = max2(maxSum, sum+nums[end]) + sum += nums[end] + end++ + } + + return maxSum +} + +func test1() { + tc := []int{-2, 1, -3, 4, -1, 2, 1, -5, 4} + + fmt.Printf("maxSum = %d\n", maxSubArray(tc)) +} + +func test2() { + tc := []int{-3, -2, 1} + + fmt.Printf("maxSum = %d\n", maxSubArray(tc)) +} + +func test3() { + tc := []int{4, -1, 2, 1} + + fmt.Printf("maxSum = %d\n", maxSubArray(tc)) +} + +func main() { + test1() + test2() + test3() +} diff --git a/partition-equal-subset-sum.go b/partition-equal-subset-sum.go new file mode 100644 index 0000000..80a0bc5 --- /dev/null +++ b/partition-equal-subset-sum.go @@ -0,0 +1,74 @@ +package main + +import "fmt" + +func canPartition(nums []int) bool { + sum := 0 + + for _, n := range nums { + sum += n + } + + if sum%2 != 0 { + return false + } + + halfSum := sum / 2 + part := make([][]bool, halfSum+1) + n := len(nums) + + for i := 0; i <= halfSum; i++ { + part[i] = make([]bool, n+1) + } + + for i := 0; i <= n; i++ { + part[0][i] = true + } + + for s := 1; s <= halfSum; s++ { + part[s][0] = false + + for j := 1; j <= n; j++ { + if part[s][j-1] { + part[s][j] = true + + continue + } + + if s-nums[j-1] >= 0 { + // parts[s][j] is true if there's a subset of nums[0 .. j - 1] that sums to s. + // In other words, if there's a subset of nums[0 .. j - 2] that sums to s - nums[j - 1]. + part[s][j] = part[s-nums[j-1]][j-1] + } + } + } + + return part[halfSum][n] +} + +type testCase struct { + nums []int + expected bool +} + +func tests() { + testCases := []testCase{ + testCase{nums: []int{1, 3, 2}, expected: true}, + testCase{nums: []int{1, 5, 11, 5}, expected: true}, + testCase{nums: []int{1, 2, 3, 5}, expected: false}, + } + + for i, tc := range testCases { + result := canPartition(tc.nums) + + if result == tc.expected { + fmt.Printf("Test number %d: PASS\n", i) + } else { + fmt.Printf("Test number %d: FAILED (expected: %v, got %v)\n", i, tc.expected, result) + } + } +} + +func main() { + tests() +} diff --git a/reconstruct-itinerary.go b/reconstruct-itinerary.go new file mode 100644 index 0000000..db2776f --- /dev/null +++ b/reconstruct-itinerary.go @@ -0,0 +1,150 @@ +package main + +// reference: https://en.wikipedia.org/wiki/Eulerian_path + +import ( + "fmt" + "sort" +) + +type AirportCodes []string + +func (ac AirportCodes) Len() int { + return len(ac) +} + +func (ac AirportCodes) Swap(i, j int) { + ac[i], ac[j] = ac[j], ac[i] +} + +func (ac AirportCodes) Less(i, j int) bool { + return ac[i] < ac[j] // strings.Compare(ac[i], ac[j]) < 0 +} + +func dfs(adjMat [][]int, from int, acs []string, traversed []string) []string { + for to := 0; to < len(adjMat[from]); { + if adjMat[from][to] <= 0 { + to++ + continue + } + + adjMat[from][to]-- + + if adjMat[from][to] == 0 { + adjMat[from][to] = -1 + } + + traversed = dfs(adjMat, to, acs, traversed) + } + + return append(traversed, acs[from]) +} + +func reverse(strings []string) { + for i, j := 0, len(strings)-1; i < j; i, j = i+1, j-1 { + strings[i], strings[j] = strings[j], strings[i] + } +} + +func findItinerary(tickets [][]string) []string { + acOffsets := make(map[string]int) + + for _, srcDest := range tickets { + src, dest := srcDest[0], srcDest[1] + + acOffsets[src] = -1 + acOffsets[dest] = -1 + } + + acs := make([]string, len(acOffsets)) + acsOffset := 0 + + for k, _ := range acOffsets { + acs[acsOffset] = k + acsOffset++ + } + + sort.Sort(AirportCodes(acs)) + + for i, ac := range acs { + acOffsets[ac] = i + } + + fmt.Printf("acOffsets: %v\n", acOffsets) + + numAirports := len(acs) + adjMat := make([][]int, numAirports) + + for i := 0; i < numAirports; i++ { + adjMat[i] = make([]int, numAirports) + } + + for _, ticket := range tickets { + from, to := acOffsets[ticket[0]], acOffsets[ticket[1]] + adjMat[from][to]++ + } + + traversed := dfs(adjMat, acOffsets["JFK"], acs, []string{}) + + reverse(traversed) + + return traversed +} + +func test1() { + tickets := [][]string{ + []string{"MUC", "LHR"}, + []string{"JFK", "MUC"}, + []string{"SFO", "SJC"}, + []string{"LHR", "SFO"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func test2() { + tickets := [][]string{ + []string{"JFK", "LHR"}, + []string{"LHR", "SFO"}, + []string{"SFO", "JFK"}, + []string{"JFK", "ATL"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func test3() { + tickets := [][]string{ + []string{"JFK", "AAA"}, + []string{"AAA", "JFK"}, + []string{"JFK", "BBB"}, + []string{"JFK", "CCC"}, + []string{"CCC", "JFK"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func test4() { + tickets := [][]string{ + []string{"JFK", "SFO"}, []string{"JFK", "ATL"}, []string{"SFO", "ATL"}, []string{"ATL", "JFK"}, []string{"ATL", "SFO"}, + } + + fmt.Printf("itinerary: %v\n", findItinerary(tickets)) +} + +func tests() { + airportCodes := AirportCodes([]string{"JFK", "ATL", "SFO"}) + + sort.Sort(airportCodes) + + fmt.Printf("sorted codes: %v\n", airportCodes) +} + +func main() { + // tests() + // test1() + // test2() + // test3() + test4() +} diff --git a/ugly-number-2.go b/ugly-number-2.go new file mode 100644 index 0000000..ed33393 --- /dev/null +++ b/ugly-number-2.go @@ -0,0 +1,64 @@ +package main + +import "fmt" + +func min3(a, b, c int) int { + if a < b { + if a < c { + return a + } + + return c + } + + if b < c { + return b + } + + return c +} + +func nthUglyNumber(n int) int { + nums := make([]int, n) + x, y, z := 0, 0, 0 + nums[0] = 1 + next := 1 + + for next < len(nums) { + a := 2 * nums[x] + b := 3 * nums[y] + c := 5 * nums[z] + + min := min3(a, b, c) + nums[next] = min + next++ + + if a == min { + x++ + } + + if b == min { + y++ + } + + if c == min { + z++ + } + } + + return nums[len(nums)-1] +} + +func test() { + for i := 1; i < 11; i++ { + uglyNumber := nthUglyNumber(i) + + fmt.Printf("%d ", uglyNumber) + } + + fmt.Printf("\n") +} + +func main() { + test() +} diff --git a/unique-paths.go b/unique-paths.go new file mode 100644 index 0000000..2a32797 --- /dev/null +++ b/unique-paths.go @@ -0,0 +1,61 @@ +package main + +import "fmt" + +func uniquePaths(m int, n int) int { + if m == 0 || n == 0 { + return 0 + } + + grid := make([][]int, m) + + for i, _ := range grid { + grid[i] = make([]int, n) + } + + grid[0][0] = 1 + + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + rightR, rightC := i, j+1 + downR, downC := i+1, j + + if rightC < n { + grid[rightR][rightC] += grid[i][j] + } + + if downR < m { + grid[downR][downC] += grid[i][j] + } + } + } + + return grid[m-1][n-1] +} + +type testcase struct { + m int + n int + expectedResult int +} + +func tests() { + testcases := []testcase{ + testcase{m: 3, n: 2, expectedResult: 3}, + testcase{m: 7, n: 3, expectedResult: 28}, + } + + for i, tc := range testcases { + result := uniquePaths(tc.m, tc.n) + + if result != tc.expectedResult { + fmt.Printf("Test %d FAILED (expected: %d; actual %d)\n", i, tc.expectedResult, result) + } else { + fmt.Printf("Test %d PASSED\n", i) + } + } +} + +func main() { + tests() +} diff --git a/word-search-2.go b/word-search-2.go new file mode 100644 index 0000000..74eedc0 --- /dev/null +++ b/word-search-2.go @@ -0,0 +1,141 @@ +package main + +import "fmt" + +func runeToByteBoard(rBoard [][]rune) [][]byte { + rows := len(rBoard) + cols := len(rBoard[0]) + bBoard := make([][]byte, rows) + + for r := 0; r < rows; r++ { + bBoard[r] = make([]byte, cols) + + for c := 0; c < cols; c++ { + bBoard[r][c] = byte(rBoard[r][c]) + } + } + + return bBoard +} + +func clearTraversed(traversed [][]bool) { + for r := 0; r < len(traversed); r++ { + for c := 0; c < len(traversed[0]); c++ { + traversed[r][c] = false + } + } +} + +func checkTraversed(traversed [][]bool) { + for r := 0; r < len(traversed); r++ { + for c := 0; c < len(traversed[0]); c++ { + if traversed[r][c] { + fmt.Printf("traversed[%d][%d] is true", r, c) + + return + } + } + } +} + +func checkForWord(board [][]byte, traversed [][]bool, word []rune, fromRow, fromCol, start int) bool { + if start >= len(word) { + return true + } + + if board[fromRow][fromCol] != byte(word[start]) { + return false + } + + if start == len(word)-1 { + return true + } + + dirR := []int{0, 0, -1, 1} + dirC := []int{-1, 1, 0, 0} + numRows := len(board) + numCols := len(board[0]) + + for d := 0; d < len(dirR); d++ { + newR, newC := fromRow+dirR[d], fromCol+dirC[d] + + if newR < 0 || newR >= numRows || newC < 0 || newC >= numCols || traversed[newR][newC] { + continue + } + + traversed[newR][newC] = true + + if checkForWord(board, traversed, word, newR, newC, start+1) { + traversed[newR][newC] = false + return true + } + + traversed[newR][newC] = false + } + + return false +} + +func findWords(board [][]byte, words []string) []string { + if len(board) == 0 { + return []string{} + } + + rows := len(board) + cols := len(board[0]) + result := []string{} + + traversed := make([][]bool, rows) + + for r := 0; r < rows; r++ { + traversed[r] = make([]bool, cols) + } + + for w := 0; w < len(words); w++ { + wordRunes := []rune(words[w]) + wordFound := false + + for r := 0; r < rows && !wordFound; r++ { + for c := 0; c < cols && !wordFound; c++ { + // checkTraversed(traversed) + + if (byte)(wordRunes[0]) != board[r][c] { + continue + } + + // fmt.Printf("found ") + traversed[r][c] = true + if checkForWord(board, traversed, wordRunes, r, c, 0) { + traversed[r][c] = false + wordFound = true + break + } + traversed[r][c] = false + } + } + + if wordFound { + result = append(result, words[w]) + } + } + + return result +} + +func test1() { + rBoard := [][]rune{ + []rune{'o', 'a', 'a', 'n'}, + []rune{'e', 't', 'a', 'e'}, + []rune{'i', 'h', 'k', 'r'}, + []rune{'i', 'f', 'l', 'v'}, + } + bBoard := runeToByteBoard(rBoard) + words := []string{"oath", "pea", "eat", "rain"} + result := findWords(bBoard, words) + + fmt.Printf("result: %v\n", result) +} + +func main() { + test1() +} From 25a30e7ad1c1b62e4ce6b1c91ff61a41ec0c0a4e Mon Sep 17 00:00:00 2001 From: Anurag Singh Date: Wed, 11 Sep 2019 00:15:08 -0700 Subject: [PATCH 2/2] Up to course-schedule-ii --- .gitignore | 1 + binary-tree-maximum-path-sum.go | 48 +++ course-schedule-ii.go | 190 +++++++++++ delete-columns-to-make-sorted-ii.go | 102 ++++++ deo-evaporator.go | 19 ++ direction-and-position-rule-verification.go | 80 +++++ ds/doubly-linked-list.go | 67 ++++ ds/doubly-linked-list_test.go | 60 ++++ fair-coin-from-biased.go | 55 +++ largest-bst-subtree.go | 95 ++++++ lfu.go | 361 ++++++++++++++++++++ longest-consecutive-sequence.go | 99 ++++++ n-bishops.go | 90 +++++ non-decreasing-array.go | 119 +++++++ number-of-islands.go | 72 ++++ remove-invalid-parentheses.go | 154 +++++++++ 16 files changed, 1612 insertions(+) create mode 100644 .gitignore create mode 100644 binary-tree-maximum-path-sum.go create mode 100644 course-schedule-ii.go create mode 100644 delete-columns-to-make-sorted-ii.go create mode 100644 deo-evaporator.go create mode 100644 direction-and-position-rule-verification.go create mode 100644 ds/doubly-linked-list.go create mode 100644 ds/doubly-linked-list_test.go create mode 100644 fair-coin-from-biased.go create mode 100644 largest-bst-subtree.go create mode 100644 lfu.go create mode 100644 longest-consecutive-sequence.go create mode 100644 n-bishops.go create mode 100644 non-decreasing-array.go create mode 100644 number-of-islands.go create mode 100644 remove-invalid-parentheses.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d74e21 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ diff --git a/binary-tree-maximum-path-sum.go b/binary-tree-maximum-path-sum.go new file mode 100644 index 0000000..2d549fc --- /dev/null +++ b/binary-tree-maximum-path-sum.go @@ -0,0 +1,48 @@ +package main + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func max3(a, b, c int) int { + return max2(a, max2(b, c)) +} + +func traverse(root *TreeNode, maxSum *int) int { + if root == nil { + return 0 + } + + lPathMaxSum := traverse(root.Left, maxSum) + rPathMaxSum := traverse(root.Right, maxSum) + rootPathMaxSum := max3(root.Val, root.Val+lPathMaxSum, root.Val+rPathMaxSum) + + *maxSum = max3(*maxSum, root.Val+lPathMaxSum+rPathMaxSum, rootPathMaxSum) + + return rootPathMaxSum +} + +func maxPathSum(root *TreeNode) int { + if root == nil { + return 0 + } + + maxSum := root.Val + traverse(root, &maxSum) + + return maxSum +} + +func main() { + +} diff --git a/course-schedule-ii.go b/course-schedule-ii.go new file mode 100644 index 0000000..9a443f4 --- /dev/null +++ b/course-schedule-ii.go @@ -0,0 +1,190 @@ +package main + +import "fmt" + +type intSet struct { + size int + elements map[int]struct{} +} + +func NewIntSet() intSet { + return intSet{size: 0, elements: make(map[int]struct{})} +} + +func (s *intSet) Add(elem int) { + if _, exists := s.elements[elem]; !exists { + s.elements[elem] = struct{}{} + s.size++ + } + + return +} + +func (s *intSet) Remove(elem int) { + if _, exists := s.elements[elem]; exists { + delete(s.elements, elem) + s.size-- + } + + return +} + +func (s *intSet) Contains(elem int) bool { + _, exists := s.elements[elem] + + return exists +} + +func (s *intSet) GetAnyElement() (int, error) { + if len(s.elements) == 0 { + return 0, fmt.Errorf("set is empty") + } + + var elem int + for k, _ := range s.elements { + elem = k + + break + } + + return elem, nil +} + +// Return true if cycle exists. +func checkCycleFrom(start int, adjs [][]int, whiteSet, graySet, blackSet intSet) bool { + if blackSet.Contains(start) { + return false + } + + if graySet.Contains(start) { + return true + } + + if whiteSet.Contains(start) { + whiteSet.Remove(start) + graySet.Add(start) + } + + // Explore all adjacencies + for i := 0; i < len(adjs[start]); i++ { + cycleExists := checkCycleFrom(adjs[start][i], adjs, whiteSet, graySet, blackSet) + + if cycleExists { + return true + } + } + + graySet.Remove(start) + blackSet.Add(start) + + return false +} + +func checkCycle(adjs [][]int) bool { + whiteSet, graySet, blackSet := NewIntSet(), NewIntSet(), NewIntSet() + + for i := 0; i < len(adjs); i++ { + whiteSet.Add(i) + } + + for { + elem, err := whiteSet.GetAnyElement() + + if err != nil { + break + } + + if checkCycleFrom(elem, adjs, whiteSet, graySet, blackSet) { + return true + } + } + + return false +} + +func topologicalTraversal(start int, adjs [][]int, visited map[int]struct{}, traversal []int) []int { + if _, exists := visited[start]; exists { + return traversal + } + + for _, to := range adjs[start] { + traversal = topologicalTraversal(to, adjs, visited, traversal) + } + + visited[start] = struct{}{} + traversal = append(traversal, start) + + return traversal +} + +func findOrder(numCourses int, prerequisites [][]int) []int { + adjs := make([][]int, numCourses) + + for i, _ := range adjs { + adjs[i] = []int{} + } + + visited := make(map[int]struct{}) + leafCheck := make([]bool, numCourses) + + for i, _ := range leafCheck { + leafCheck[i] = true + } + + for _, pair := range prerequisites { + from, to := pair[0], pair[1] + + adjs[from] = append(adjs[from], to) + + leafCheck[to] = false + } + + if checkCycle(adjs) { + return []int{} + } + + order := []int{} + + for i := 0; i < numCourses; i++ { + if leafCheck[i] { + // leaf course - this is a course that no other course depends on + order = topologicalTraversal(i, adjs, visited, order) + } + } + + for i := 0; i < numCourses; i++ { + if _, exists := visited[i]; !exists { + return []int{} + } + } + + return order +} + +type testcase struct { + numCourses int + preReqs [][]int +} + +func tests() { + testcases := []testcase{ + testcase{numCourses: 2, preReqs: [][]int{[]int{1, 0}}}, + testcase{numCourses: 4, preReqs: [][]int{[]int{1, 0}, []int{2, 0}, []int{3, 1}, []int{3, 2}}}, + testcase{numCourses: 3, preReqs: [][]int{[]int{0, 2}, []int{1, 2}, []int{2, 0}}}, + } + + for i, tc := range testcases { + order := findOrder(tc.numCourses, tc.preReqs) + + fmt.Printf("Test case: %d; order: %v\n", i, order) + } +} + +func main() { + // tests() + s1 := NewIntSet() + + s1.Add(4) + + fmt.Printf("s1.size = %d\n", s1.size) +} diff --git a/delete-columns-to-make-sorted-ii.go b/delete-columns-to-make-sorted-ii.go new file mode 100644 index 0000000..4042b24 --- /dev/null +++ b/delete-columns-to-make-sorted-ii.go @@ -0,0 +1,102 @@ +package main + +import "fmt" + +func minDeletionSize(A []string) int { + charss := make([][]rune, len(A)) + + for i, _ := range A { + charss[i] = []rune(A[i]) + } + + strictlyGreater := make([]bool, len(A)) + numDeletions := 0 + + for j := 0; j < len(A[0]); j++ { + monotonicIncreasing := true + + for i := 1; i < len(A); i++ { + if charss[i-1][j] >= charss[i][j] { + monotonicIncreasing = false + } + + if charss[i-1][j] > charss[i][j] { + if strictlyGreater[i] { + continue + } + + // inversion + if j == 0 { + // inversion in first column + numDeletions++ + + for k := 0; k < len(A); k++ { + charss[k][j] = '_' + } + + break + } else { + // inversion that's not in first column + if charss[i-1][j-1] == charss[i][j-1] { + // copy previous column and delete this one + numDeletions++ + + for k := 0; k < len(A); k++ { + charss[k][j] = charss[k][j-1] + } + + break + } + } + } + } + + for i := 1; i < len(A); i++ { + if charss[i-1][j] < charss[i][j] { + strictlyGreater[i] = true + } + } + + if monotonicIncreasing { + return numDeletions + } + } + + return numDeletions +} + +func test1() { + A := []string{ + "ca", + "bb", + "ac", + } + + fmt.Printf("minDeletions: %d\n", minDeletionSize(A)) +} + +func test2() { + A := []string{ + "xc", + "yb", + "za", + } + + fmt.Printf("minDeletions: %d\n", minDeletionSize(A)) +} + +func test3() { + A := []string{ + "zyx", + "wvu", + "tsr", + } + + fmt.Printf("minDeletions: %d\n", minDeletionSize(A)) +} + +func main() { + // test1() + // test2() + test3() +} diff --git a/deo-evaporator.go b/deo-evaporator.go new file mode 100644 index 0000000..af8dd52 --- /dev/null +++ b/deo-evaporator.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "math" +) + +func Evaporator(content float64, evapPerDay int, threshold int) int { + // your code + n := math.Log10(float64(threshold) / 100) / math.Log10(1 - float64(evapPerDay) / 100.0) + + return int(math.Ceil(n)) +} + +func main() { + fmt.Printf("%d\n", Evaporator(10, 10, 10)) + fmt.Printf("%d\n", Evaporator(10, 10, 5)) + fmt.Printf("%d\n", Evaporator(100, 5, 5)) +} diff --git a/direction-and-position-rule-verification.go b/direction-and-position-rule-verification.go new file mode 100644 index 0000000..248f929 --- /dev/null +++ b/direction-and-position-rule-verification.go @@ -0,0 +1,80 @@ +package main + +import ( + "fmt" + "strings" +) + +type edge struct { + from string + to string + dirLabel string // read as is of +} + +type vertexCoords struct { + x int + y int +} + +type vertexAdjs struct { + vertex string + fromEdges []edge + toEdges []edge +} + +func processDirections(directions []string) []edge { + edges := []edge{} + + for _, direction := range directions { + dirParts := strings.Split(direction, " ") + from, dirLabel, to := dirParts[0], dirParts[1], dirParts[2] + edges = append(edges, edge{from: from, to: to, dirLabel: dirLabel}) + } + + return edges +} + +func buildVertextAdjs(edges []edge) map(string, []vertexAdjs) { + adjs := make(map(string, vertexAdjs)) + + for _, e := range edges { + f, t := e.from, e.to + + _, okf := adjs[f] + _, okt := adjs[t] + + if !okf { + adjs[f] = vertexAdjs{vertex: f, fromEdges: []edge{}, toEdges: []edge{}} + } + + if !okt { + adjs[t] = vertexAdjs{vertex: f, fromEdges: []edge{}, toEdges: []edge{}} + } + + + } +} + +type testcase struct { + directions []string + expectedValidity bool +} + +func test() { + tcs := []testcase{ + testcase { + directions: []string{"A NE B", "A N B"}, + expectedValidity: true, + }, + } + + for _, tc := range tcs { + edges := processDirections(tc.directions) + + fmt.Printf("edges: %v\n", edges) + } +} + +func main() { + test() +} diff --git a/ds/doubly-linked-list.go b/ds/doubly-linked-list.go new file mode 100644 index 0000000..4c2fddf --- /dev/null +++ b/ds/doubly-linked-list.go @@ -0,0 +1,67 @@ +package ds + +import ( + "errors" +) + +type LinkedNode struct { + value interface{} + next *LinkedNode + prev *LinkedNode +} + +func (l *LinkedNode) IsEmpty() bool { + if l.next == nil && l.prev == nil { + return true + } + + if l.next == l && l.prev == l { + return true + } + + return false +} + +func (l *LinkedNode) AddNode(newNode *LinkedNode) { + if l.IsEmpty() { + l.next = newNode + l.prev = newNode + newNode.prev = l + newNode.next = l + } else { + currFirstNode := l.next + l.next = newNode + newNode.next = currFirstNode + newNode.prev = l + currFirstNode.prev = newNode + } +} + +// Should only be called from the head of the list (the head node is essentially the sentinel) +func (l *LinkedNode) Traverse(nodeVisitorFn func(data interface{})) { + for curr := l.next; curr != l; curr = curr.next { + nodeVisitorFn(curr.value) + } +} + +func DeleteNode(l *LinkedNode) error { + if l.IsEmpty() { + return errors.New("DeleteNode called on an empty list") + } + + next := l.next + prev := l.prev + + next.prev = prev + prev.next = next + + return nil +} + +func NewLinkedNode(data interface{}) *LinkedNode { + return &LinkedNode{value: data} +} + +func NewEmptyLinkedNode() *LinkedNode { + return &LinkedNode{} +} diff --git a/ds/doubly-linked-list_test.go b/ds/doubly-linked-list_test.go new file mode 100644 index 0000000..67c8155 --- /dev/null +++ b/ds/doubly-linked-list_test.go @@ -0,0 +1,60 @@ +package ds + +import ( + "testing" +) + +func TestIsEmpty(t *testing.T) { + list := NewEmptyLinkedNode() + + if !list.IsEmpty() { + t.Fatalf("Expected list to be empty") + } +} + +func TestAddAndTraverse(t *testing.T) { + head := NewEmptyLinkedNode() + + head.AddNode(NewLinkedNode(1)) + head.AddNode(NewLinkedNode(2)) + + visitorFn := func(d interface{}) { + num, ok := d.(int) + + if !ok { + t.Fatalf("Unexpected type") + } + + t.Logf("Data: %d\n", num) + } + + head.Traverse(visitorFn) +} + +func TestDeleteAllNodes(t *testing.T) { + head := NewEmptyLinkedNode() + + node1 := NewLinkedNode(1) + node2 := NewLinkedNode(2) + + head.AddNode(node1) + head.AddNode(node2) + + DeleteNode(node2) + + // visitorFn := func(d interface{}) { + // num, ok := d.(int) + + // if !ok { + // t.Fatalf("Unexpected type") + // } + + // t.Logf("Data: %d\n", num) + // } + + DeleteNode(node1) + + if !head.IsEmpty() { + t.Fatalf("Expected list to be empty but it's not") + } +} diff --git a/fair-coin-from-biased.go b/fair-coin-from-biased.go new file mode 100644 index 0000000..c165b3f --- /dev/null +++ b/fair-coin-from-biased.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "math/rand" +) + +func biasedCoin() int { + n := rand.Intn(100) + + if n < 60 { + return 0 + } + + return 1 +} + +func fairCoin() int { + for { + coin1 := biasedCoin() + coin2 := biasedCoin() + + if coin1 != coin2 { + return coin1 + } + } +} + +type CoinFn func() int + +func flip() { + zeros := 0 + ones := 0 + + var flipCoin CoinFn + + // flipCoin = biasedCoin + flipCoin = fairCoin + + for i := 0; i < 100; i++ { + flipResult := flipCoin() + + if flipResult == 0 { + zeros++ + } else { + ones++ + } + } + + fmt.Printf("zeros: %d; ones: %d\n", zeros, ones) +} + +func main() { + flip() +} diff --git a/largest-bst-subtree.go b/largest-bst-subtree.go new file mode 100644 index 0000000..f9c5724 --- /dev/null +++ b/largest-bst-subtree.go @@ -0,0 +1,95 @@ +package main + +import ( + "fmt" +) + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func min2(a, b int) int { + if a < b { + return a + } + + return b +} + +func bstSum(root *TreeNode, maxBSTSize *int) (bool, int, int, int) { + if root.Left == nil && root.Right == nil { + *maxBSTSize = max2(*maxBSTSize, 1) + + return true, 1, root.Val, root.Val + } + + isTreeBST := false + treeMin, treeMax := 0, 0 + treeSize := 0 + + if root.Left != nil && root.Right != nil { + isLSubTreeBST, lSubTreeSize, lSubTreeMin, lSubTreeMax := bstSum(root.Left, maxBSTSize) + isRSubTreeBST, rSubTreeSize, rSubTreeMin, rSubTreeMax := bstSum(root.Right, maxBSTSize) + + isTreeBST = isLSubTreeBST && isRSubTreeBST && (root.Val > lSubTreeMax) && (root.Val < rSubTreeMin) + treeSize = 1 + lSubTreeSize + rSubTreeSize + treeMin, treeMax = min2(root.Val, min2(lSubTreeMin, rSubTreeMin)), max2(root.Val, max2(lSubTreeMax, rSubTreeMax)) + } else if root.Left == nil { + isRSubTreeBST, rSubTreeSize, rSubTreeMin, rSubTreeMax := bstSum(root.Right, maxBSTSize) + isTreeBST = isRSubTreeBST && (root.Val < rSubTreeMin) + treeSize = 1 + rSubTreeSize + treeMin, treeMax = min2(root.Val, rSubTreeMin), max2(root.Val, rSubTreeMax) + } else { + isLSubTreeBST, lSubTreeSize, lSubTreeMin, lSubTreeMax := bstSum(root.Left, maxBSTSize) + isTreeBST = isLSubTreeBST && (root.Val > lSubTreeMax) + treeSize = 1 + lSubTreeSize + treeMin, treeMax = min2(root.Val, lSubTreeMin), max2(root.Val, lSubTreeMax) + } + + if root.Val == -2 { + fmt.Printf("treeMin: %v, treeMax: %v\n", treeMin, treeMax) + } + + if isTreeBST { + *maxBSTSize = max2(*maxBSTSize, treeSize) + } + + return isTreeBST, treeSize, treeMin, treeMax +} + +func largestBSTSubtree(root *TreeNode) int { + if root == nil { + return 0 + } + + maxBSTSum := 0 + + bstSum(root, &maxBSTSum) + + return maxBSTSum +} + +func test1() { + nodeM1 := TreeNode{Val: -1} + nodeM2 := TreeNode{Val: -2} + + nodeM2.Right = &nodeM1 + + result := largestBSTSubtree(&nodeM2) + + fmt.Printf("result: %d\n", result) +} + +func main() { + test1() +} diff --git a/lfu.go b/lfu.go new file mode 100644 index 0000000..7a962a9 --- /dev/null +++ b/lfu.go @@ -0,0 +1,361 @@ +package main + +import ( + "errors" + "fmt" +) + +type LinkedNode struct { + value interface{} + next *LinkedNode + prev *LinkedNode +} + +func (l *LinkedNode) IsEmpty() bool { + if l.next == nil && l.prev == nil { + return true + } + + if l.next == l && l.prev == l { + return true + } + + return false +} + +func (l *LinkedNode) AddNode(newNode *LinkedNode) { + if l.IsEmpty() { + l.next = newNode + l.prev = newNode + newNode.prev = l + newNode.next = l + } else { + currFirstNode := l.next + l.next = newNode + newNode.next = currFirstNode + newNode.prev = l + currFirstNode.prev = newNode + } +} + +// Should only be called from the head of the list (the head node is essentially the sentinel) +func (l *LinkedNode) Traverse(nodeVisitorFn func(data interface{})) { + for curr := l.next; curr != l; curr = curr.next { + nodeVisitorFn(curr.value) + } +} + +func DeleteNode(l *LinkedNode) error { + if l.IsEmpty() { + return errors.New("DeleteNode called on an empty list") + } + + next := l.next + prev := l.prev + + next.prev = prev + prev.next = next + + return nil +} + +func NewLinkedNode(data interface{}) *LinkedNode { + return &LinkedNode{value: data} +} + +func NewEmptyLinkedNode() *LinkedNode { + return &LinkedNode{} +} + +type LFUCache struct { + capacity int + size int + freqList *LinkedNode + keyMap map[int]valueDesc // int -> valueDesc +} + +type valueDesc struct { + valueNode *LinkedNode // Node containing the actual value + freqListNode *LinkedNode // Node from the freq list containing the value node +} + +type freqData struct { + freq int + valuesNodes *LinkedNode +} + +type keyValueData struct { + key int + value int +} + +func Constructor(capacity int) LFUCache { + return LFUCache{capacity: capacity, freqList: NewEmptyLinkedNode(), keyMap: make(map[int]valueDesc)} +} + +func (this *LFUCache) Get(key int) int { + vdesc, ok := this.keyMap[key] + + if !ok { + return -1 + } + + valueFreqNode := vdesc.freqListNode + valueFreqData := valueFreqNode.value.(freqData) + nextFreqNode := valueFreqNode.next + + nextFreqData := freqData{} + if nextFreqNode.value != nil { + nextFreqData = nextFreqNode.value.(freqData) + } + + var higherFreqNode *LinkedNode + + if nextFreqNode == this.freqList || nextFreqData.freq != valueFreqData.freq+1 { + // Either: + // - this particular freq node is the last one, or + // - the next higher freq node is for higher frequency than we need + // For both cases, create a new freq node. + newFreqData := freqData{freq: valueFreqData.freq + 1, valuesNodes: NewEmptyLinkedNode()} + newFreqNode := NewLinkedNode(newFreqData) + valueFreqNode.AddNode(newFreqNode) + higherFreqNode = newFreqNode + } else if nextFreqData.freq == valueFreqData.freq+1 { + higherFreqNode = nextFreqNode + } else { + panic("freqNode conditions not met") + } + + // Delete the value node from its current list and add it to the new freq node's value node list + DeleteNode(vdesc.valueNode) + higherFreqData := higherFreqNode.value.(freqData) + higherFreqData.valuesNodes.AddNode(vdesc.valueNode) + + // We also need to update the freq node since it has changed + vdesc.freqListNode = higherFreqNode + this.keyMap[key] = vdesc + + // If the old freq node's value node list is empty, we delete the freq node itself + if valueFreqData.valuesNodes.IsEmpty() { + DeleteNode(valueFreqNode) + } + + return vdesc.valueNode.value.(keyValueData).value +} + +func (this *LFUCache) Put(key int, value int) { + if this.Get(key) != -1 { + vdesc, ok := this.keyMap[key] + + if !ok { + panic(fmt.Sprintf("We should have found the %d in keyMap", key)) + } + + kvData := vdesc.valueNode.value.(keyValueData) + kvData.value = value + vdesc.valueNode.value = kvData + + return + } + + // if ok { + // // Update this value. No changes needed to freqList + // kvData := vdesc.valueNode.value.(keyValueData) + // kvData.value = value + // vdesc.valueNode.value = kvData + + // return + // } + + if this.capacity == 0 { + return + } + + // TODO: remove node if we're at capacity + if this.size == this.capacity { + // Delete one node to make space + firstFreqNode := this.freqList.next + firstFreqData := firstFreqNode.value.(freqData) + firstFreqValuesNodes := firstFreqData.valuesNodes + + // Delete the last (oldest) node in the valuesNodes pointed at by this freqNode + valueNodeToDelete := firstFreqValuesNodes.prev + DeleteNode(valueNodeToDelete) + + if firstFreqValuesNodes.IsEmpty() { + DeleteNode(firstFreqNode) + } + + delete(this.keyMap, valueNodeToDelete.value.(keyValueData).key) + this.size-- + } + + newVDesc := valueDesc{} + newValueNode := NewLinkedNode(keyValueData{key: key, value: value}) + newVDesc.valueNode = newValueNode + + // key not found in map: + // check if there's a freq node that has never been accessed. If + // there's one, the new value will go into that freq node's list. + // Otherwise, we'll need to create a new freq node and add this new + // value node to that freq node's list. + if this.freqList.IsEmpty() { + newValuesNodes := NewEmptyLinkedNode() + newValuesNodes.AddNode(newValueNode) + newFreqData := freqData{freq: 0, valuesNodes: newValuesNodes} + newFreqNode := NewLinkedNode(newFreqData) + this.freqList.AddNode(newFreqNode) + newVDesc.freqListNode = newFreqNode + } else { + // does the first node have freq 0? If so, we just add to that freqNode list + firstFreqNode := this.freqList.next + // We want to panic if the types are wrong + firstFreqData := firstFreqNode.value.(freqData) + + if firstFreqData.freq == 0 { + firstFreqValuesNodes := firstFreqData.valuesNodes + firstFreqValuesNodes.AddNode(newValueNode) + newVDesc.freqListNode = firstFreqNode + } else { + // We need to add new freq node with freq 0 + newValuesNodes := NewEmptyLinkedNode() + newValuesNodes.AddNode(newValueNode) + newFreqData := freqData{freq: 0, valuesNodes: newValuesNodes} + newFreqNode := NewLinkedNode(newFreqData) + this.freqList.AddNode(newFreqNode) + newVDesc.freqListNode = newFreqNode + } + } + + this.keyMap[key] = newVDesc + + this.size++ +} + +func printFreqList(freqList *LinkedNode) { + freqNodeVisitor := func(d interface{}) { + fd := d.(freqData) + + fmt.Printf("freq: %d\n", fd.freq) + + valueNodeVisitor := func(d interface{}) { + kvData := d.(keyValueData) + + fmt.Printf("\t%d -> %d\n", kvData.key, kvData.value) + } + + fd.valuesNodes.Traverse(valueNodeVisitor) + } + + freqList.Traverse(freqNodeVisitor) +} + +func printValues(head *LinkedNode) { + visitorFn := func(d interface{}) { + num, ok := d.(int) + + if !ok { + fmt.Printf("Unexpected type\n") + } + + fmt.Printf("Data: %d\n", num) + } + + head.Traverse(visitorFn) +} + +func test1() { + lfuCache := Constructor(2) + + lfuCache.Put(1, 1) + lfuCache.Put(2, 2) + lfuCache.Put(3, 3) + + // cache.put(1, 1); + // cache.put(2, 2); + // cache.get(1); // returns 1 + // cache.put(3, 3); // evicts key 2 + // cache.get(2); // returns -1 (not found) + // cache.get(3); // returns 3. + // cache.put(4, 4); // evicts key 1. + // cache.get(1); // returns -1 (not found) + // cache.get(3); // returns 3 + // cache.get(4); + + fmt.Printf("lfuCache.Get(1) = %d\n", lfuCache.Get(1)) + fmt.Printf("lfuCache.Get(2) = %d\n", lfuCache.Get(2)) + fmt.Printf("lfuCache.Get(2) = %d\n", lfuCache.Get(2)) + fmt.Printf("lfuCache.Get(3) = %d\n", lfuCache.Get(3)) + fmt.Printf("lfuCache.Get(3) = %d\n", lfuCache.Get(3)) + + // firstFreqNode := lfuCache.freqList.next + // firstFreqData := firstFreqNode.value.(freqData) + // printValues(firstFreqData.valuesNodes) + + printFreqList(lfuCache.freqList) +} + +func test2() { + lfuCache := Constructor(2) + + lfuCache.Put(1, 1) + lfuCache.Put(2, 2) + fmt.Printf("lfuCache.Get(1) = %d (expect 1)\n", lfuCache.Get(1)) + lfuCache.Put(3, 3) + fmt.Printf("lfuCache.Get(2) = %d (expect -1)\n", lfuCache.Get(2)) + fmt.Printf("lfuCache.Get(3) = %d (expect 3)\n", lfuCache.Get(3)) + lfuCache.Put(4, 4) + fmt.Printf("lfuCache.Get(1) = %d (expect -1)\n", lfuCache.Get(1)) + fmt.Printf("lfuCache.Get(3) = %d (expect 3)\n", lfuCache.Get(3)) + fmt.Printf("lfuCache.Get(4) = %d (expect 4)\n", lfuCache.Get(4)) + + // cache.put(1, 1); + // cache.put(2, 2); + // cache.get(1); // returns 1 + // cache.put(3, 3); // evicts key 2 + // cache.get(2); // returns -1 (not found) + // cache.get(3); // returns 3. + // cache.put(4, 4); // evicts key 1. + // cache.get(1); // returns -1 (not found) + // cache.get(3); // returns 3 + // cache.get(4); + + printFreqList(lfuCache.freqList) +} + +func test3() { + // ["LFUCache","put","put","put","put","get"] + //[[2],[3,1],[2,1],[2,2],[4,4],[2]] + lfuCache := Constructor(2) + + lfuCache.Put(3, 1) + lfuCache.Put(2, 1) + lfuCache.Put(2, 2) + lfuCache.Put(4, 4) + fmt.Printf("lfuCache.Get(2) = %d (expect 2)\n", lfuCache.Get(2)) + + printFreqList(lfuCache.freqList) +} + +func test4() { + // ["LFUCache","put","put","put","put","get","get"] + // [[2],[2,1],[1,1],[2,3],[4,1],[1],[2]] + lfuCache := Constructor(2) + + lfuCache.Put(2, 1) + lfuCache.Put(1, 1) + lfuCache.Put(2, 3) + lfuCache.Put(4, 1) + // printFreqList(lfuCache.freqList) + fmt.Printf("lfuCache.Get(1) = %d (expect -1)\n", lfuCache.Get(1)) + fmt.Printf("lfuCache.Get(2) = %d (expect 3)\n", lfuCache.Get(2)) + + printFreqList(lfuCache.freqList) +} + +func main() { + // test1() + // test2() + // test3() + test4() +} diff --git a/longest-consecutive-sequence.go b/longest-consecutive-sequence.go new file mode 100644 index 0000000..ba9697c --- /dev/null +++ b/longest-consecutive-sequence.go @@ -0,0 +1,99 @@ +package main + +import "fmt" + +func max2(a, b int) int { + if a > b { + return a + } + + return b +} + +func longestConsecutive(nums []int) int { + if len(nums) == 0 { + return 0 + } + + numIndices := make(map[int]int) + + for i, n := range nums { + numIndices[n] = i + } + + distinctNums := make([]int, 0, len(nums)) + + for k, _ := range numIndices { + distinctNums = append(distinctNums, k) + } + + for i, n := range distinctNums { + numIndices[n] = i + } + + consecStartLens := make(map[int]int) + path := make([]int, 0, len(distinctNums)) + + for k, _ := range numIndices { + _, ok := numIndices[k+1] + if !ok { + consecStartLens[k] = 1 + } + } + + maxConsecLen := 1 + + for k, _ := range numIndices { + curr := k + kPath := path[:] + + for { + consecLen := consecStartLens[curr] + + if consecLen >= 1 { + // We've reached a point starting from which we know how many + // consecutive elements follow + kPathLen := len(kPath) + for i, kp := range kPath { + consecStartLens[kp] = kPathLen - i + consecLen + maxConsecLen = max2(maxConsecLen, consecStartLens[kp]) + } + + break + } else { + kPath = append(kPath, curr) + } + + curr += 1 + } + } + + return maxConsecLen +} + +func test1() { + nums := []int{100, 4, 200, 1, 3, 2} + result := longestConsecutive(nums) + + fmt.Printf("result: %d\n", result) +} + +func test2() { + nums := []int{100, -4, 200, -1, 3, 2} + result := longestConsecutive(nums) + + fmt.Printf("result: %d\n", result) +} + +func test3() { + nums := []int{100, -4, 200} + result := longestConsecutive(nums) + + fmt.Printf("result: %d\n", result) +} + +func main() { + // test1() + // test2() + test3() +} diff --git a/n-bishops.go b/n-bishops.go new file mode 100644 index 0000000..4505948 --- /dev/null +++ b/n-bishops.go @@ -0,0 +1,90 @@ +package main + +import ( + "bufio" + "fmt" + "os" +) + +type bishopPos struct { + row int + col int +} + +type board struct { + bishops []bishopPos + maxRows int + maxCols int +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} + +func readBishopPos() board { + scanner := bufio.NewScanner(os.Stdin) + maxRows, maxCols := -1, -1 + + scanner.Scan() + var n int + fmt.Sscanf(scanner.Text(), "%d", &n) + bps := make([]bishopPos, n) + + // fmt.Printf("Reading %d lines\n", n) + + for i := 0; i < n; i++ { + scanner.Scan() + fmt.Sscanf(scanner.Text(), "%d %d", &(bps[i].row), &(bps[i].col)) + maxCols = max(maxCols, bps[i].col) + } + + for _, bp := range bps { + diagL := bp.row + bp.col - 1 + diagR := bp.row + (maxCols - bp.col) + + maxRows = max(max(maxRows, diagL), diagR) + } + + return board{ + bishops: bps, + maxRows: maxRows, + maxCols: maxCols, + } +} + +func countCaptures(b board) int { + diagLCounts := make([]int, b.maxRows+1) + diagRCounts := make([]int, b.maxRows+1) + captureCount := 0 + + for _, bp := range b.bishops { + diagL := bp.row + bp.col - 1 + diagR := bp.row + (b.maxCols - bp.col) + + prevDiagLCount := diagLCounts[diagL] + prevDiagRCount := diagRCounts[diagR] + + diagLCounts[diagL]++ + diagRCounts[diagR]++ + + prevDiagLCaptures := prevDiagLCount * (prevDiagLCount - 1) / 2 + prevDiagRCaptures := prevDiagRCount * (prevDiagRCount - 1) / 2 + newDiagLCaptures := diagLCounts[diagL] * (diagLCounts[diagL] - 1) / 2 + newDiagRCaptures := diagRCounts[diagR] * (diagRCounts[diagR] - 1) / 2 + + captureCount = captureCount + (newDiagLCaptures - prevDiagLCaptures) + (newDiagRCaptures - prevDiagRCaptures) + } + + return captureCount +} + +func main() { + b := readBishopPos() + + // fmt.Printf("%v\n", b) + fmt.Printf("%d\n", countCaptures(b)) +} diff --git a/non-decreasing-array.go b/non-decreasing-array.go new file mode 100644 index 0000000..c3a876a --- /dev/null +++ b/non-decreasing-array.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" +) + +/* +Cases: + +1. a < b < c -- do nothing +2. a < b > c + 2.1 a <= c -- set b = c + 2.2 a > c -- can't fix +3. a < b = c -- do nothing +4. a > b < c + 4.1 a <= c -- set b = c + 4.2 a > c -- can't fix +5. a > b > c -- can't fix +6. a > b = c -- can't fix +7. a = b < c -- do nothing +8. a = b > c -- can't fix +9. a = b = c -- do noting + +*/ + +func checkPossibility(nums []int) bool { + if len(nums) <= 2 { + return true + } + + a, b, c := 0, 0, 0 + numChanges := 0 + + for i := 0; i < len(nums); i++ { + if i == 0 { + b, c = nums[i], nums[i + 1] + + if b > c { + numChanges++ + b = c + nums[i] = b + } + + continue + } + + if i == len(nums) - 1 { + a, b = nums[i - 1], nums[i] + + if a > b { + numChanges++ + b = a + nums[i] = b + } + + continue + } + + a, b, c = nums[i-1], nums[i], nums[i + 1] + + // 2. a < b > c + // 2.1 a <= c -- set b = c + // 2.2 a > c -- can't fix + if (a < b && b > c) || (a > b && b < c) { + if a <= c { + numChanges++ + b = c + nums[i] = b + } + + continue + } + } + + // Check if non-decreasing + for i := 0; i < len(nums); i++ { + if i < len(nums) - 1 { + if nums[i] > nums[i + 1] { + return false + } + } + } + + return numChanges <= 1 +} + +type testcase struct { + nums []int + expectedResult bool +} + +func tests() { + tcs := []testcase { + testcase{nums: []int{4}, expectedResult: true}, + testcase{nums: []int{4,2}, expectedResult: true}, + testcase{nums: []int{4,2,3}, expectedResult: true}, + testcase{nums: []int{4,3,2}, expectedResult: false}, + testcase{nums: []int{1,1,3,2}, expectedResult: true}, + testcase{nums: []int{1,2,3,2}, expectedResult: true}, + testcase{nums: []int{1,2,3,2,5}, expectedResult: true}, + testcase{nums: []int{1,7,3,2,5}, expectedResult: false}, + testcase{nums: []int{1,7,3,2,5,5}, expectedResult: false}, + testcase{nums: []int{1,2,4,5,3}, expectedResult: true}, + } + + for i, tc := range tcs { + result := checkPossibility(tc.nums) + + if result == tc.expectedResult { + fmt.Printf("TC %d PASSED\n", i) + } else { + fmt.Printf("TC %d FAILED; expected: %v, actual: %v\n", i, tc.expectedResult, result) + } + } +} + +func main() { + tests() +} \ No newline at end of file diff --git a/number-of-islands.go b/number-of-islands.go new file mode 100644 index 0000000..f7c57cf --- /dev/null +++ b/number-of-islands.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" +) + +const ( + UnvisitedLand = '1' + VisitedLand = '2' +) + +func dfs(grid [][]byte, atR, atC int) { + if grid[atR][atC] != UnvisitedLand { + return + } + + grid[atR][atC] = VisitedLand + + numRows := len(grid) + numCols := len(grid[0]) + dirR := []int{-1, 1, 0, 0} + dirC := []int{ 0, 0,-1, 1} + + for i := 0; i < len(dirR); i++ { + newR, newC := atR + dirR[i], atC + dirC[i] + + if newR < 0 || newR >= numRows || newC < 0 || newC >= numCols { + continue + } + + dfs(grid, newR, newC) + } +} + +func numIslands(grid [][]byte) int { + n := 0 + numRows := len(grid) + + if numRows == 0 { + return 0 + } + + numCols := len(grid[0]) + + for i := 0; i < numRows; i++ { + for j := 0; j < numCols; j++ { + if grid[i][j] == UnvisitedLand { + n++ + dfs(grid, i, j) + } + } + } + + return n +} + +func test1() { + grid := [][]byte { + []byte {'1','1','1','1','0'}, + []byte {'1','1','0','1','0'}, + []byte {'1','1','0','0','0'}, + []byte {'0','0','0','0','0'}, + } + + fmt.Printf("numIslands: %d\n", numIslands(grid)) +} + + + +func main() { + test1() +} diff --git a/remove-invalid-parentheses.go b/remove-invalid-parentheses.go new file mode 100644 index 0000000..8e8e2b2 --- /dev/null +++ b/remove-invalid-parentheses.go @@ -0,0 +1,154 @@ +package main + +import ( + "fmt" +) + +func mismatchedParens(s string) (int, int) { + lParens, rParens := 0, 0 + chars := []rune(s) + mismatchedLParens, mismatchedRParens := 0, 0 + + for _, c := range chars { + if c == '(' { + lParens++ + } + + if c == ')' { + rParens++ + + if lParens == 0 { + mismatchedRParens++ + } else { + lParens-- + rParens-- + } + } + + } + + mismatchedLParens = lParens + + return mismatchedLParens, mismatchedRParens +} + +func removeParensAt(chars []rune, at, lParensToDelete, rParensToDelete int, results map[string]struct{}) map[string]struct{} { + if at >= len(chars) || at < 0 { + if lParensToDelete == 0 && rParensToDelete == 0 { + // Check if balanced and add to results if it is + s := string(chars) + mismatchedL, mismatchedR := mismatchedParens(s) + + if mismatchedL == 0 && mismatchedR == 0 { + results[s] = struct{}{} + } + } + + return results + } + + c := chars[at] + + if c != '(' && c != ')' { + return removeParensAt(chars, at+1, lParensToDelete, rParensToDelete, results) + } + + if c == '(' { + if lParensToDelete > 0 { + atCharDeleted := []rune{} + atCharDeleted = append(atCharDeleted, chars[0:at]...) + if at < len(chars)-1 { + atCharDeleted = append(atCharDeleted, chars[at+1:]...) + } + + results = removeParensAt(atCharDeleted, at, lParensToDelete-1, rParensToDelete, results) + } + } else { + if rParensToDelete > 0 { + atCharDeleted := []rune{} + atCharDeleted = append(atCharDeleted, chars[0:at]...) + if at < len(chars)-1 { + atCharDeleted = append(atCharDeleted, chars[at+1:]...) + } + + results = removeParensAt(atCharDeleted, at, lParensToDelete, rParensToDelete-1, results) + } + } + + unmodified := []rune(string(chars)) + results = removeParensAt(unmodified, at+1, lParensToDelete, rParensToDelete, results) + + return results +} + +func removeInvalidParentheses(s string) []string { + mismatchedL, mismatchedR := mismatchedParens(s) + + if mismatchedL == 0 && mismatchedR == 0 || len(s) == 0 { + return []string{s} + } + + results := make(map[string]struct{}) + results = removeParensAt([]rune(s), 0, mismatchedL, mismatchedR, results) + + resultList := []string{} + for k, _ := range results { + resultList = append(resultList, k) + } + + return resultList +} + +func testMismatchedParens() { + testStrs := []string{ + "( ) ) ) ( ( ( )", // 0 + "(", // 1 + ")", // 2 + "( ( )", // 3 + "( ) )", // 4 + "( ( ) )", // 5 + "( ) ( )", // 6 + "( ( ) ) )", // 7 + "( ) ( ) ) ( )", // 8 + } + + for i, ts := range testStrs { + mL, mR := mismatchedParens(ts) + + fmt.Printf("Test %d: mL = %d, mR = %d\n", i, mL, mR) + } +} + +func testRemoveInvalidParens() { + testStrs := []string{ + // "( ) ) ) ( ( ( )", // 0 + // "(", // 1 + // ")", // 2 + "(a(b)", // 3 + // "( ) )", // 4 + "(())", // 5 + // "( ) ( )", // 6 + // "( ( ) ) )", // 7 + "(a)())()", // 8 + "()())()", // 9 + ")(", // 10 + } + + for i, ts := range testStrs { + results := removeInvalidParentheses(ts) + + fmt.Printf("Test %d:", i) + + for _, result := range results { + fmt.Printf(" \"%s\" ", result) + + } + + fmt.Println() + } +} + +func main() { + // testMismatchedParens() + testRemoveInvalidParens() +}