Heapsort implementation only nearly sorts array - ruby

I've been trying to implement heapsort in Ruby, but thus far, my algorithm only sorts like 90% of the array correctly and not the rest. Can anyone see what goes wrong?
This is my code
require 'pp'
def left(i)
(i+1)*2-1
end
def right(i)
(i+1)*2
end
def max_heapify(a, root)
left, right = left(root), right(root)
max = root
if left < a.length and a[left] > a[max]
max = left
end
if right < a.length and a[right] > a[max]
max = right
end
if max != root
a[root], a[max] = a[max], a[root]
max_heapify(a, max)
else
a
end
end
def build_max_heap(a)
((a.size-1)/2).downto(0) do |i|
max_heapify(a, i)
end
a
end
def heap_sort(a)
len = a.size
build_max_heap(a)
(len-1).downto(0) do |i|
a[0], a[i] = a[i], a[0]
a.delete_at(len)
max_heapify(a, 0)
end
a
end
a = (1..10).to_a.shuffle
pp heap_sort(a)
result: [10, 9, 7, 8, 6, 2, 4, 5, 1, 3]

During the sort, when you move the max element to the end, don't touch it anymore, it is
sorted and the array to sort (and the heap) should end just before it.
You need one more parameter to max_heapify, to tell where the heap ends, it's not the
end of the array.
require 'pp'
def left(i)
i*2+1
end
def right(i)
i*2+2
end
def max_heapify(a, root, len)
left, right = left(root), right(root)
max = root
if left < len and a[left] > a[max]
max = left
end
if right < len and a[right] > a[max]
max = right
end
if max != root
a[root], a[max] = a[max], a[root]
max_heapify(a, max, len)
else
a
end
end
def build_max_heap(a)
((a.size-1)/2).downto(0) do |i|
max_heapify(a, i, a.length)
end
a
end
def heap_sort(a)
len = a.size
build_max_heap(a)
(len-1).downto(0) do |i|
a[0], a[i] = a[i], a[0]
max_heapify(a, 0, i)
end
a
end
a = (1..10).to_a.shuffle
pp heap_sort(a)
PP. Not sure what delete_at does (don't know Ruby), but I strongly suspect it's not
needed, during a sort you don't "delete" anything from the array, you just rearrange elements.

Related

How to implement Java's Comparable module in Ruby

I'm currently going over Robert Sedgewick's Algorithms book. In the book for the implementation of a Priority Queue there is the use of the Comparable module. While going over the top k frequent elements leetcode problem I noticed that there would be an error in my Ruby implementation.
def top_k_frequent(nums, k)
ans = []
h = Hash.new(0)
nums.each do |num|
h[num] += 1
end
heap = Heap.new
h.each do |k,v|
heap.insert({k => v})
end
k.times do
a = heap.del_max
ans.push(a.keys[0])
end
ans
end
class Heap
def initialize
#n = 0
#pq = []
end
def insert(v)
#pq[#n += 1] = v
swim(#n)
end
def swim(k)
while k > 1 && less((k / 2).floor, k)
swap((k / 2).floor, k)
k = k/2
end
end
def swap(i, j)
temp = #pq[i]
#pq[i] = #pq[j]
#pq[j] = temp
end
def less(i, j)
#pq[i].values[0] < #pq[j].values[0]
end
def del_max
max = #pq[1]
swap(1, #n)
#n -= 1
#pq[#n + 1] = nil
sink(1)
max
end
def sink(k)
while 2 * k <= #n
j = 2 * k
if !#pq[j + 1].nil?
j += 1 if j > 1 && #pq[j].values[0] < #pq[j + 1].values[0]
end
break if !less(k, j)
swap(k, j)
k = j
end
end
end
Above is the Java Priority Queue implementation.
Ruby's comparable operator is <=> which will return one of -1, 0, 1 and nil (nil mean could not compare).
In order to compare two objects , both need to implement a method def <=>(other). This is not on Object, so is not available on any objects that don't implement it or extend from a class that does implement it. Numbers and Strings, for example, do have an implementation. Hashes do not.
I think in your case, the issue is slightly different.
When you call queue.insert(my_hash) what you're expecting is for the algorithm to break up my_hash and build from that. Instead, the algorithm takes the hash as a single, atomic object and inserts that.
If you add something like:
class Tuple
attr_accessor :key, :value
def initialize(key, value)
#key = key
#value = value
end
def <=>(other)
return nil unless other.is_a?(Tuple)
value <=> other.value
end
end
then this will allow you to do something like:
hsh = { 1 => 3, 2 => 2, 3 => 1}
tuples = hsh.map { |k, v| Tuple.new(k, v) }
tuples.each { |tuple| my_heap.insert(tuple) }
you will have all of your data in the heap.
When you retrieve an item, it will be a tuple, so you can just call item.key and item.value to access the data.

Ruby: Continue adding each value in an array until it reaches the max set value

I can't think of a way on how to sum up the values inside the given array and stops when it reaches the max value. Like the example on below, it should add only 60 and 80. The array could be more than 3 values.
def sum (array, max_value)
#code here
end
puts sum([60, 80, 90], 200)
You can always just cap it with inject:
def sum(array, max_value)
array.inject do |s, v|
s + v <= max_value ? s + v : s
end
end
Since you have control over what value is chained forward, you can stop adding to the sum if it'd exceed your threshold.
Edit: If you're looking for this to break out on longer lists:
def sum (array, max_value)
array.inject do |s, v|
break s if s + v > max_value
s + v
end
end
add = ->(arr, max, current = 0) do
val = arr.shift
arr.empty? || val + current > max ? \
current : add(arr, max, val + current)
end
add.([60, 80, 90], 200)
#⇒ 140
The prevent a mutation of the original array, when passed by reference, one should dup it in advance:
arr, max = [60, 80, 90], 200
add.(arr.dup, max)
FWIW: the solution that does not mutate an input:
add = lambda do |arr, max, current = 0, entry = true|
arr = arr.dup if entry
val = arr.shift
arr.empty? || val + current > max ? \
current : add(arr, max, val + current, false)
end
Ruby is not my first language, but you could do a simple loop:
def msum(arr,m)
i=0
sum=0
arr.each do |e|
sum+=e
break if sum>m
i+=1
end
return arr.take(i)
end

How to create a bubble sort of a double-linked list for Ruby

I have been implementing a bubble sort for a doubly linked list:
def sort2(list) #bubble sort
for i in 0...list.length
for j in 0...list.length-1-i
if list.get(j+1)<list.get(j)
list.swap(j+1, j)
end
end
end
end
I don't have any idea how implement a bucket-sort. We can only use methods like:
get(i) - which return value of i element
swap(i, j) - which swaps two elements
length(list) - return length of list
This is the code for get, swap and length:
def swap(i,j)
if i > j
i, j = j, i
elsif j == i
return
end
tmp = nil
list = #ListE.next #first element
for it in 0...j
if i == it
tmp = list
end
list = list.next
end
tmp.v, list.v = list.v, tmp.v
end
def get(i)
a = #ListE
while i>0
a = a.next
i-=1
end
return a.next.v
end
def length()
list = #ListE.next
length_of_list = 0
while list.v != nil
length_of_list += 1
list = list.next
end
return length_of_list
end
This is my attempt at an insertion sort:
def sort3(list) #insertion sort
for i in 1...list.length
j = i
while j > 0 and list.get(j-1) > list.get(j)
list.swap(j-1, i)
j -= 1
end
end
end

How do I write a merge sort?

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

Using the Bubble sort method for an array in Ruby [closed]

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

Resources