Level Order Traversal Binary Tree Issue - ruby

Problem statement:
Given a binary tree, return the level order traversal of its nodes'
values. (ie, from left to right, level by level).
For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its level order traversal as:
[
[3],
[9,20],
[15,7]
]
I've solved this with BFS, which is the most intuitive way to do this. However, I tried solving it another way and I'm unable to. Below is sample input / correct output vs. my output:
Your input
[3,9,20,null,null,15,7]
Your answer
[[3],[[9],[],[20],[[15],[],[7],[]]]]
Expected answer
[[3],[9,20],[15,7]]
This is obviously because somewhere in the code [] is being returned, but here's my code:
def level_order(root)
return [] if root.nil?
arr = merge([level_order(root.left)], [level_order(root.right)]) #this returns an empty arr if both of those are nil..
arr.insert(0, [root.val])
end
def merge(arr1, arr2)
i = j = 0
while i < arr1.length && j < arr2.length
arr1[i] += arr2[j]
i += 1
j += 1
end
while j < arr2.length #check if any remaining elements in arr 2
arr1 << arr2[j]
j += 1
end
arr1
end
In the above, I dealt with [] case by doing += instead of << and the merge function works if one arr is empty. The idea here is that I'm merging each level of the level order traversal for both left and right sides, then inserting the root at the beginning of the array.
I also considered that the root could be inserted as an empty array, but this can't be happening because I have an initial return statement that's called if root is nil. Any ideas?

It should be as simple as changing this
arr = merge([level_order(root.left)], [level_order(root.right)])
To
arr = merge(level_order(root.left), level_order(root.right))
However I would have written this slightly differently:
input = [3,9,20,nil,nil,15,7]
output = []
start = 0
length = 1
while start < input.length do
output << input.slice(start, length).compact
start += length
length *= 2
end
puts output.inspect
This would avoid building a tree and would be more efficient than recursion.

Related

Find Leaves of Binary Tree

Working on following problem:
Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves, repeat until the tree is empty.
Example:
Given binary tree
1
/ \
2 3
/ \
4 5
Returns [4, 5, 3], [2], [1].
Explanation:
1. Removing the leaves [4, 5, 3] would result in this tree:
1
/
2
2. Now removing the leaf [2] would result in this tree:
1
3. Now removing the leaf [1] would result in the empty tree:
[]
Returns [4, 5, 3], [2], [1].
My idea was a simple recursive algorithm shown below. The idea is to find the leaves of the left subtree and the right subtree, and weave them such that the depths are in the right subarray. I've tested the 'weave' method pretty thoroughly, and I think it's fine. My concern is with my recursive implementation-- I'm getting an answer way off from the correct one, and not sure why.
Below is my code with sample input/output:
def find_leaves(root)
return [] if root.nil?
#create leaf_arr of root.left and root.right
#weave them in order.
#add the root
left_arr = find_leaves(root.left)
right_arr = find_leaves(root.right)
weave(left_arr, right_arr) << [root]
end
def weave(arr1, arr2) #these are 2d arrs
i = 0
until i == arr1.length || i == arr2.length #potential nil/empty case here
arr1[i] += arr2[i]
i += 1
end
if i < arr2.length
#either arr 1 or arr2 isn't finished. if arr1 isn't finished, we're done. if arr2 isnt finished, do the below:
until i == arr2.length
arr1 << arr2[i]
i += 1
end
end
arr1
end
Sample input/output/correct answer:
Run Code Result: ×
input: [1,2,3,4,5]
Your answer: [[[4],[5],[3]],[[2,4,5]],[[1,2,3,4,5]]]
Expected answer: [[4,5,3],[2],[1]]
I've printed the output for the left_arr and right_arr variables and they look fine, and I've stress-tested my weave algorithm. Am I off conceptually here?
I can't comment so I will do it like this. (do remember that i dont know ruby)
I think something goes already wrong in how the double arrays (root.left and root.right) are defined. How are they defined? how is root defined?
But the following eplains the repeat of the whole array.
weave(left_arr, right_arr) << [root]
This should be someting in the line of this.
weave(left_arr, right_arr) << [root.root]
Otherwise you are appending the whole root array wich is [1,2,3,4,5].
So this explains the adding of last part. [[[4],[5],[3]],[[2,4,5]],[[1,2,3,4,5]]].
My suggestion in finding the error in weave would be to print arr1 and arr2 at every stage....
Could you show that..
In your code you are using pure depth first search algorithm DFS and with that algorithm I think that you can hardly achieve your goal with array joggling you are doing in weave function. Because your tree will be processed in this order 4 , 5 , 2 , 3 , 1.
One solution will be to do it with iteration (pseudo code):
function doJob(root) begin
leaves = findLeaves(root)
while leaves.size > 0 do begin
for each leaf in leaves delete(leaf)
leaves = findLeaves(root)
end
delete(root)
end
function findLeaves(node) begin
if node = nil then begin
return []
end
else begin
leftLeaves = findLeaves(node.left)
rightLeaves = fingLeaves(node.right)
leaves = leftLeaves + rightLeaves
if leaves.size == 0 then begin
leaves.add(node)
end
return leaves
end
end
Since this still sits open and seems to fair highly when I google search your title. I'll show a pretty expressive solution:
def find_leaves(root)
return [] if root.nil?
return [[root.val]] if root.left.nil? && root.right.nil?
todo = [root]
leaves = []
until todo.empty?
top = todo.shift
%w[left right].each do |path|
leaf = top.send(path)
next if leaf.nil?
if leaf.left.nil? && leaf.right.nil?
leaves << leaf.val
top.instance_variable_set("##{path}", nil)
else
todo << leaf
end
end
end
[leaves].concat(find_leaves(root))
end
A more refactored version:
def find_leaves(root)
leaves = []
search = lambda do |branch|
return -1 unless branch
i = 1 + [search[branch.left], search[branch.right]].max
(leaves[i] ||= []) << branch.val
i
end
search[root]
leaves
end
They're both about the same speed, and really the first one is easier to read and understand.

