diff --git a/src/chapter1/problem1.go b/src/chapter1/problem1.go index d7a5730..ff1ea8f 100644 --- a/src/chapter1/problem1.go +++ b/src/chapter1/problem1.go @@ -12,3 +12,18 @@ func IsUnique(input string) bool { } return true } + +// Using no additional data structures. Assumes the string only contains alphabet. +func IsUniqueB(input string) bool { + seen := 0 + for _, r := range input { + rBit := 1 << uint32(r-'A') + if (seen & rBit) != 0 { + return false + } + + seen |= rBit + + } + return true +} diff --git a/src/chapter1/problem1_test.go b/src/chapter1/problem1_test.go index e4f35d3..86dca66 100644 --- a/src/chapter1/problem1_test.go +++ b/src/chapter1/problem1_test.go @@ -11,13 +11,32 @@ func TestIsUnique(t *testing.T) { }{ {"abcd", true}, {"abcc", false}, + {"asbckmdsf", false}, + {" ", false}, {" ", true}, {"", true}, } for _, c := range cases { actual := IsUnique(c.input) if actual != c.expected { - t.Fatalf("Input %s. Expected: %b, actual: %b\n", c.input, c.expected, actual) + t.Fatalf("Input %s. Expected: %t, actual: %t\n", c.input, c.expected, actual) + } + } +} + +func TestIsUniqueB(t *testing.T) { + cases := []struct { + input string + expected bool + }{ + {"abcd", true}, + {"abcc", false}, + {"asbckmdsf", false}, + } + for _, c := range cases { + actual := IsUniqueB(c.input) + if actual != c.expected { + t.Fatalf("Input %s. Expected: %t, actual: %t\n", c.input, c.expected, actual) } } } diff --git a/src/chapter1/problem2.go b/src/chapter1/problem2.go index 84fa563..bb64181 100644 --- a/src/chapter1/problem2.go +++ b/src/chapter1/problem2.go @@ -18,3 +18,21 @@ func ArePermutations(input1, input2 string) bool { } return true } + +func ArePermutationsB(input1, input2 string) bool { + if len(input1) != len(input2) { + return false + } + counts := make(map[rune]int) + for _, r := range input1 { + counts[r]++ + } + for _, r := range input2 { + counts[r]-- + if counts[r] < 0 { + return false + } + } + + return true +} diff --git a/src/chapter1/problem2_test.go b/src/chapter1/problem2_test.go index b602156..d85fb1f 100644 --- a/src/chapter1/problem2_test.go +++ b/src/chapter1/problem2_test.go @@ -20,7 +20,30 @@ func TestArePermutations(t *testing.T) { for _, c := range cases { actual := ArePermutations(c.input1, c.input2) if actual != c.expected { - t.Fatalf("Inputs %s, %s. Expected: %b, actual: %b\n", + t.Fatalf("Inputs %s, %s. Expected: %t, actual: %t\n", + c.input1, c.input2, c.expected, actual) + } + } +} + +func TestArePermutationsB(t *testing.T) { + cases := []struct { + input1 string + input2 string + expected bool + }{ + {"abcd", "abcd", true}, + {"abcd", "abdc", true}, + {"abcc", "ccbb", false}, + {"abcc", "abcc ", false}, + {"abccc", "abccb", false}, + {" ", " ", true}, + {"", "", true}, + } + for _, c := range cases { + actual := ArePermutationsB(c.input1, c.input2) + if actual != c.expected { + t.Fatalf("Inputs %s, %s. Expected: %t, actual: %t\n", c.input1, c.input2, c.expected, actual) } } diff --git a/src/chapter1/problem3.go b/src/chapter1/problem3.go index cb64116..145c5cf 100644 --- a/src/chapter1/problem3.go +++ b/src/chapter1/problem3.go @@ -50,3 +50,30 @@ func URLifySlice(input []rune) { } } } + +func URLifySliceWithLength(input []rune, realLength int) []rune { + inWord := false + + slowPtr := len(input) - 1 + movedChar := 0 + + for i := slowPtr; i >= 0 && movedChar < realLength; i-- { + if input[i] == rune(' ') { + if inWord { + input[slowPtr-2] = rune('%') + input[slowPtr-1] = rune('2') + input[slowPtr] = rune('0') + slowPtr -= 3 + movedChar++ + } + } else { + if !inWord { + inWord = true + } + input[slowPtr] = input[i] + movedChar++ + slowPtr-- + } + } + return input[slowPtr+1:] +} diff --git a/src/chapter1/problem3_test.go b/src/chapter1/problem3_test.go index 5ae2ff4..1d23086 100644 --- a/src/chapter1/problem3_test.go +++ b/src/chapter1/problem3_test.go @@ -39,3 +39,22 @@ func TestURLifySlice(t *testing.T) { } } } + +func TestURLifySliceB(t *testing.T) { + cases := []struct { + input []rune + expected []rune + realLength int + }{ + {[]rune("hello my name is "), []rune("hello%20my%20name%20is"), 16}, + {[]rune("hello"), []rune("hello"), 5}, + {[]rune(" hello my name is "), []rune("%20hello%20my%20name%20is"), 17}, + {[]rune(" hello my name is "), []rune("%20hello%20my%20name%20is"), 17}, + } + for _, c := range cases { + result := URLifySliceWithLength(c.input, c.realLength) + if !reflect.DeepEqual(c.expected, result) { + t.Fatalf("Expected: %s, actual: %s\n", string(c.expected), string(result)) + } + } +} diff --git a/src/chapter1/problem4.go b/src/chapter1/problem4.go index e8edd18..9a84073 100644 --- a/src/chapter1/problem4.go +++ b/src/chapter1/problem4.go @@ -20,3 +20,38 @@ func IsPalindromePerm(input string) bool { } return true } + +// with space O(1) +func IsPalindromePermB(input string) bool { + bitVector := 0 + + for _, r := range input { + index := getIndexValue(r) + if index == -1 { + continue + } + mask := 1 << uint8(index) + if (bitVector & mask) == 0 { + bitVector |= mask + } else { + bitVector &= ^mask + } + } + return (bitVector & (bitVector - 1)) == 0 + +} + +func getIndexValue(r rune) int8 { + + if r < 'A' || r > 'z' { + return int8(-1) + } + + if r >= 'a' { + return int8(r - 'a') + } else if r >= 'A' { + return int8(r - 'A') + } + + return int8(-1) +} diff --git a/src/chapter1/problem4_test.go b/src/chapter1/problem4_test.go index 5aabb17..309ec3c 100644 --- a/src/chapter1/problem4_test.go +++ b/src/chapter1/problem4_test.go @@ -18,7 +18,28 @@ func TestIsPalindromePerm(t *testing.T) { for _, c := range cases { actual := IsPalindromePerm(c.input) if actual != c.expected { - t.Fatalf("Input %s. Expected: %b, actual: %b\n", c.input, c.expected, actual) + t.Fatalf("Input %s. Expected: %t, actual: %t\n", c.input, c.expected, actual) + } + } +} + +func TestIsPalindromePermB(t *testing.T) { + cases := []struct { + input string + expected bool + }{ + {"My rats live on no evil star", false}, // not a palindrome + {"Rats live on no evil star", true}, // normal palindrome + {"amanaplanacanalpanama", true}, // normal palindrome + {"amanalpancaaanplanama", true}, // jumbled palindrome + {"amanaplanacanalpanamab", false}, // not a palindrome + {"a", true}, + {"", true}, + } + for _, c := range cases { + actual := IsPalindromePermB(c.input) + if actual != c.expected { + t.Fatalf("Input %s. Expected: %t, actual: %t\n", c.input, c.expected, actual) } } } diff --git a/src/chapter1/problem5.go b/src/chapter1/problem5.go index 3bff2e2..241edf6 100644 --- a/src/chapter1/problem5.go +++ b/src/chapter1/problem5.go @@ -53,3 +53,35 @@ func AreOneEditAway(input1, input2 string) bool { return oneRemovalAway(input1, input2) } } + +func AreOneEditAwayB(input1, input2 string) bool { + + longerOne, shorterOne := input1, input2 + if len(longerOne) < len(shorterOne) { + longerOne = input2 + shorterOne = input1 + } + longerLength, shorterLength := len(longerOne), len(shorterOne) + if (longerLength - shorterLength) > 1 { + return false + } + + diffFound := false + for i, j := 0, 0; i < longerLength && j < shorterLength; i++ { + + if longerOne[i] != shorterOne[j] { + if diffFound { + return false + } else { + diffFound = true + } + if longerLength == shorterLength { + j++ + } + } else { + j++ + } + } + + return true +} diff --git a/src/chapter1/problem5_test.go b/src/chapter1/problem5_test.go index f18291b..41b1694 100644 --- a/src/chapter1/problem5_test.go +++ b/src/chapter1/problem5_test.go @@ -17,6 +17,8 @@ func TestAreOneEditAway(t *testing.T) { {"abcd", "abcdef", false}, {"abcde", "abcd", true}, {"abcdef", "abcd", false}, + {"abbc", "abcd", false}, + {"abbcd", "abcd", true}, {" ", "", true}, {"", " ", true}, {"", "", true}, @@ -24,7 +26,35 @@ func TestAreOneEditAway(t *testing.T) { for _, c := range cases { actual := AreOneEditAway(c.input1, c.input2) if actual != c.expected { - t.Fatalf("Inputs %s, %s. Expected: %b, actual: %b\n", + t.Fatalf("Inputs %s, %s. Expected: %t, actual: %t\n", + c.input1, c.input2, c.expected, actual) + } + } +} + +func TestAreOneEditAwayB(t *testing.T) { + cases := []struct { + input1 string + input2 string + expected bool + }{ + {"abcd", "abcd", true}, + {"abcd", "abcc", true}, + {"abcd", "accc", false}, + {"abcd", "abcde", true}, + {"abcd", "abcdef", false}, + {"abcde", "abcd", true}, + {"abcdef", "abcd", false}, + {"abbc", "abcd", false}, + {"abbcd", "abcd", true}, + {" ", "", true}, + {"", " ", true}, + {"", "", true}, + } + for _, c := range cases { + actual := AreOneEditAwayB(c.input1, c.input2) + if actual != c.expected { + t.Fatalf("Inputs %s, %s. Expected: %t, actual: %t\n", c.input1, c.input2, c.expected, actual) } } diff --git a/src/chapter1/problem6.go b/src/chapter1/problem6.go index 1710c25..a4ca22f 100644 --- a/src/chapter1/problem6.go +++ b/src/chapter1/problem6.go @@ -1,6 +1,7 @@ package chapter1 import ( + "bytes" "strconv" ) @@ -23,3 +24,28 @@ func BasicCompress(input string) string { return compStr } } + +func BasicCompressB(input string) string { + var compressed bytes.Buffer + counter := 1 + inputLen := len(input) + for i := 1; i < inputLen; i++ { + + if input[i-1] == input[i] { + counter++ + } else { + compressed.WriteByte(input[i-1]) + compressed.WriteString(strconv.Itoa(counter)) + counter = 1 + } + if compressed.Len() >= inputLen { + return input + } + } + compressed.WriteByte(input[inputLen-1]) + compressed.WriteString(strconv.Itoa(counter)) + if compressed.Len() >= inputLen { + return input + } + return compressed.String() +} diff --git a/src/chapter1/problem6_test.go b/src/chapter1/problem6_test.go index 863d338..e8ef83b 100644 --- a/src/chapter1/problem6_test.go +++ b/src/chapter1/problem6_test.go @@ -19,3 +19,21 @@ func TestBasicCompress(t *testing.T) { } } } +func TestBasicCompressB(t *testing.T) { + cases := []struct { + input string + expected string + }{ + {"aaaabbbcdddd", "a4b3c1d4"}, + {"aaaabbbbbbbbbbbcdddd", "a4b11c1d4"}, + {"aaaabbbbbbbbbbbcd", "a4b11c1d1"}, + {"abcd", "abcd"}, // compressed is longer, expect input + {"aabbccdd", "aabbccdd"}, // compressed is longer, expect input + } + for _, c := range cases { + actual := BasicCompressB(c.input) + if actual != c.expected { + t.Fatalf("Input %s. Expected: %s, actual: %s\n", c.input, c.expected, actual) + } + } +} diff --git a/src/chapter1/problem7_test.go b/src/chapter1/problem7_test.go index 289ac2a..15acc14 100644 --- a/src/chapter1/problem7_test.go +++ b/src/chapter1/problem7_test.go @@ -35,6 +35,21 @@ func TestMatrixRotate(t *testing.T) { []int{15, 11, 7, 3}, []int{16, 12, 8, 4}, }, + },{ + [][]int{ + []int{1, 2, 3, 4,5}, + []int{5, 6, 7, 8,9}, + []int{9, 10, 11, 12,13}, + []int{13, 14, 15, 16,17}, + []int{23, 24, 25, 26,27}, + }, + [][]int{ + []int{23,13, 9, 5, 1}, + []int{24,14, 10, 6, 2}, + []int{25,15, 11, 7, 3}, + []int{26,16, 12, 8, 4}, + []int{27,17, 13, 9, 5}, + }, }, } for _, c := range cases { diff --git a/src/chapter1/problem8.go b/src/chapter1/problem8.go index f393b96..88b4659 100644 --- a/src/chapter1/problem8.go +++ b/src/chapter1/problem8.go @@ -1,5 +1,58 @@ package chapter1 +func ZeroMatrixB(matrix [][]int) { + + rowCnt, columnCnt := len(matrix), len(matrix[0]) + if rowCnt == 0 { + return + } + var zeroFirstCol, zeroFirstRow bool + for j := 0; j < columnCnt; j++ { + if matrix[0][j] == 0 { + zeroFirstRow = true + break + } + } + for i := 0; i < rowCnt; i++ { + if matrix[i][0] == 0 { + zeroFirstCol = true + break + } + } + for i := 1; i < rowCnt; i++ { + for j := 1; j < columnCnt; j++ { + if matrix[i][j] == 0 { + matrix[0][j] = 0 + matrix[i][0] = 0 + } + } + } + + for j := 1; j < columnCnt; j++ { + + if matrix[0][j] == 0 { + ZeroColumn(matrix, j) + } + + } + + for i := 1; i < rowCnt; i++ { + + if matrix[i][0] == 0 { + ZeroRow(matrix, i) + } + + } + if zeroFirstCol { + ZeroColumn(matrix, 0) + } + + if zeroFirstRow { + ZeroRow(matrix, 0) + } + +} + func ZeroRow(matrix [][]int, row int) { for i := 0; i < len(matrix[0]); i++ { matrix[row][i] = 0 diff --git a/src/chapter1/problem8_test.go b/src/chapter1/problem8_test.go index 7de6d07..5587ba7 100644 --- a/src/chapter1/problem8_test.go +++ b/src/chapter1/problem8_test.go @@ -66,3 +66,65 @@ func TestZeroMatrix(t *testing.T) { } } } + +func TestZeroMatrixB(t *testing.T) { + cases := []struct { + input [][]int + expected [][]int + }{ + { + [][]int{ + []int{1, 2, 3}, + []int{4, 0, 6}, + []int{7, 8, 9}, + }, + [][]int{ + []int{1, 0, 3}, + []int{0, 0, 0}, + []int{7, 0, 9}, + }, + }, + { + [][]int{ + []int{1, 2, 3}, + []int{4, 5, 6}, + []int{7, 8, 0}, + }, + [][]int{ + []int{1, 2, 0}, + []int{4, 5, 0}, + []int{0, 0, 0}, + }, + }, + { + [][]int{ + []int{1, 2, 3}, + []int{0, 5, 6}, + []int{7, 8, 9}, + }, + [][]int{ + []int{0, 2, 3}, + []int{0, 0, 0}, + []int{0, 8, 9}, + }, + }, + { + [][]int{ + []int{1, 2, 3}, + []int{4, 5, 6}, + []int{7, 8, 9}, + }, + [][]int{ + []int{1, 2, 3}, + []int{4, 5, 6}, + []int{7, 8, 9}, + }, + }, + } + for _, c := range cases { + ZeroMatrixB(c.input) + if !reflect.DeepEqual(c.input, c.expected) { + t.Fatalf("Expected: %v, actual: %v\n", c.expected, c.input) + } + } +} diff --git a/src/chapter1/problem9_test.go b/src/chapter1/problem9_test.go index 14157b0..d56445d 100644 --- a/src/chapter1/problem9_test.go +++ b/src/chapter1/problem9_test.go @@ -20,7 +20,7 @@ func TestIsRotation(t *testing.T) { for _, c := range cases { actual := IsRotation(c.input1, c.input2) if actual != c.expected { - t.Fatalf("Inputs %s, %s. Expected: %b, actual: %b\n", + t.Fatalf("Inputs %s, %s. Expected: %t, actual: %t\n", c.input1, c.input2, c.expected, actual) } } diff --git a/src/chapter2/problem4.go b/src/chapter2/problem4.go index 37d2c0c..ecb1103 100644 --- a/src/chapter2/problem4.go +++ b/src/chapter2/problem4.go @@ -19,3 +19,33 @@ func (ll *DoublyLinkedList) PivotAroundValue(value int) { ll.tail = higher.tail ll.head = lower.head } + +func (ll *DoublyLinkedList) PivotAroundValueB(value int) { + lower := GetLinkedList() + higher := GetLinkedList() + middle := GetLinkedList() + node := ll.head + for i := 0; i < ll.count; i++ { + next := node.next + if node.value == value { + middle.insertNode(node) + } else if node.value < value { + lower.insertNode(node) + } else { + higher.insertNode(node) + } + node = next + } + // Join higher and lower lists, and replace ll. + if middle.Len() > 0 { + lower.tail.next = middle.head + middle.head.prev = lower.tail + middle.tail.next = higher.head + higher.head.prev = middle.tail + } else { + lower.tail.next = higher.head + higher.head.prev = lower.tail + } + ll.tail = higher.tail + ll.head = lower.head +} diff --git a/src/chapter2/problem4_test.go b/src/chapter2/problem4_test.go index 02af8d2..9577892 100644 --- a/src/chapter2/problem4_test.go +++ b/src/chapter2/problem4_test.go @@ -15,3 +15,14 @@ func TestPivotAroundValue(t *testing.T) { t.Fatalf("Expected: %v, actual: %v\n", expected, actual) } } + +func TestPivotAroundValueB(t *testing.T) { + vals := []int{1, 10, 20, 4, 7, 10, 3, 25} + ll := GetLinkedListFromValues(vals) + ll.PivotAroundValueB(10) + expected := []int{1, 4, 7, 3, 10, 10, 20, 25} + actual := ll.Slice() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("Expected: %v, actual: %v\n", expected, actual) + } +} diff --git a/src/chapter2/problem5.go b/src/chapter2/problem5.go index bf3def6..76dec1d 100644 --- a/src/chapter2/problem5.go +++ b/src/chapter2/problem5.go @@ -20,3 +20,30 @@ func AddTwoLists(l1, l2 *DoublyLinkedList) int { } return res } + +func AddTwoListsB(l1, l2 *DoublyLinkedList) *DoublyLinkedList { + result := GetLinkedList() + carryNumber := 0 + sum := 0 + n1, n2 := l1.head, l2.head + for n1 != nil || n2 != nil { + sum = carryNumber + if n1 != nil { + sum += n1.value + n1 = n1.next + } + + if n2 != nil { + sum += n2.value + n2 = n2.next + } + + carryNumber = sum / 10 + result.Insert(sum % 10) + + } + if carryNumber != 0 { + result.Insert(carryNumber) + } + return result +} diff --git a/src/chapter2/problem5_test.go b/src/chapter2/problem5_test.go index a046a15..bcc1c41 100644 --- a/src/chapter2/problem5_test.go +++ b/src/chapter2/problem5_test.go @@ -1,6 +1,7 @@ package chapter2 import ( + "reflect" "testing" ) @@ -14,6 +15,11 @@ func TestAddTwoLists(t *testing.T) { []int{5, 9, 2}, 912, }, + { + []int{7, 1, 6}, + []int{5, 9, 8}, + 1512, + }, { []int{7, 1, 6}, []int{2, 4}, @@ -44,3 +50,50 @@ func TestAddTwoLists(t *testing.T) { } } } + +func TestAddTwoListsB(t *testing.T) { + cases := []struct { + vals1, vals2, expected []int + }{ + { + []int{7, 1, 6}, + []int{5, 9, 2}, + []int{2, 1, 9}, + }, + { + []int{7, 1, 6}, + []int{5, 9, 8}, + []int{2, 1, 5, 1}, + }, + { + []int{7, 1, 6}, + []int{2, 4}, + []int{9, 5, 6}, + }, + { + []int{2, 4}, + []int{7, 1, 6}, + []int{9, 5, 6}, + }, + { + []int{}, + []int{7, 1, 6}, + []int{7, 1, 6}, + }, + { + []int{7, 1, 6}, + []int{}, + []int{7, 1, 6}, + }, + } + for _, c := range cases { + l1 := GetLinkedListFromValues(c.vals1) + l2 := GetLinkedListFromValues(c.vals2) + + actual := AddTwoListsB(l1, l2).Slice() + + if !reflect.DeepEqual(c.expected, actual) { + t.Fatalf("Expected: %v, actual: %v\n", c.expected, actual) + } + } +} diff --git a/src/chapter2/problem8_test.go b/src/chapter2/problem8_test.go index 0db0bd8..6de27d4 100644 --- a/src/chapter2/problem8_test.go +++ b/src/chapter2/problem8_test.go @@ -48,6 +48,10 @@ func TestFindLoopNodePositive(t *testing.T) { []int{100, 101, 102, 103, 104}, 2, }, + { + []int{100, 101, 102, 103, 104}, + 1, + }, { []int{100, 101, 102, 103, 104}, 4, diff --git a/src/chapter3/problem6_test.go b/src/chapter3/problem6_test.go index c34ef20..5bc00b6 100644 --- a/src/chapter3/problem6_test.go +++ b/src/chapter3/problem6_test.go @@ -16,20 +16,20 @@ func TestPetShelter(t *testing.T) { t.Fatalf("Unexpected error: %v\n", err) } if i.petType != DogType || i.name != "lucky" { - t.Fatalf("Expected: lucky, actual: %d\n", i.name) + t.Fatalf("Expected: lucky, actual: %s\n", i.name) } i, err = ps.dequeueDog() if err != nil { t.Fatalf("Unexpected error: %v\n", err) } if i.petType != DogType || i.name != "spot" { - t.Fatalf("Expected: spot, actual: %d\n", i.name) + t.Fatalf("Expected: spot, actual: %s\n", i.name) } i, err = ps.dequeueCat() if err != nil { t.Fatalf("Unexpected error: %v\n", err) } if i.petType != CatType || i.name != "snowball" { - t.Fatalf("Expected: snowball, actual: %d\n", i.name) + t.Fatalf("Expected: snowball, actual: %s\n", i.name) } } diff --git a/src/chapter4/binary_tree_node.go b/src/chapter4/binary_tree_node.go new file mode 100644 index 0000000..9751ad8 --- /dev/null +++ b/src/chapter4/binary_tree_node.go @@ -0,0 +1,21 @@ +package chapter4 + +// BTNode represents a node in binary tree +type BTNode struct { + Value int + Parent *BTNode + Left *BTNode + Right *BTNode +} + +// SetRight sets child on right side +func (n *BTNode) SetRight(c *BTNode) { + n.Right = c + c.Parent = n +} + +// SetLeft sets child on right side +func (n *BTNode) SetLeft(c *BTNode) { + n.Left = c + c.Parent = n +} diff --git a/src/chapter4/graph_node.go b/src/chapter4/graph_node.go new file mode 100644 index 0000000..946332f --- /dev/null +++ b/src/chapter4/graph_node.go @@ -0,0 +1,12 @@ +package chapter4 + +// GraphNode represents a node in graph +type GraphNode struct { + Value int + Nodes []*GraphNode +} + +// PushNodes append node into node list +func (n *GraphNode) PushNodes(nodes ...*GraphNode) { + n.Nodes = append(n.Nodes, nodes...) +} diff --git a/src/chapter4/problem1.go b/src/chapter4/problem1.go new file mode 100644 index 0000000..816e5b5 --- /dev/null +++ b/src/chapter4/problem1.go @@ -0,0 +1,32 @@ +package chapter4 + +import "container/list" + +// RouteBetweenNodes returns true if there is a path between start and end on directed graph +func RouteBetweenNodes(start, end *GraphNode) bool { + if start == nil || end == nil { + return false + } + visits := make(map[*GraphNode]bool, 0) + + list := list.New() + list.PushFront(start) + for list.Len() > 0 { + node := list.Back() + list.Remove(node) + gNode := node.Value.(*GraphNode) + + if gNode == end { + return true + } + + visits[gNode] = true + for _, n := range gNode.Nodes { + if _, ok := visits[n]; !ok { + list.PushFront(n) + } + } + } + + return false +} diff --git a/src/chapter4/problem1_test.go b/src/chapter4/problem1_test.go new file mode 100644 index 0000000..c20dff6 --- /dev/null +++ b/src/chapter4/problem1_test.go @@ -0,0 +1,94 @@ +package chapter4 + +import "testing" + +func TestRouteBetweenNodes(t *testing.T) { + // Initiate Node for Graph + node1 := &GraphNode{Value: 1, Nodes: []*GraphNode(nil)} + node2 := &GraphNode{Value: 2, Nodes: []*GraphNode(nil)} + node3 := &GraphNode{Value: 3, Nodes: []*GraphNode(nil)} + node4 := &GraphNode{Value: 4, Nodes: []*GraphNode(nil)} + node5 := &GraphNode{Value: 5, Nodes: []*GraphNode(nil)} + node6 := &GraphNode{Value: 6, Nodes: []*GraphNode(nil)} + node7 := &GraphNode{Value: 7, Nodes: []*GraphNode(nil)} + + // Conect nodes + /* + * // un-directed + * 1 - 2 + * | / + * 3 - 4 - 5 + * + * // directed + * 6 -> 7 + */ + node1.PushNodes(node2, node3) + node2.PushNodes(node1, node3) + node3.PushNodes(node2, node4) + node4.PushNodes(node3, node5) + node5.PushNodes(node4) + node6.PushNodes(node7) + node7.PushNodes() + + type ( + in struct { + start *GraphNode + end *GraphNode + } + ) + tests := map[string]struct { + in + out bool + }{ + + "Should return true when there is a route": { + in: in{start: node1, end: node2}, + out: true, + }, + + "Should return true when there is a route and route is greather than 1": { + in: in{start: node1, end: node5}, + out: true, + }, + + "Should return true when start and end are the same": { + in: in{start: node1, end: node1}, + out: true, + }, + + "Should return false when there is no route": { + in: in{start: node1, end: node6}, + out: false, + }, + + "Should return false when end has a route to start but start deons't have a route to end": { + in: in{start: node7, end: node6}, + out: false, + }, + + // Edge cases + "Should return false when start is nil": { + in: in{start: nil, end: node1}, + out: false, + }, + + "Should return false when end is nil": { + in: in{start: node1, end: nil}, + out: false, + }, + + "Should return false when start and end are nil": { + in: in{start: nil, end: nil}, + out: false, + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + out := RouteBetweenNodes(test.in.start, test.in.end) + if out != test.out { + t.Errorf("actual=%t expected=%t", out, test.out) + } + }) + } +} diff --git a/src/chapter4/problem2.go b/src/chapter4/problem2.go new file mode 100644 index 0000000..d9afb45 --- /dev/null +++ b/src/chapter4/problem2.go @@ -0,0 +1,27 @@ +package chapter4 + +// MinimalTree creates a binary search tree by given sorted slice +func MinimalTree(nums []int) *BTNode { + if len(nums) == 0 { + return nil + } + + return MinimalTreeR(nums, 0, len(nums)-1) +} + +// MinimalTreeR is recursive function to build minimal tree +func MinimalTreeR(nums []int, start, end int) *BTNode { + if start > end { + return nil + } + + mid := (start + end) / 2 + left := MinimalTreeR(nums, start, mid-1) + right := MinimalTreeR(nums, mid+1, end) + + return &BTNode{ + Value: nums[mid], + Left: left, + Right: right, + } +} diff --git a/src/chapter4/problem2_test.go b/src/chapter4/problem2_test.go new file mode 100644 index 0000000..5034329 --- /dev/null +++ b/src/chapter4/problem2_test.go @@ -0,0 +1,103 @@ +package chapter4 + +import ( + "reflect" + "testing" +) + +func TestMinimalTree(t *testing.T) { + tests := map[string]struct { + in []int + out *BTNode + }{ + + "Should return nil when slice is empty": { + in: []int{}, + out: nil, + }, + + "Should return only root node when slice has single value": { + in: []int{1}, + out: &BTNode{Value: 1}, + }, + + "Should return minimal BST when slice has 2 values": { + in: []int{1, 2}, + out: &BTNode{ + Value: 1, + Right: &BTNode{Value: 2}, + }, + }, + + "Should return minimal BST when slice has 3 values": { + in: []int{1, 2, 3}, + out: &BTNode{ + Value: 2, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 3}, + }, + }, + + "Should return minimal BST when slice has 4 values": { + in: []int{1, 2, 3, 4}, + out: &BTNode{ + Value: 2, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 3, + Right: &BTNode{Value: 4}, + }, + }, + }, + + "Should return minimal BST when slice has 5 values": { + in: []int{1, 2, 3, 4, 5}, + out: &BTNode{ + Value: 3, + Left: &BTNode{Value: 1, + Right: &BTNode{Value: 2}, + }, + Right: &BTNode{Value: 4, + Right: &BTNode{Value: 5}, + }, + }, + }, + + "Should return minimal BST when slice has 6 values": { + in: []int{1, 2, 3, 4, 5, 6}, + out: &BTNode{ + Value: 3, + Left: &BTNode{Value: 1, + Right: &BTNode{Value: 2}, + }, + Right: &BTNode{Value: 5, + Left: &BTNode{Value: 4}, + Right: &BTNode{Value: 6}, + }, + }, + }, + + "Should return minimal BST when slice has 7 values": { + in: []int{1, 2, 3, 4, 5, 6, 7}, + out: &BTNode{ + Value: 4, + Left: &BTNode{Value: 2, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 3}, + }, + Right: &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7}, + }, + }, + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + out := MinimalTree(test.in) + if !reflect.DeepEqual(out, test.out) { + t.Errorf("actual=%+v expected=%+v", out, test.out) + } + }) + } +} diff --git a/src/chapter4/problem3.go b/src/chapter4/problem3.go new file mode 100644 index 0000000..400f5dd --- /dev/null +++ b/src/chapter4/problem3.go @@ -0,0 +1,40 @@ +package chapter4 + +import "container/list" + +// ListOfDepth returns slice of list which has nodes on each depth +func ListOfDepth(root *BTNode) []*list.List { + if root == nil { + return nil + } + + var res []*list.List + queue := list.New() + queue.PushFront(root) + for queue.Len() > 0 { + res = append(res, cloneList(queue)) + nextQueue := list.New() + for queue.Len() > 0 { + elm := queue.Back() + queue.Remove(elm) + btNode := elm.Value.(*BTNode) + + if btNode.Left != nil { + nextQueue.PushFront(btNode.Left) + } + if btNode.Right != nil { + nextQueue.PushFront(btNode.Right) + } + } + + queue = nextQueue + } + + return res +} + +func cloneList(l *list.List) *list.List { + clone := list.New() + clone.PushBackList(l) + return clone +} diff --git a/src/chapter4/problem3_test.go b/src/chapter4/problem3_test.go new file mode 100644 index 0000000..d57ff67 --- /dev/null +++ b/src/chapter4/problem3_test.go @@ -0,0 +1,145 @@ +package chapter4 + +import ( + "container/list" + "reflect" + "testing" +) + +func TestListOfDepth(t *testing.T) { + tests := map[string]struct { + in *BTNode + out []*list.List + }{ + + "Should return slice of a single list when binary tree has only root": { + in: &BTNode{ + Value: 1, + }, + out: []*list.List{ + pushList(list.New(), &BTNode{Value: 1}), + }, + }, + + "Should return slice of lists when binary tree has nodes": { + in: &BTNode{ + Value: 4, + Left: &BTNode{Value: 2, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 3}, + }, + Right: &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7}, + }, + }, + out: []*list.List{ + pushList(list.New(), + &BTNode{ + Value: 4, + Left: &BTNode{Value: 2, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 3}, + }, + Right: &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7}, + }, + }), + pushList(list.New(), + &BTNode{Value: 2, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 3}, + }, + &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7}, + }, + ), + pushList(list.New(), + &BTNode{Value: 1}, + &BTNode{Value: 3}, + &BTNode{Value: 5}, + &BTNode{Value: 7}, + ), + }, + }, + + "Should return slice of lists when binary tree is unbalanced": { + in: &BTNode{ + Value: 4, + Left: &BTNode{Value: 2}, + Right: &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7, + Right: &BTNode{Value: 8, + Right: &BTNode{Value: 9}, + }, + }, + }, + }, + out: []*list.List{ + pushList(list.New(), + &BTNode{ + Value: 4, + Left: &BTNode{Value: 2}, + Right: &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7, + Right: &BTNode{Value: 8, + Right: &BTNode{Value: 9}, + }, + }, + }, + }), + pushList(list.New(), + &BTNode{Value: 2}, + &BTNode{Value: 6, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 7, + Right: &BTNode{Value: 8, + Right: &BTNode{Value: 9}, + }, + }, + }, + ), + pushList(list.New(), + &BTNode{Value: 5}, + &BTNode{Value: 7, + Right: &BTNode{Value: 8, + Right: &BTNode{Value: 9}, + }, + }, + ), + pushList(list.New(), + &BTNode{Value: 8, + Right: &BTNode{Value: 9}, + }, + ), + pushList(list.New(), &BTNode{Value: 9}), + }, + }, + + "Should return nil if root is nil": { + in: nil, + out: []*list.List(nil), + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + out := ListOfDepth(test.in) + if !reflect.DeepEqual(out, test.out) { + t.Errorf("actual=%+v expected=%+v", out, test.out) + } + }) + } +} + +func pushList(l *list.List, data ...interface{}) *list.List { + for _, d := range data { + l.PushFront(d) + } + + return l +} diff --git a/src/chapter4/problem4.go b/src/chapter4/problem4.go new file mode 100644 index 0000000..f5b0889 --- /dev/null +++ b/src/chapter4/problem4.go @@ -0,0 +1,34 @@ +package chapter4 + +import "math" + +// CheckBalanced checks if given tree is balanced +func CheckBalanced(root *BTNode) bool { + return CheckBalancedR(root) != math.MinInt64 +} + +// CheckBalancedR checks if given tree is balanced by checking depth +// It returns math.MinInt64 if it's not balanced +func CheckBalancedR(node *BTNode) int64 { + if node == nil { + return -1 + } + + leftD := CheckBalancedR(node.Left) + if leftD == math.MinInt64 { + return math.MinInt64 + } + + rightD := CheckBalancedR(node.Right) + if rightD == math.MinInt64 { + return math.MinInt64 + } + + if diff := leftD - rightD; diff < -1 || diff > 1 { + return math.MinInt64 + } + if rightD < leftD { + return leftD + 1 + } + return rightD + 1 +} diff --git a/src/chapter4/problem4_test.go b/src/chapter4/problem4_test.go new file mode 100644 index 0000000..8871c60 --- /dev/null +++ b/src/chapter4/problem4_test.go @@ -0,0 +1,53 @@ +package chapter4 + +import "testing" + +func TestCheckBalanced(t *testing.T) { + tests := map[string]struct { + in *BTNode + out bool + }{ + + "Should return true when tree only has a single node": { + in: &BTNode{}, + out: true, + }, + + "Should return true when difference of height of subtree is less than or equal to 1": { + in: &BTNode{ + Right: &BTNode{ + Right: &BTNode{}, + }, + Left: &BTNode{}, + }, + out: true, + }, + + "Should return false when difference of height of subtree is greater than 1": { + in: &BTNode{ + Right: &BTNode{ + Right: &BTNode{ + Right: &BTNode{}, + Left: &BTNode{}, + }, + }, + Left: &BTNode{}, + }, + out: false, + }, + + "Should return true when tree is nil": { + in: nil, + out: true, + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + out := CheckBalanced(test.in) + if out != test.out { + t.Errorf("actual=%t expected=%t", out, test.out) + } + }) + } +} diff --git a/src/chapter4/problem5.go b/src/chapter4/problem5.go new file mode 100644 index 0000000..8c0ab97 --- /dev/null +++ b/src/chapter4/problem5.go @@ -0,0 +1,26 @@ +package chapter4 + +// ValidateBST checks binary tree to see if it's valid binary search tree +func ValidateBST(node *BTNode) bool { + if node == nil { + return true + } + return ValidateBSTR(node.Left, nil, node.Value) && ValidateBSTR(node.Right, node.Value, nil) +} + +// ValidateBSTR checks binary tree to see if it's valid binary search tree +func ValidateBSTR(node *BTNode, min, max interface{}) bool { + if node == nil { + return true + } + + if min != nil && node.Value < min.(int) { + return false + } + + if max != nil && node.Value > max.(int) { + return false + } + + return ValidateBSTR(node.Left, min, node.Value) && ValidateBSTR(node.Right, node.Value, max) +} diff --git a/src/chapter4/problem5_test.go b/src/chapter4/problem5_test.go new file mode 100644 index 0000000..abc8ee9 --- /dev/null +++ b/src/chapter4/problem5_test.go @@ -0,0 +1,108 @@ +package chapter4 + +import "testing" + +func TestValidateBST(t *testing.T) { + tests := map[string]struct { + in *BTNode + out bool + }{ + + "Should return true when root is nul": { + in: nil, + out: true, + }, + + "Should return true when BST has only root": { + in: &BTNode{}, + out: true, + }, + + "Should return true when left and right children are valid": { + in: &BTNode{ + Value: 10, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 15}, + }, + out: true, + }, + + "Should return true when left and right subtrees of root are valid binary search tree": { + in: &BTNode{ + Value: 10, + Left: &BTNode{ + Value: 5, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 7}, + }, + Right: &BTNode{ + Value: 15, + Left: &BTNode{Value: 12}, + Right: &BTNode{Value: 17}, + }, + }, + out: true, + }, + + "Should return false when left child of root is invalid": { + in: &BTNode{ + Value: 10, + Left: &BTNode{Value: 15}, + Right: &BTNode{Value: 15}, + }, + out: false, + }, + + "Should return false when right child of root is invalid": { + in: &BTNode{ + Value: 10, + Left: &BTNode{Value: 5}, + Right: &BTNode{Value: 5}, + }, + out: false, + }, + + "Should return false when left subtrees of root is valid binary search tree but max value of the subtree is greater than root": { + in: &BTNode{ + Value: 10, + Left: &BTNode{ + Value: 5, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 11}, + }, + Right: &BTNode{ + Value: 15, + Left: &BTNode{Value: 12}, + Right: &BTNode{Value: 17}, + }, + }, + out: false, + }, + + "Should return false when right subtrees of root is valid binary search tree but max value of the subtree is greater than root": { + in: &BTNode{ + Value: 10, + Left: &BTNode{ + Value: 5, + Left: &BTNode{Value: 1}, + Right: &BTNode{Value: 7}, + }, + Right: &BTNode{ + Value: 15, + Left: &BTNode{Value: 7}, + Right: &BTNode{Value: 17}, + }, + }, + out: false, + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + out := ValidateBST(test.in) + if out != test.out { + t.Errorf("actual=%t expected=%t", out, test.out) + } + }) + } +} diff --git a/src/chapter4/problem6.go b/src/chapter4/problem6.go new file mode 100644 index 0000000..44c1f8b --- /dev/null +++ b/src/chapter4/problem6.go @@ -0,0 +1,26 @@ +package chapter4 + +// Successor checks the next node in binary tree with in-order traversal +func Successor(node *BTNode) *BTNode { + if node == nil { + return nil + } + + // node has right child + if node.Right != nil { + for n := node.Right; ; n = n.Left { + if n.Left == nil { + return n + } + } + } + + // node is right child of Parent + for ; node.Parent != nil; node = node.Parent { + if node.Parent.Left == node { + return node.Parent + } + } + + return nil +} diff --git a/src/chapter4/problem6_test.go b/src/chapter4/problem6_test.go new file mode 100644 index 0000000..e139dd8 --- /dev/null +++ b/src/chapter4/problem6_test.go @@ -0,0 +1,88 @@ +package chapter4 + +import ( + "reflect" + "testing" +) + +func TestSuccessor(t *testing.T) { + // Initiate Node for parent traversal + n1 := &BTNode{} + n2 := &BTNode{} + n3 := &BTNode{} + n2.SetRight(n1) + n3.SetLeft(n2) + + n4 := &BTNode{} + n5 := &BTNode{} + n6 := &BTNode{} + n5.SetRight(n4) + n6.SetRight(n5) + + tests := map[string]struct { + in *BTNode + out *BTNode + }{ + + "Should return nil if input is nil": { + in: nil, + out: nil, + }, + + "Should return nil if input doesn't have parent and children": { + in: &BTNode{}, + out: nil, + }, + + "Should return most left node of right subtree": { + in: &BTNode{ + Right: &BTNode{ + Left: &BTNode{ + Value: 5, + Left: &BTNode{ + Value: 10, + Right: &BTNode{}, + }, + }, + }, + }, + out: &BTNode{ + Value: 10, + Right: &BTNode{}, + }, + }, + + /* + * n3 + * / + * n2 + * \ + * n1 + */ + "Should return parent which has left child": { + in: n1, + out: n3, + }, + + /* + * n6 + * \ + * n5 + * \ + * n4 + */ + "Should return nil whne input is the last node to traversal": { + in: n4, + out: nil, + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + out := Successor(test.in) + if !reflect.DeepEqual(out, test.out) { + t.Errorf("actual=%+v expected=%+v", out, test.out) + } + }) + } +} diff --git a/src/chapter4/problem7.go b/src/chapter4/problem7.go new file mode 100644 index 0000000..ff79083 --- /dev/null +++ b/src/chapter4/problem7.go @@ -0,0 +1,98 @@ +package chapter4 + +type Project struct { + Name string + + Children []*Project + DepCnt int +} + +type ProjectNode struct { + Name string + + State int // 0 new 1 checking 2 done + + Dependencies []*ProjectNode +} + +func OrderProjects(projects []string, Dependencies [][]string) []string { + projectMap := make(map[string]*Project, len(projects)) + for _, name := range projects { + projectMap[name] = &Project{Name: name, Children: make([]*Project, 0), DepCnt: 0} + } + for _, dep := range Dependencies { + from, to := projectMap[dep[0]], projectMap[dep[1]] + to.DepCnt++ + from.Children = append(from.Children, to) + + } + + ordered := make([]string, len(projects)) + index := 0 + for len(projectMap) > 0 { + for _, project := range projectMap { + if project.DepCnt == 0 { + ordered[index] = project.Name + index++ + for _, child := range project.Children { + child.DepCnt-- + } + delete(projectMap, project.Name) + } + } + } + return ordered +} + +func OrderProjectsDFS(projects []string, dependencies [][]string) []string { + projectNodes := make(map[string]*ProjectNode, len(projects)) + + for _, name := range projects { + projectNodes[name] = &ProjectNode{Name: name, Dependencies: make([]*ProjectNode, 0), State: 0} + } + for _, dep := range dependencies { + from, to := projectNodes[dep[0]], projectNodes[dep[1]] + + to.Dependencies = append(to.Dependencies, from) + + } + + ordered := make([]string, len(projects)) + index := 0 + + for _, project := range projects { + var projectNode *ProjectNode + found := false + if projectNode, found = projectNodes[project]; !found { + ordered[index] = project + index++ + continue + } + if !dfs(projectNode, &ordered, &index) { + return nil + } + } + + return ordered +} + +func dfs(projectNode *ProjectNode, ordered *[]string, index *int) bool { + if projectNode.State == 2 { + return true + } + if projectNode.State == 1 { + return false + } + projectNode.State = 1 + for _, node := range projectNode.Dependencies { + + if ok := dfs(node, ordered, index); !ok { + return false + } + } + (*ordered)[*index] = projectNode.Name + *index++ + projectNode.State = 2 + return true + +} diff --git a/src/chapter4/problem7_test.go b/src/chapter4/problem7_test.go new file mode 100644 index 0000000..dc02be5 --- /dev/null +++ b/src/chapter4/problem7_test.go @@ -0,0 +1,40 @@ +package chapter4 + +import "testing" + +func TestOrderProjects(t *testing.T) { + + projects := []string{"a", "b", "c", "d", "e", "f"} + dependencies := [][]string{ + + {"a", "d"}, + {"f", "b"}, + {"b", "d"}, + {"f", "a"}, + {"d", "c"}, + } + + ordered := OrderProjects(projects, dependencies) + + t.Logf("result: %v", ordered) + +} + +func TestOrderProjectsDFS(t *testing.T) { + + projects := []string{"a", "b", "c", "d", "e", "f"} + dependencies := [][]string{ + + {"a", "d"}, + {"f", "b"}, + {"b", "d"}, + {"f", "a"}, + {"d", "c"}, + //{"e", "d"}, + } + + ordered := OrderProjectsDFS(projects, dependencies) + + t.Logf("result: %v", ordered) + +} diff --git a/src/chapter4/problem8.go b/src/chapter4/problem8.go new file mode 100644 index 0000000..12fbde6 --- /dev/null +++ b/src/chapter4/problem8.go @@ -0,0 +1,52 @@ +package chapter4 + +func FindFirstCommonAncestor(parent, current *BTNode, p, q int) *BTNode { + if current == nil { + return nil + } + + if current.Value == p { + if isAncestor(current.Left, q) || isAncestor(current.Right, q) { + return parent + } + return current + } + + if current.Value == q { + if isAncestor(current.Left, p) || isAncestor(current.Right, p) { + return parent + } + return current + } + + result := FindFirstCommonAncestor(current, current.Left, p, q) + if result == nil { + return FindFirstCommonAncestor(current, current.Right, p, q) + } + if result.Value == p && isAncestor(current.Right, q) { + + return current + + } + if result.Value == q && isAncestor(current.Right, p) { + return current + + } + return result + +} + +func isAncestor(node *BTNode, target int) bool { + if node == nil { + return false + } + if node.Value == target { + return true + } + foundInLeft := isAncestor(node.Left, target) + if foundInLeft { + return true + } + return isAncestor(node.Right, target) + +} diff --git a/src/chapter4/problem8_test.go b/src/chapter4/problem8_test.go new file mode 100644 index 0000000..1dbcfae --- /dev/null +++ b/src/chapter4/problem8_test.go @@ -0,0 +1,29 @@ +package chapter4 + +import "testing" + +func TestFindFirstCommonAncestor(t *testing.T) { + + root := &BTNode{ + Value: 3, + Left: &BTNode{Value: 1, + Left: &BTNode{Value: 7}, + Right: &BTNode{Value: 5}, + }, + Right: &BTNode{Value: 4, + Left: &BTNode{Value: 9}, + Right: &BTNode{Value: 8}, + }, + } + + result := FindFirstCommonAncestor(nil, root, 7, 5) + if result.Value != 1 { + t.Errorf("invalid result: %d", result.Value) + } + + result = FindFirstCommonAncestor(nil, root, 9, 5) + if result.Value != 3 { + t.Errorf("invalid result: %d", result.Value) + } + +}