Can anybody please tell what am I doing wrong in my Merge-Sort
def print_array(a)
print a
print "\n\n"
end
#Merge-Sort code starts Here.
def merge_sort(a)
if a.size < 2
return a
end
middle = (a.length / 2).to_i
left = a.slice(0, middle)
right = a.slice(middle, a.size)
merge_sort(left)
merge_sort(right)
a = merge(left, right)
end
def merge(left, right)
result = []
while left.length > 0 || right.length > 0
if left.length > 0 && right.length > 0
if left[0] <= right[0]
result << left.slice!(0)
else
result << right.slice!(0)
end
elsif left.length > 0
result.concat left.slice!(0..left.length-1)
elsif right.length > 0
result.concat right.slice!(0..right.length-1)
end
end
result
end
a = [ 3, 2, 1 ]
print_array(a)
a = merge_sort(a)
print_array(a)
Basically, What I know is that when it comes time to Merge [2] and [1], that happens correctly (ie. [1, 2] is returned) , but for the previous step of the Recursion as it unwinds, it comes back (or doesn't come back) as [1, 2] but as [2, 1], ie.
Previous step
-> left = [3]
-> right = [2, 1] #This is the value of right from recursion instead of [1, 2]
I don't know how to fix this, and it's driving me crazy, Can you please help me?
These two lines:
merge_sort(left)
merge_sort(right)
should be:
left = merge_sort(left)
right = merge_sort(right)
Since your merge sort is not sorting in place, you need to assign the result back to the variables.
Related
I am learning ruby and have started practicing problems from leetcode, yesterday I have a problem which I am not able to solve since yesterday.
I tried hard doing that in ruby, but not able to do yet.
I tried this
def give_chair(a)
u = a.uniq
d = []
u.each do |i|
d << i if a.count(i) == 1
end
d
end
def smallest_chair(times, target_friend)
friend = times[target_friend]
sorted_arrival_times = times.sort
leave_time_chair = {}
chair = 0
chairs_array = []
uniq_chars_array = []
sorted_arrival_times.each do |i|
if leave_time_chair.keys.select { |k| i[0] > k }.empty?
leave_time_chair[i[1]] = chair
chair+=1
else
all_keys = leave_time_chair.keys.select { |k| k <= i[0] }
chairs_array = leave_time_chair.values
p chairs_array
if give_chair(chairs_array).empty?
leave_time_chair[i[1]] = chairs_array.sort.first
else
leave_time_chair[i[1]] = give_chair(chairs_array).sort.first
end
end
if i == friend
p leave_time_chair
return leave_time_chair[i[1]]
end
end
end
# a = [[33889,98676],[80071,89737],[44118,52565],[52992,84310],[78492,88209],[21695,67063],[84622,95452],[98048,98856],[98411,99433],[55333,56548],[65375,88566],[55011,62821],[48548,48656],[87396,94825],[55273,81868],[75629,91467]]
# b = 6
# p smallest_chair(a, b)
but it is failing for some test cases.
I am not able to create an algorithm for it.
Question = https://leetcode.com/problems/the-number-of-the-smallest-unoccupied-chair
My approach:
First I sort the times array according to arrival times.
Then I iterate over each array element
Now if the arrival time is greater than all the previous leaving time (I am creating key, value pair of leaving time and chair given) then I add a new key=> value pair in leave_time_chair (which is hash) and where key is the leaving time of current array and value is the chair given to it.
Then I increment the chair (chair+=1)
Else I get all those leaving time which are equal or less than the current arrival time (all_keys = leave_time_chair.keys.select { |k| k <= i[0] })
Then I get all the chairs of those times
Now I have all the chairs like this => [0, 0, 1, 2] so I wrote one function [ give_chair(a) ] which gives me those elements which are not repeated. like this => [1, 2] and then I assign the shortest number (chair) to the leaving time of current array. and so on...
Then if my current array is equal to the friend I return the chair of it. by extracting it from a hash (leave_time_chair) return leave_time_chair[i[1]]
my naive solution (not optimize yet), basically my idea that i flat-map the input array into an array with each element is a pair [time arrive/leave, friend index], then i will sort that array base on time (don't care arrive or leave), if both pair have same time, then i'll compare the arrive time of fiend index. Finally i loop through the sorted array and evaluate minimum free chair index each step, whenever i meet the targetFriend i return that minimum free chair index.
# #param {Integer[][]} times
# #param {Integer} target_friend
# #return {Integer}
def smallest_chair(times, target_friend)
# times = [[1,2],[4,7],[2,4]]
# targetFriend = 1
sit_times = times.each_with_index.inject([]) { |combi, (time, index)|
combi += [[time.first, index], [time.last, index]]
}
# [[1, 0], [2, 0], [4, 1], [7, 1], [2, 2], [4, 2]]
sit_times.sort! {|x, y|
c = x[0] <=> y[0]
# [[1, 0], [2, 0], [2, 2], [4, 1], [4, 2], [7, 1]]
c = times[x[1]][0] <=> times[y[1]][0] if c == 0
# [[1, 0], [2, 0], [2, 2], [4, 2], [4, 1], [7, 1]]
c
}
chairs = {} # to mark time of friend
occupied = Array.new(times.size, 0) # occupied chair: 1, otherwise: 0
min_free = 0 # current minimum not occupied chair
sit_times.each do |time, friend_index|
if target_friend == friend_index # check
return min_free
end
sit = chairs[friend_index]
if sit # leave
occupied[sit] = 0
chairs[friend_index] = nil
min_free = sit if min_free > sit
else # arrive
chairs[friend_index] = min_free
occupied[min_free] = 1
min_free += 1 until occupied[min_free] == 0 # re-calculate
end
end
end
Note: the code pass test cases on leetcode but the performance is not good.
update
here is the better version, using 3 priority queues, one for arrive times, one for leave times and the last for chair.
PriorityQueue class
class PriorityQueue
attr_reader :length
def initialize(opts={}, &comparator)
order_opt = opts.fetch(:order, :asc)
#order = order_opt == :asc ? -1 : 1
#comparator = comparator
#items = [nil]
#length = 0
end
def push(item)
#items << item
#length += 1
swim(#length)
true
end
def pop
return nil if empty?
swap(1, #length) if #length > 1
#length -= 1
sink(1) if #length > 0
#items.pop
end
def empty?
#length == 0
end
def swap(i, j)
temp = #items[i]
#items[i] = #items[j]
#items[j] = temp
end
def in_order?(i, j)
x = #items[i]
y = #items[j]
order = #comparator.nil? ? (x <=> y) : #comparator.call(x, y)
order == #order
end
def swim(from)
while (up = from / 2) >= 1
break if in_order?(up, from)
swap(up, from)
from = up
end
end
def sink(from)
while (down = from * 2) <= #length
down += 1 if down < #length && in_order?(down + 1, down)
break if in_order?(from, down)
swap(down, from)
from = down
end
end
end
smallest_chair with priority queues (note that i found using sort is faster than a queue for arrive times, but basically the idea is same)
def smallest_chair_pq(times, target_friend)
# a_pq = PriorityQueue.new { |x, y|
# x[0] <=> y[0]
# }
#
# times.each do |t|
# a_pq.push(t)
# end
# sort arrive times is faster than a priority queue
a_pq = times.sort_by(&:first).reverse
# leave times queue
l_pq = PriorityQueue.new { |x, y|
c = x[0] <=> y[0]
c = x[1] <=> y[1] if c == 0
c
}
# chair-indexes queue
# consider case a friend come in at arrive-time at1
# and there's a range chairs with leave times in range lm <= at1 <= ln
# that mean that friend could pick one of those chairs
# and according this problem requirement, should pick the minimun chair index
c_pq = PriorityQueue.new
target_time = times[target_friend][0]
last_chair_index = 0
until a_pq.empty?
a_top = a_pq.pop
arrive_time = a_top.first
if l_pq.empty?
return 0 if arrive_time == target_time
l_pq.push([a_top.last, 0])
else
l_top = l_pq.pop
if l_top.first <= arrive_time
c_pq.push(l_top.last)
until (l_ntop = l_pq.pop).nil? || arrive_time < l_ntop.first
c_pq.push(l_ntop.last)
end
l_pq.push(l_ntop) unless l_ntop.nil?
min_chair_index = c_pq.pop
return min_chair_index if arrive_time == target_time
l_pq.push([a_top.last, min_chair_index])
else
unless c_pq.empty?
chair_index = c_pq.pop
return chair_index if arrive_time == target_time
l_pq.push([a_top.last, chair_index])
else
last_chair_index += 1
return last_chair_index if arrive_time == target_time
l_pq.push([a_top.last, last_chair_index])
end
l_pq.push(l_top)
end
end
end
end
I see that there are better ruby bubble sort codes already posted in places such as here:
Using the Bubble sort method for an array in Ruby
But I am having trouble debugging my current code and would appreciate some help with figuring out why my code does not work. Thanks.
def bubble_sort(arr)
original = arr
x = 0
while x < arr.count - 1
if arr[x] < arr[x + 1]
y = arr[x + 1]
arr[x + 1] = arr[x]
arr[x] = y
end
x += 1
end
if original == arr
return arr
else
return bubble_sort(arr)
end
end
One of the problems is this:
original = arr
You expect original to a copy of arr in its current state, right? Well, no. They will point to the same array. That's why your function will never recurse here:
if original == arr
return arr
else
return bubble_sort[arr]
end
To copy the array, use dup
original = arr.dup
Four issues :
bubble_sort[arr] does not work - you should call bubble_sort(arr)
original == arr - will always be true, since you assigned it to arr before - you should have assigned it using dup - original = arr.dup
arr[x] < arr [x+1] will create an array sorted in reverse order...
you should change the local copy rather than the one you got as parameters - result = arr.dup rather than original = arr.dup
The code after the above fixes:
def bubble_sort(arr)
result = arr.dup
x = 0
while x < result.count - 1
if result[x] > result[x + 1]
y = result[x + 1]
result[x + 1] = result[x]
result[x] = y
end
x += 1
end
if arr == result
return result
else
return bubble_sort(result)
end
end
bubble_sort([1,3,5,2,4])
# => [1, 2, 3, 4, 5]
I am trying to implement a merge sort and am getting stack level too deep (SystemStackError) error when I run my code. I am not sure what the issue may be.
def merge_sort(lists)
lists if lists.count == 1
middle = lists[0..(lists.count / 2) - 1 ]
left = lists[0..middle.count - 1]
right = lists[middle.count..lists.count]
x = merge_sort(left)
y = merge_sort(right)
end
merge_sort [1,2,3,4,5,6,7,8]
Any help would be great!
From wikipedia:
def mergesort(list)
return list if list.size <= 1
mid = list.size / 2
left = list[0...mid]
right = list[mid...list.size]
merge(mergesort(left), mergesort(right))
end
def merge(left, right)
sorted = []
until left.empty? || right.empty?
if left.first <= right.first
sorted << left.shift
else
sorted << right.shift
end
end
sorted.concat(left).concat(right)
end
write this
return lists if lists.count == 1
instead of
lists if lists.count == 1
In Ruby, from a method last statement is always returned by default. But if you want to return from the middle of any lines except the last line conditionally, you must need to use return keyword explicitly.
A simplified version with comments
def merge_sort(arr)
# 0. Base case
return arr if arr.length <= 1
# 1. Divide
mid = arr.length / 2
arr0 = merge_sort(arr[0, mid])
arr1 = merge_sort(arr[mid, arr.length])
# 2. Conquer
output = merge(arr0, arr1)
end
def merge(l, r)
output = []
until l.empty? || r.empty?
output << if l.first <= r.first
l.shift
else
r.shift
end
end
# The order of `concat(l)` or `concat(r)` does not matters
output.concat(l).concat(r)
end
https://www.khanacademy.org/computing/computer-science/algorithms/merge-sort/a/divide-and-conquer-algorithms
This is a good way to do it. Tis a bit tricky at first, but stay at it.
def merge_sort list
if list.length <= 1
list
else
mid = (list.length / 2).floor
left = merge_sort(list[0..mid - 1])
right = merge_sort(list[mid..list.length])
merge(left, right)
end
end
def merge(left, right)
if left.empty?
right
elsif right.empty?
left
elsif left.first < right.first
[left.first] + merge(left[1..left.length], right)
else
[right.first] + merge(left, right[1..right.length])
end
end
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I'm trying to implement the Bubble sort method into an easy coding problem for Ruby, but I'm having some trouble. I understand the idea is to look at the value of the first element and compare it to the value of the second element and then swap them accordingly, but I can't seem to do it in an actual problem. Would anyone be willing to provide a brief example of how this might work in Ruby?
Correct implementation of the bubble sort with a while loop
def bubble_sort(list)
return list if list.size <= 1 # already sorted
swapped = true
while swapped do
swapped = false
0.upto(list.size-2) do |i|
if list[i] > list[i+1]
list[i], list[i+1] = list[i+1], list[i] # swap values
swapped = true
end
end
end
list
end
arr = [4,2,5,1]
loop until arr.each_cons(2).with_index.none?{|(x,y),i| arr[i],arr[i+1] = y,x if x > y}
p arr #=> [1, 2, 4, 5]
Source
def bubble_sort(list)
return list if list.size <= 1 # already sorted
loop do
swapped = false
0.upto(list.size-2) do |i|
if list[i] > list[i+1]
list[i], list[i+1] = list[i+1], list[i] # swap values
swapped = true
end
end
break unless swapped
end
list
end
Although I would certainly recommend something with a better run-time than bubblesort :)
Here's my version of the top answer. It calls size on the array only once instead of every loop. It doesn't compare elements once they have moved to the end of the array.
And the while loop quits one loop sooner. You're done once you've gone through the whole array and only did one swap, so no need to do another with 0 swaps.
def bubble_sort(list)
iterations = list.size - 2
return list unless iterations > 0 # already sorted
swaps = 2
while swaps > 1 do
swaps = 0
0.upto(iterations) do |i|
if list[i] > list[i + 1]
list[i], list[i + 1] = list[i + 1], list[i] # swap values
swaps += 1
end
end
iterations -= 1
end
list
end
Running this test takes 25% less time.
that_array = this_array = [22,66,4,44,5,7,392,22,8,77,33,118,99,6,1,62,29,14,139,2]
49.times {|variable| that_array = that_array + this_array}
bubble_sort that_array
Just re-writing #VanDarg's code to use a while loop
(note: code not tested... run at your own peril)
def bubble_sort(list)
return list if list.size <= 1 # already sorted
swapped = true
while swapped
swapped = false # maybe this time, we won't find a swap
0.upto(list.size-2) do |i|
if list[i] > list[i+1]
list[i], list[i+1] = list[i+1], list[i] # swap values
swapped = true # found a swap... keep going
end
end
end
list
end
Edit: updated swapped-values because bubble sort keeps sorting while there are still swaps being made - as soon as it finds no more swaps, it stops sorting. Note, this does not follow #Doug's code, but does conform with #cLuv's fix
def bubble_sort array
array.each do
swap_count = 0
array.each_with_index do |a, index|
break if index == (array.length - 1)
if a > array[index+1]
array[index],array[index+1] = array[index +1], array[index]
swap_count += 1
end
end
break if swap_count == 0 # this means it's ordered
end
array
end
The straight forward:
def bubble_sort(n)
return n if n.length <= 1
0.upto(n.length - 1) do |t|
0.upto(n.length - 2 - t) do |i|
if n[i] > n[i + 1]
n[i], n[i + 1] = n[i + 1], n[i]
end
end
end
n
end
If you don't want to use this funny swapping line (IMO):
arr[i], arr[j] = arr[j], arr[i]
here's my take:
def bubble_sort(arr)
temp = 0
arr.each do |i|
i = 0
j = 1
while (j < arr.length)
if arr[i] > arr[j]
temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
p arr
end
i+=1
j+=1
end
end
arr
end
Old school
def bubble_sort(random_numbers)
for i in 0..random_numbers.size
for j in i+1..random_numbers.size-1
random_numbers[i], random_numbers[j] = random_numbers[j], random_numbers[i] if(random_numbers[i] > random_numbers[j])
end
end
random_numbers
end
class Array
a = [6, 5, 4, 3, 2, 1]
n = a.length
for j in 0..n-1
for i in 0..n - 2 - j
if a[i]>a[i+1]
tmp = a[i]
a[i] = a[i+1]
a[i+1] = tmp
end
end
end
puts a.inspect
end
Here's my take using the operator XOR:
def bubble(arr)
n = arr.size - 1
k = 1
loop do
swapped = false
0.upto(n-k) do |i|
if arr[i] > arr[i+1]
xor = arr[i]^arr[i+1]
arr[i] = xor^arr[i]
arr[i+1] = xor^arr[i+1]
swapped = true
end
end
break unless swapped
k +=1
end
return arr
end
Another, slightly different naming.
def bubble_sort(list)
return list if list.size <= 1
not_sorted = true
while not_sorted
not_sorted = false
0.upto(list.size - 2) do |i|
if list[i] > list[i + 1]
list[i], list[i + 1] = list[i + 1], list[i]
not_sorted = true
end
end
end
list
end
def bubbleSort(list)
sorted = false
until sorted
sorted = true
for i in 0..(list.length - 2)
if list[i] > list[i + 1]
sorted = false
list[i], list[i + 1] = list[i + 1], list[i]
end
end
end
return list
end
Here is my code. I like using the (arr.length-1). For loops you can also use such iterations such as until, while, for, upto, loop do, etc. Fun to try different things to see how it functions.
def bubble_sort(arr) #10/17/13 took me 8mins to write it
return arr if arr.length <= 1
sorted = true
while sorted
sorted = false
(arr.length-1).times do |i|
if arr[i] > arr[i+1]
arr[i], arr[i+1] = arr[i+1], arr[i]
sorted = true
end
end
end
arr
end
I am new to Ruby and I am trying out the merge sort algorithm as given in Wikipedia
I am getting "comparison of Fixnum with Array failed (ArgumentError)" failed error when comparing the first elements of the left and right array in the merge method. What could be the reason and how do I resolve this problem? Thanks :)
def mergeSort(array)
if array.length == 1
return array
end
middle = array.length/2 - 1
left = array[0..middle]
right = array[middle+1..array.length-1]
left = mergeSort(left)
right = mergeSort(right)
merge(left,right)
end
def merge(left,right)
result = []
while left.length > 0 || right.length > 0
if left.length > 0 && right.length > 0
one = left[0]
two = right[0]
puts ("one's class is #{one.class} two's class is #{two.class} two is #{two}")
if one <= two
result << left.shift
else
result << right.shift
end
elsif left.length > 0
result.push(left)
left = []
else
result.push(right)
right = []
end
end
puts "result is #{result}"
result
end
The error is on these lines:
elsif left.length > 0
result.push(left)
left = []
else
result.push(right)
right = []
end
A simple example should indicate why:
irb(main):067:0> a=[1,2]
=> [1, 2]
irb(main):068:0> b=[3,4]
=> [3, 4]
irb(main):069:0> a.push(b)
=> [1, 2, [3, 4]]
Instead of push(), try concat().