Speeding up solution to algorithm

Working on the following algorithm:
Given an array of non-negative integers, you are initially positioned
at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
Below is my solution. It tries every single potential step, and then memoizes accordingly. So if the first element is three, the code takes three steps, two steps, and one step, and launches three separate functions from there. I then memoized with a hash. My issue is that the code works perfectly fine, but it's timing out for very large inputs. Memoizing helped, but only a little bit. Am I memoizing correctly or is backtracking the wrong approach here?
def can_jump(nums)
#memo = {}
avail?(nums, 0)
end
def avail?(nums, index)
return true if nums.nil? || nums.empty? || nums.length == 1 || index >= nums.length - 1
current = nums[index]
true_count = 0
until current == 0 #try every jump from the val to 1
#memo[index + current] ||= avail?(nums, index + current)
true_count +=1 if #memo[index + current] == true
current -= 1
end
true_count > 0
end
Here's a 𝑂(𝑛) algorithm:
Initialize 𝑚𝑎𝑥 to 0.
For each number 𝑛𝑖 in 𝑁:
If 𝑖 is greater than 𝑚𝑎𝑥, neither 𝑛𝑖 nor any subsequent number can be reached, so
return false.
If 𝑛𝑖+𝑖 is greater than 𝑚𝑎𝑥, set 𝑚𝑎𝑥 to 𝑛𝑖+𝑖.
If 𝑚𝑎𝑥 is greater than or equal to the last index in 𝑁
return true.
Otherwise return false.
Here's a Ruby implementation:
def can_jump(nums)
max_reach = 0
nums.each_with_index do |num, idx|
return false if idx > max_reach
max_reach = [idx+num, max_reach].max
end
max_reach >= nums.size - 1
end
p can_jump([2,3,1,1,4]) # => true
p can_jump([3,2,1,0,4]) # => false
See it on repl.it: https://repl.it/FvlV/1
Your code is O(n^2), but you can produce the result in O(n) time and O(1) space. The idea is to work backwards through the array keeping the minimum index found so far from which you can reach index n-1.
Something like this:
def can_jump(nums)
min_index = nums.length - 1
for i in (nums.length - 2).downto(0)
if nums[i] + i >= min_index
min_index = i
end
end
min_index == 0
end
print can_jump([2, 3, 1, 1, 4]), "\n"
print can_jump([3, 2, 1, 0, 4]), "\n"

How do I get a numerical value for all the characters that are repeated in a certain string?

