I have submitted the below solution for the question in golang and it fails for the base case. I am not able to figure out why it is failing.
var answer[][]int
func hasPathSum(root *TreeNode, sum int, path []int){
if root == nil {
return
}
newPath := append(path, root.Val)
sum = sum - root.Val
if root.Left == nil && root.Right == nil && sum == 0 {
answer = append(answer, newPath)
fmt.Println(answer)
return
}
if root.Left != nil {
hasPathSum(root.Left, sum, newPath)
}
if root.Right != nil {
hasPathSum(root.Right, sum, newPath)
}
}
func pathSum(root *TreeNode, sum int) [][]int {
var path []int
answer = [][]int{}
hasPathSum(root, sum, path)
return answer
}
And when I avoid declaring newPath it passes the base check. Like this:
var answer[][]int
func hasPathSum(root *TreeNode, sum int, path []int){
if root == nil {
return
}
sum = sum - root.Val
if root.Left == nil && root.Right == nil && sum == 0 {
answer = append(answer, append(path, root.Val))
fmt.Println(answer)
return
}
if root.Left != nil {
hasPathSum(root.Left, sum, append(path, root.Val))
}
if root.Right != nil {
hasPathSum(root.Right, sum, append(path, root.Val))
}
}
func pathSum(root *TreeNode, sum int) [][]int {
var path []int
answer = [][]int{}
hasPathSum(root, sum, path)
return answer
}
I am not able to figure out what is the difference between the two solutions. Both solutions are the same from the recursion point of view. Furthermore, a similar solution in C++ passes all the checks.
The problem occurs because slices refer to an underlying array, and append does not re-allocate a new array if there's space. See https://blog.golang.org/slices-intro
That means when you append(path, root.Val), the new slice will sometimes share the backing array with path. That can be a problem, for example:
if root.Left != nil {
hasPathSum(root.Left, sum, append(path, root.Val))
}
if root.Right != nil {
hasPathSum(root.Right, sum, append(path, root.Val))
}
Here, both branches may be using the same backing array. That's a problem, because when executing the first hasPathSum it may add a slice to answer, but the underlying array of that slice may still be used in the second call to hasPathSum, which will change its contents.
Both versions of the code have the same problem, but because the second is less likely to cause problems because if append(path, root.Val) needs to reallocate, then you get two different copies in the two branches of hasPathSum. This means the bug will happen at a lower frequency.
I'm not sure about fmt.Println(answer), you'd probably just need to return answer.
This'll pass:
func pathSum(root *TreeNode, sum int) [][]int {
var paths [][]int
if root == nil {
return paths
}
if root.Left == nil && root.Right == nil {
if sum == root.Val {
return append(paths, []int{ root.Val })
}
return paths
}
for _, path := range pathSum(root.Left, sum - root.Val) {
paths = append(paths, append([]int{ root.Val}, path... ))
}
for _, path := range pathSum(root.Right, sum - root.Val) {
paths = append(paths, append([]int{ root.Val}, path... ))
}
return paths
}
Here is LeetCode's Java solution with comments:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private void recurseTree(TreeNode node, int remainingSum, List<Integer> pathNodes, List<List<Integer>> pathsList) {
if (node == null) {
return;
}
// Add the current node to the path's list
pathNodes.add(node.val);
// Check if the current node is a leaf and also, if it
// equals our remaining sum. If it does, we add the path to
// our list of paths
if (remainingSum == node.val && node.left == null && node.right == null) {
pathsList.add(new ArrayList<>(pathNodes));
} else {
// Else, we will recurse on the left and the right children
this.recurseTree(node.left, remainingSum - node.val, pathNodes, pathsList);
this.recurseTree(node.right, remainingSum - node.val, pathNodes, pathsList);
}
// We need to pop the node once we are done processing ALL of it's
// subtrees.
pathNodes.remove(pathNodes.size() - 1);
}
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> pathsList = new ArrayList<List<Integer>>();
List<Integer> pathNodes = new ArrayList<Integer>();
this.recurseTree(root, sum, pathNodes, pathsList);
return pathsList;
}
}
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
Related
I was writing a binary search tree traversal when I came across a problem, and then a slight syntax change fixed it but I don't understand why it wasn't working in the first palce. The two code examples I provide I would expect to run the exact same way but they don't.
One sets the curr variable to it's left node curr.left, then recursively calls the InOrderRecursive, while the other calls InOrderRecursive directly on curr.left itself.
type BST struct {
value int
left *BST
right *BST
}
Does not work (This does return but with the wrong values):
func (tree *BST) InOrderRecursive(values []int) []int {
curr := tree
if curr.left != nil {
curr = curr.left
values = curr.InOrderRecursive(values)
}
values = append(values, curr.value)
if curr.right != nil {
curr = curr.right
values = curr.InOrderRecursive(values)
}
return values
}
Works (returns the correct values):
func (tree *BST) InOrderRecursive(values []int) []int {
curr := tree
if curr.left != nil {
values = curr.left.InOrderRecursive(values)
}
values = append(values, curr.value)
if curr.right != nil {
values = curr.right.InOrderRecursive(values)
}
return values
}
Could someone please explain the difference in these two code examples and the reason for the different behavior?
The first version has a slight bug. If curr.left is not nil in version 1, then values = append(values, curr.value) will append the left child node's value to the list, not the current node, since curr is now equal to curr.left outside of the if scope. More specifically,
func (tree *BST) InOrderRecursive(values []int) []int {
curr := tree
if curr.left != nil {
curr = curr.left
values = curr.InOrderRecursive(values)
}
// curr here will take on the node's left child value if
// it's not nil (bug).
values = append(values, curr.value)
// That issue will cascade to here as well (if the OG curr.left
// != nil), we're now checking the left child node's right child.
if curr.right != nil {
curr = curr.right
values = curr.InOrderRecursive(values)
}
// The result of the right recursive call is not appended to the
// `values` list. (bug)
return values
}
I wrote a code for All Elements in Two Binary Search Trees problem on leetcode:
https://leetcode.com/problems/all-elements-in-two-binary-search-trees/
I couldn't find anyone else trying to solve it this way. I know that this code could be improved with helper() function, but the main problem is sorting, is there some elegant way to avoid it while walking both trees at the same time? See line with sort.Ints(curr).
What I'm looking for is a way to walk 2 trees at the same time and fill the answer array directly in sorted order, without additional arrays.
func getAllElements(root1 *TreeNode, root2 *TreeNode) []int {
if root1 == nil && root2 == nil {
return []int{}
} else if root1 != nil && root2 != nil {
curr := getAllElements(root1.Left, root2.Left)
if root1.Val < root2.Val {
curr = append(curr, root1.Val)
curr = append(curr, root2.Val)
} else {
curr = append(curr, root2.Val)
curr = append(curr, root1.Val)
}
curr = append(curr, getAllElements(root1.Right, root2.Right)...)
sort.Ints(curr) //no TLE, but how to walk both trees at the same time, like in this code, to avoid this sorting?
return curr
} else if root1 != nil {
curr := getAllElements(root1.Left, nil)
curr = append(curr, root1.Val)
curr = append(curr, getAllElements(root1.Right, nil)...)
return curr
} else if root2 != nil {
curr := getAllElements(nil, root2.Left)
curr = append(curr, root2.Val)
curr = append(curr, getAllElements(nil, root2.Right)...)
return curr
}
return []int{}
}
This is a solution for 1305. All Elements in Two Binary Search Trees
func getAllElements(root1 *TreeNode, root2 *TreeNode) []int {
if root1 == nil && root2 == nil {
return nil
}
var list1 []int
if root1 != nil {
list1 = inOrder(root1, list1)
}
var list2 []int
if root2 != nil {
list2 = inOrder(root2, list2)
}
if len(list1) == 0 {
return list2
}
if len(list2) == 0 {
return list1
}
var list3 []int
for i, j := 0, 0;; {
var elem1 int
if len(list1) > i {
elem1 = list1[i]
} else {
// we add the remaining elements from list2 into list3
// there are no elements in list1
if len(list2) > j {
list3 = append(list3, list2[j:]...)
break
}
}
var elem2 int
if len(list2) > j {
elem2 = list2[j]
} else {
// we add the remaining elements from list1 into list3
// there are no elements in list2
if len(list1) > i {
list3 = append(list3, list1[i:]...)
break
}
}
if elem1 < elem2 {
list3 = append(list3, elem1)
i++
} else {
list3 = append(list3, elem2)
j++
}
}
return list3
}
func inOrder(node *TreeNode, list []int) []int {
if node.Left != nil {
list = inOrder(node.Left, list)
}
list = append(list, node.Val)
if node.Right != nil {
return inOrder(node.Right, list)
}
return list
}
Why would you need to do any sorting at all? By definition, binary trees are ordered. All you have to do is
Walk each tree, producing a list containing that tree's contents.
You now have two ordered lists.
Merge them to produce a single, ordered list.
Let us assume we have a tree struct, thus:
type tree *struct {
left tree
right tree
payload int
}
Walking such a binary tree to produce an ordered list is trivial:
func treeToSlice(root tree) []int {
arr := make([]int, 0)
var visit func(tree)
visit = func(curr tree) {
if curr.left != nil {
visit(curr.left)
}
arr = append(arr, curr.payload)
if curr.right != nil {
visit(curr.right)
}
}
visit(root)
return arr
}
Merging two such ordered lists is likewise trivial:
func merge( a []int, b []int ) []int {
c := make([]int, len(a)+len(b)) // merged list to be returned
i := 0 // current element of A
j := 0 // current element of B
k := 0 // current element of C
// while we have both A and B...
for ; i < len(a) && j < len(b) ; k++ {
if a[i] <= b[j] {
c[k] = a[i]
i++
} else {
c[k] = b[j]
j++
}
}
// if we have any As left....
for ; i < len(a) ; k++ {
c[k] = a[i]
i++
}
// if we have any Bs left...
for ; j < len(b) ; k++ {
c[k] = b[j]
j++
}
return c
}
And with those, converting two binary trees into a single ordered list is... triviality itself:
func twoBTreesToOrderedSlice( t1 tree, t2 tree ) []int {
return merge(
treeToSlice( t1 ),
treeToSlice( t2 ),
)
}
While you could use goroutines and channels to walk the trees in parallel, but the code is no more "elegant", and due to channel overhead and the cost of synchronization, it's virtually guaranteed to be less performant than the simpler "walk each tree to build ordered lists and then merge the two ordered lists.
I suppose, though, it probably saves a little space.
type tree *struct {
left tree
right tree
payload int
}
func walkTree( root tree, ch chan int ) {
var visit func(tree)
visit = func(node tree) {
if node != nil {
visit(node.left)
ch <- node.payload
visit(node.right)
}
}
visit(root)
close(ch)
}
func btreesToOrderedSlice( t1 tree, t2 tree ) []int {
arr := make([]int, 0)
ch1 := make(chan int)
ch2 := make(chan int)
go walkTree(t1, ch1)
go walkTree(t2, ch2)
x, haveX := <-ch1
y, haveY := <-ch2
for haveX && haveY {
if x <= y {
arr = append(arr, x)
x, haveX = <-ch1
} else {
arr = append(arr, y)
y, haveY = <-ch2
}
}
for x = range ch1 {
arr = append(arr, x)
}
for y = range ch2 {
arr = append(arr, y)
}
return arr
}
I am trying to solve Leaf-Similar Trees problem on leetcode.com with O(1) space without concurrency/goroutines.
Iterative solution needs a stack for storing nodes, and a recursive one needs memory for function calls.
However, with the help of the Morris Traversal algorithm, I was able to traverse each tree with O(1) memory (which was tricky btw, as it is non-trivial to detect leafs with this algorithm).
Now I have a problem of how to traverse both trees and compare that 1) they have the same number of leafs and 2) in the same order.
The obvious way is to store both sequences of leafs in arrays/slices and then compare those. However, it ruins the O(1) memory. I can also store the first tree sequence in an array, and yield the values from the second tree one by one, comparing them to the values in an array, but it still violates O(1).
So I cheated by using goroutines to send the values from both Morris traversals, effectively creating no intermediate arrays and achieving O(1) space.
Now I am trying to make this solution more generic by not using goroutines or any other concurrency techniques. It must be a sequential solution.
Alternatively, I have suspicion that it is not possible to make this work without concurrency, please confirm if it is true.
Here is my solution:
func leafSimilar(root1 *TreeNode, root2 *TreeNode) bool {
getLeafs := func(node *TreeNode, ch chan int) {
var pre *TreeNode // predecessor of the current node
curr := node
for curr != nil {
if curr.Left == nil {
if curr.Right == nil {
ch <- curr.Val
}
curr = curr.Right
continue
}
// curr.Right != nil
pre = curr.Left
for pre.Right != nil && pre.Right != curr {
pre = pre.Right
}
if pre.Right == nil {
pre.Right = curr
curr = curr.Left
continue
}
// pre.Right == curr
pre.Right = nil
if pre.Left == nil { // tricky! not using curr, but pre
ch <- pre.Val
}
curr = curr.Right
}
close(ch)
}
ch1 := make(chan int)
ch2 := make(chan int)
go getLeafs(root1, ch1)
go getLeafs(root2, ch2)
similar := true
for {
val1, ok1 := <-ch1
val2, ok2 := <-ch2
if val1 != val2 || ok1 != ok2 {
similar = false
}
if ok1 == false && ok2 == false {
break
}
}
return similar
}
I think it can work without concurrency, and still fit within your criteria. It's not pretty but it seems to work...
func leafSimilarNoConcurrency(root1 *TreeNode, root2 *TreeNode) bool {
if root1 == root2 {
return true
}
getLeafs := func(curr *TreeNode, pre *TreeNode) (int, *TreeNode, *TreeNode, bool) {
for curr != nil {
if curr.Left == nil {
if curr.Right == nil {
//ch <- curr.Val
return curr.Val, curr.Right, pre, true
}
curr = curr.Right
continue
}
// curr.Right != nil
pre = curr.Left
for pre.Right != nil && pre.Right != curr {
pre = pre.Right
}
if pre.Right == nil {
pre.Right = curr
curr = curr.Left
continue
}
// pre.Right == curr
pre.Right = nil
if pre.Left == nil { // tricky! not using curr, but pre
// ch <- pre.Val
return pre.Val, curr.Right, pre, true
}
curr = curr.Right
}
return 0, nil, nil, false
}
var val1, val2 int
var pre1, pre2 *TreeNode
var ok1, ok2 bool
curr1, curr2 := root1, root2
similar := true
for {
val1, curr1, pre1, ok1 = getLeafs(curr1, pre1)
val2, curr2, pre2, ok2 = getLeafs(curr2, pre2)
if val1 != val2 || ok1 != ok2 {
similar = false
}
if ok1 == false && ok2 == false {
break
}
}
return similar
}
Performing a little benchmarking, it may even be faster (at least for the example1 test case)...
goos: windows
goarch: amd64
BenchmarkLeafSimilar-4 244850 5509 ns/op 192 B/op 2 allocs/op
BenchmarkLeafSimilarNoConcurrency-4 13185218 92.9 ns/op 0 B/op 0 allocs/op
PASS
ok _/d_/Build/morristree 2.893s
I've put together an example which uses the deprecated testing.Main in the playground here, to demonstrate that it works the same for the 5 examples.
https://play.golang.org/p/hIk4zM3qpsT
I need to minimize the memory consumption and the execution time of a function that calculates the Range Sum of Binary Search Tree (https://leetcode.com/problems/range-sum-of-bst/).
The current results I am having are:
Runtime: 88 ms, faster than 69.00% of Go online submissions for Range Sum of BST.
Memory Usage: 7.9 MB, less than 5.67% of Go online submissions for Range Sum of BST.
My current code:
func rangeSumBST(root *TreeNode, min int, max int) int {
sum := 0
arr := []*TreeNode{root}
var node *TreeNode
for len(arr) > 0 {
node = arr[len(arr)-1]
arr = arr[:len(arr)-1]
if node.Val >= min && node.Val <= max {
sum += node.Val
}
if node.Left != nil && node.Val > min {
arr = append(arr, node.Left)
}
if node.Right != nil && node.Val < max {
arr = append(arr, node.Right)
}
}
return sum
}
I tried to solve the problem recursively, which was elegant but of course slower and more memory hungry than iterative solution.
The iterative solution I have is as lean and simple as I possibly can make it. I declare and reuse the node variable instead of declaring it inside of the for loop. I add nodes to the end of the slice instead of beginning.
What else can I do to make it faster and use less memory? Or is there a more efficient algorithm? Or is it that Leetcode measuring the execution time and memory consumption somehow incorrectly?
Since it is a BST you can do in O(1) space complexity using Inorder Morris traversal for BST, you cannot do better than O(N) time complexity for single queries unless you have some kind of preprocessing in the tree itself. Your current implementation is using a stack so your current space complexity is O(N) in worst case, when tree is basically a path.
Implementation in Go (was able to beat 99%):
func rangeSumBST(root *TreeNode, min int, max int) int {
if root == nil {
return 0
}
var pre *TreeNode
curr := root
sum := 0
for curr != nil {
if curr.Left == nil {
if curr.Val >= min && curr.Val <= max {
sum += curr.Val
}
if curr.Val > max {
break
}
curr = curr.Right
} else {
pre = curr.Left
for pre.Right != nil && pre.Right != curr {
pre = pre.Right
}
if pre.Right == nil {
pre.Right = curr
curr = curr.Left
} else {
pre.Right = nil
if curr.Val >= min && curr.Val <= max {
sum += curr.Val
}
if curr.Val > max {
break
}
curr = curr.Right
}
}
}
return sum
}
Time complexity: O(Number of nodes)
Space complexity: O(1)
Note: Somehow it doesn't show any improvement in memory performance, maybe because the tests are not enough and leetcode is known to show old statistics of previously submitted solutions when tests were not too big.
I am trying to realize a simple full text search in golang but all my implementations turn out to be too slow to overcome the thresholds.
The task is as follows:
Documents are non-empty strings of lowercase words divided by spaces
Each document has an implicit identifier equal to its index in the input array
New() constructs the index
Search(): accepts a query, which is also a string of lowercase words divided by spaces, and returns a sorted array of unique identifiers of documents that contains all words from the query regardless of their order
Example:
index := New([]string{
"this is the house that jack built", //: 0
"this is the rat that ate the malt", //: 1
})
index.Search("") // -> []
index.Search("in the house that jack built") // -> []
index.Search("malt rat") // -> [1]
index.Search("is this the") // -> [0, 1]
I have already tried to implement:
a binary search tree for each document and for all documents all together
a trie (prefix tree) for each document and for all documents all together
inverted index search
binary search tree (for all documents):
type Tree struct {
m map[int]bool
word string
left *Tree
right *Tree
}
type Index struct {
tree *Tree
}
binary search tree (a tree for each document):
type Tree struct {
word string
left *Tree
right *Tree
}
type Index struct {
tree *Tree
index int
next *Index
}
trie (for all documents):
type Trie struct {
m map[uint8]*Trie
end_node map[int]bool
}
type Index struct {
trie *Trie
}
trie (for each document):
type Trie struct {
m map[uint8]*Trie
end_node bool
}
type Index struct {
trie *Trie
index int
next *Index
}
inverted index:
type Index struct {
m map[string]map[int]bool
}
New and Search implementation for inverted index:
// New creates a fulltext search index for the given documents
func New(docs []string) *Index {
m := make(map[string]map[int]bool)
for i := 0; i < len(docs); i++ {
words := strings.Fields(docs[i])
for j := 0; j < len(words); j++ {
if m[words[j]] == nil {
m[words[j]] = make(map[int]bool)
}
m[words[j]][i+1] = true
}
}
return &(Index{m})
}
// Search returns a slice of unique ids of documents that contain all words from the query.
func (idx *Index) Search(query string) []int {
if query == "" {
return []int{}
}
ret := make(map[int]bool)
arr := strings.Fields(query)
fl := 0
for i := range arr {
if idx.m[arr[i]] == nil {
return []int{}
}
if fl == 0 {
for value := range idx.m[arr[i]] {
ret[value] = true
}
fl = 1
} else {
tmp := make(map[int]bool)
for value := range ret {
if idx.m[arr[i]][value] == true {
tmp[value] = true
}
}
ret = tmp
}
}
ret_arr := []int{}
for value := range ret {
ret_arr = append(ret_arr, value-1)
}
sort.Ints(ret_arr)
return ret_arr
}
Am I doing something wrong or is there a better algorithm for search in golang?
Any help is appreciated.
I can't really help you for the language specific part, but if it's of any help, here is a pseudocode that describes a Trie implementation along with a function to solve your current problem in a decently efficient manner.
struct TrieNode{
map[char] children // maps character to children
set[int] contains // set of all ids of documents that contain the word
}
// classic search function in trie, except it returns a set of document ids instead of a simple boolean
function get_doc_ids(TrieNode node, string w, int depth){
if (depth == length(w)){
return node.contains
} else {
if (node.hasChild(w[depth]) {
return get_doc_ids(node.getChild(w[depth], w, depth+1)
} else {
return empty_set()
}
}
}
// the answering query function, as straight forward as it can be
function answer_query(TrieNode root, list_of_words L){
n = length(L)
result = get_docs_ids(root, L[0], 0)
for i from 1 to n-1 do {
result = intersection(result, get_docs_ids(root, L[i], 0)) // set intersection
if (result.is_empty()){
break // no documents contains them all, no need to check further
}
}
return result
}