def num_repeats(string)
letters = string.chars
idx = 0
n = 1
arr = []
lettercount = 0
while idx < letters.length
lettercount = 0
while n < letters.length
if letters[idx] == letters[n]
lettercount = 1
end
n+=1
end
if lettercount > 0
arr.push(idx)
end
idx += 1
end
return arr.length
end
puts(num_repeats("abdbccc"))
# == 2 since 2 letters are repeated across the string of characters
I keep getting zero, although as i see it if a number is repeated the value of numbercount should shift from zero to one and then allow some value to get pushed into the array where I later get the length of said array to determine the number of repeated characters. Is there an issue with my loops?
UPDATE
If you really want to use the same kind of code and algorithm to do that, then here are the problems of it :
In your second while loop the variable n is supposed to start from idx+1, considering you are trying to pick up an index and then find whether the character at that index is repeated somewhere after the index.
But even if you fix that you will get 3 for abdbccc. That kinda shows that your algorithm is wrong. When there are more than 2 occurrences of a repeated character, just like the process I said in the above para, you do that for every such character except for the last one, without checking whether the character had already been detected for repetition. Illustration :
str = 'aaa'
When idx = 0, you get str[idx] == str[n=1], adds it to the result.
When idx = 1, you get str[idx] == str[n=2], adds it to the result.
Now you counted a twice for repetition. I think you can fix that alone.
I think you are just trying to do the same as this (assumes you need to check lower case letters only) :
str = "abdbccc"
('a'..'z').count { |x| str.count(x) > 1 }
# => 2
Or if you need to check the number of repeated characters for any character :
str = "12233aabc"
str.chars.group_by(&:to_s).count do |k, v|
v.size > 1
end
# => 3
It's Ruby we are talking about. It's not really a good idea to write code like that in Ruby, I mean you are using a lot of while loops and manually tracking down their counters, while in Ruby you usually do not have to deal with those, considering all the convenient, less error-prone and shorter alternatives Ruby provides. I think you have a C like background, I recommend that you learn more of Ruby and Ruby way of doing things.
Didn't understood what you were trying to do, maybe you could use a hash to assist:
def num_repeats(string)
letters = string.chars
counter_hash = Hash.new(0)
letters.each { |l| counter_hash[l] += 1 }
counter_hash
end
You have this inner loop
while n < letters.length
if letters[idx] == letters[n]
lettercount = 1
end
n+=1
But nowhere are you resetting n, so after this loop has scanned once, it will skip past every subsequent time
You can mostly fix that by setting n to idx + 1 here
while idx < letters.length
lettercount = 0
n = idx + 1
while n < letters.length
You still will get a result of 3 because you are not detecting that c has already been counted
You can fix this final problem with a couple more tweaks
def num_repeats(string)
letters = string.chars
idx = 0
arr = []
lettercount = 0
while idx < letters.length
lettercount = 0
n = idx + 1 # <== start looking after idx char
while n < letters.length
if letters[idx] == letters[n]
lettercount += 1 # <== incrementing here
end
n+=1
end
if lettercount == 1 # <== check for exactly one
arr.push(idx)
end
idx += 1
end
return arr.length
end
This works because now lettercount == 2 for the first c so the duplicate is not counted until you get to the second c where lettercount == 1
This is still considered a poor solution as it has O(n**2) complexity. There are solutions - for example using Hash which are O(n)

implement shell sort by ruby

I try to implement shell sort by ruby.
def shell_sort(list)
d = list.length
return -1 if d == 0
(0...list.length).each do |i|
d = d / 2
puts "d:#{d}"
(0...(list.length-d)).each do |j|
if list[j] >= list[j+d]
list[j], list[j+d] = list[j+d], list[j]
end
end
puts list.inspect
break if d == 1
end
list
end
puts shell_sort([10,9,8,7,6,5,4,3,2,1]).inspect
but the result is incorrect.
=>[2, 1, 3, 4, 5, 7, 6, 8, 9, 10]
I don't know where going wrong, hope someone can help me. Thanks in advance!
I referenced Shell Sort in here : Shell Sort - Wikepedia, and from that I have understood your algorithm is wrong. Iteration of gap sequence is alright, I mean you iterate only upto d/2 == 1.
But for a gap, let's say 2, you simply iterate from 0 to list.length-2 and swap every j and j+2 elements if list[j] is greater than list[j+2]. That isn't even a proper insertion sort, and Shell Sort requires Insertion sorts on gaps. Also Shell Sort requires that after you do an x gap sort, every xth element, starting from anywhere will be sorted (see the example run on the link and you can verify yourself).
A case where it can wrong in a 2 gap sort pass :
list = 5,4,3,2,1
j = 0 passed :
list = 3,4,5,2,1
j = 1 passed :
list = 3,2,5,4,1
j = 2 passed
list = 3,2,1,4,5
After it completes, you can see that every 2nd element starting from 0 isn't in a sorted order. I suggest that you learn Insertion Sort first, then understand where and how it is used in Shell Sort, and try again, if you want to do it by yourself.
Anyway, I have written one (save it for later if you want) taking your method as a base, with a lot of comments. Hope you get the idea through this. Also tried to make the outputs clarify the how the algorithm works.
def shell_sort(list)
d = list.length
return -1 if d == 0
# You select and iterate over your gap sequence here.
until d/2 == 0 do
d = d / 2
# Now you pick up an index i, and make sure every dth element,
# starting from i is sorted.
# i = 0
# while i < list.length do
0.step(list.length) do |i|
# Okay we picked up index i. Now it's just plain insertion sort.
# Only difference is that we take elements with constant gap,
# rather than taking them up serially.
# igap = i + d
# while igap < list.length do
(i+d).step(list.length-1, d) do |igap|
# Just like insertion sort, we take up the last most value.
# So that we can shift values greater than list[igap] to its side,
# and assign it to a proper position we find for it later.
temp = list[igap]
j = igap
while j >= i do
break if list[j] >= list[j - d]
list[j] = list[j-d]
j -= d
end
# Okay this is where it belongs.
list[j] = temp
#igap += d
end
# i += 1
end
puts "#{d} sort done, the list now : "
puts list.inspect
end
list
end
list = [10,9,8,7,6,5,4,3,2,1]
puts "List before sort : "
puts list.inspect
shell_sort(list)
puts "Sorted list : "
puts list.inspect
I think your algorithm needs a little tweaking.
The reason it fails is simply because on the last run (when d == 1) the smallest element (1) isn't near enough the first element to swap it in in one go.
The easiest way to make it work is to "restart" your inner loop whenever elements switch places. So, a little bit rough solution would be something like
(0...(list.length-d)).each do |j|
if list[j] >= list[j+d]
list[j], list[j+d] = list[j+d], list[j]
d *= 2
break
end
end
This solution is of course far from optimal, but should achieve required results with as little code as possible.
You should just do a last run on array. To simplify your code I extracted exchange part into standalone fucntion so you could see now where you should do this:
def exchange e, list
(0...(list.length-e)).each do |j|
if list[j] >= list[j+e]
list[j], list[j+e] = list[j+e], list[j]
end
end
end
def shell_sort(list)
d = list.length
return -1 if d == 0
(0...list.length).each do |i|
d = d / 2
puts "d:#{d}"
exchange(d, list)
puts list.inspect
if d == 1
exchange(d, list)
break
end
end
list
end
arr = [10,9,8,7,6,5,4,3,2,1]
p shell_sort(arr)
Result:
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Merging two sorted arrays - why does this NOT work?

I am learning ruby and was given the following assignment:
given two sorted arrays like the following we must merge them into one sorted array.
array_1 = [5,8,9,11]
array_2 = [4,6,7,10]
merge(array_1, array_2)
=> [4,5,6,7,8,9,10,11]
Given this brief description, implement the merge method that takes two arrays and returns
the properly sorted array containing the items from each array.
I wrote this answer:
def merge(arr1, arr2)
i = 0
k = 0
merged_arr = []
until k = arr2.count
while arr1[i] <= arr2[k]
merged_arr.push(arr1[i])
i += 1
end
merged_arr.push(arr2[k])
k += 1
end
merged_arr
end
My instructor sent out a solution, which I understand, however I don't understand why my answer does NOT work. Can someone please explain the faulty logic? Thank you!
Here is the (correct) solution:
def merge(array_1, array_2)
i = 0
k = 0
merged_array = []
while i < array_1.count
while k < array_2.count && array_1[i] > array_2[k]
merged_array << array_2[k]
k += 1
end
merged_array << array_1[i]
i += 1
end
print merged_array.inspect
end
k = arr2.count assigns the value of arr2.count to k and evaluates to k, so until k = arr2.count is never executed.
you also need to consider the unequal length of arr1 and arr2, your instructor's solution was only right if arr1.length >= arr2.length, but if arr1.length < arr2.length, then the elements from the extra length was lost in the solution.

Resources