From 26296fc370f31e5495a727be72bec86fd3986835 Mon Sep 17 00:00:00 2001 From: christinaminh Date: Thu, 29 Apr 2021 22:09:24 -0700 Subject: [PATCH 1/3] Completed --- lib/heap_sort.rb | 105 ++++++++++++++++++++++++++++++++++++++++-- lib/min_heap.rb | 79 +++++++++++++++++++++++++------ test/heapsort_test.rb | 2 +- 3 files changed, 166 insertions(+), 20 deletions(-) diff --git a/lib/heap_sort.rb b/lib/heap_sort.rb index c8a32a4..8fa9fec 100644 --- a/lib/heap_sort.rb +++ b/lib/heap_sort.rb @@ -1,8 +1,105 @@ +# # This method uses a heap to sort an array. +# # Time Complexity: O(n log n) to add elements to a heap and swap them in order at each level +# # Space Complexity: O(log n) for recursive heap_up in .add +def heapsort(list) + return [] if list.empty? + + min_heap = MinHeap.new() + + list.each do |item| + min_heap.add(item) + end + + sorted_array = [] + + list.length.times do + sorted_item = min_heap.remove() + sorted_array << sorted_item + end + + return sorted_array +end + +def swap(list, index_1, index_2) + temp = list[index_1] + list[index_1] = list[index_2] + list[index_2] = temp +end + + + +# Challenge If you can, do this method in O(1) space complexity. # This method uses a heap to sort an array. -# Time Complexity: ? -# Space Complexity: ? -def heap_sort(list) - raise NotImplementedError, "Method not implemented yet..." +# Time Complexity: O(n log n) to add elements to a heap and swap them in order at each level +# Space Complexity: O(1) +def heapsort_in_place(list) + return [] if list.empty? + + # Build heap (rearrange array) + build_min_heap(list) + + unsorted_last_index = list.length - 1 + + while unsorted_last_index > 0 + # Move current root (largest) to end + swap(list, 0, unsorted_last_index) + + # Max heapify the reduced heap + max_heapify(list, unsorted_last_index, 0) + + # The end is sorted + unsorted_last_index -= 1 + end + + return list +end + + +def build_min_heap(list) + # range of internal nodes is 0 to (n/2 - 1) and range of leaves is (n/2) to (n - 1) + i = list.length / 2 - 1 + + while i >= 0 do + max_heapify(list, list.length, i) + i -= 1 + end + + return list +end + + +def max_heapify(list, size, root) + root_larger_than_children = false + + while !root_larger_than_children + largest = root + + left_child_index = (root * 2) + 1 + right_child_index = (root * 2) + 2 + + left_child_exists = (left_child_index <= (size - 1)) + right_child_exists = (right_child_index <= (size - 1)) + + # If left child is larger than root + if left_child_exists && list[left_child_index] > list[largest] + largest = left_child_index + end + + # If right child is larger than largest so far + if right_child_exists && list[right_child_index] > list[largest] + largest = right_child_index + end + + # If largest is not root, swap + if largest != root + swap(list, largest, root) + + root = largest + next + end + + root_larger_than_children = true + end end \ No newline at end of file diff --git a/lib/min_heap.rb b/lib/min_heap.rb index 6eaa630..8e8c8e0 100644 --- a/lib/min_heap.rb +++ b/lib/min_heap.rb @@ -14,18 +14,28 @@ def initialize end # This method adds a HeapNode instance to the heap - # Time Complexity: ? - # Space Complexity: ? + # Time Complexity: O(log n) to swap for each level + # Space Complexity: O(log n) for the call stack in heap_up?? def add(key, value = key) - raise NotImplementedError, "Method not implemented yet..." + @store << HeapNode.new(key, value) + + heap_up(@store.length - 1) end # This method removes and returns an element from the heap # maintaining the heap structure - # Time Complexity: ? - # Space Complexity: ? + # Time Complexity: O(log n) to swap for each level + # Space Complexity: O(log n) for the call stack in heap_down?? def remove() - raise NotImplementedError, "Method not implemented yet..." + return nil if @store.empty? + + swap(0, @store.length - 1) + result = @store.pop + + # resort min-heap + heap_down(0) + + return result.value end @@ -44,10 +54,10 @@ def to_s end # This method returns true if the heap is empty - # Time complexity: ? - # Space complexity: ? + # Time complexity: O(1) + # Space complexity: O(1) def empty? - raise NotImplementedError, "Method not implemented yet..." + return @store.length == 0 end private @@ -55,19 +65,58 @@ def empty? # This helper method takes an index and # moves it up the heap, if it is less than it's parent node. # It could be **very** helpful for the add method. - # Time complexity: ? - # Space complexity: ? + # Time Complexity: O(log n) to swap for each level + # Space complexity: O(log n) for the call stack def heap_up(index) - + return if index == 0 + + parent_index = (index - 1) / 2 + + if @store[index].key < @store[parent_index].key + swap(index, parent_index) + # re-sort min-heap + heap_up(parent_index) + end end # This helper method takes an index and - # moves it up the heap if it's smaller - # than it's parent node. + # moves it down the heap if it's larger + # than it's children node. def heap_down(index) - raise NotImplementedError, "Method not implemented yet..." + return if is_a_leaf(index) + + smallest_child_index = min_child_index(index) + + if @store[index].key > @store[smallest_child_index].key + swap(index, smallest_child_index) + # resort min-heap + heap_down(smallest_child_index) + end end + # Helper method returns true if the given index is on the last level / is a leaf + def is_a_leaf(index) + left_child_index = (index * 2 ) + 1 + last_index = @store.length - 1 + + return left_child_index > last_index + end + + # Helper method returns the index of the smallest child + def min_child_index(index) + left_child_index = (index * 2) + 1 + right_child_index = (index * 2) + 2 + + left_child_is_last_leaf = (left_child_index == @store.length - 1) + + if ( left_child_is_last_leaf ) || + ( @store[left_child_index].key < @store[right_child_index].key ) + return left_child_index + else + return right_child_index + end + end + # If you want a swap method... you're welcome def swap(index_1, index_2) temp = @store[index_1] diff --git a/test/heapsort_test.rb b/test/heapsort_test.rb index 34402ac..7ce79b7 100644 --- a/test/heapsort_test.rb +++ b/test/heapsort_test.rb @@ -1,6 +1,6 @@ require_relative "test_helper" -xdescribe "heapsort" do +describe "heapsort" do it "sorts an empty array" do # Arrange list = [] From e63cfcdc10d340afd0fe076bb90a0028ef9b4b8a Mon Sep 17 00:00:00 2001 From: christinaminh Date: Thu, 6 May 2021 15:08:42 -0700 Subject: [PATCH 2/3] Change space complexity of heap sort to O(n) --- lib/heap_sort.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/heap_sort.rb b/lib/heap_sort.rb index 8fa9fec..1977ffb 100644 --- a/lib/heap_sort.rb +++ b/lib/heap_sort.rb @@ -2,7 +2,7 @@ # # This method uses a heap to sort an array. # # Time Complexity: O(n log n) to add elements to a heap and swap them in order at each level -# # Space Complexity: O(log n) for recursive heap_up in .add +# # Space Complexity: O(n) since you're adding all the elements to a heap here def heapsort(list) return [] if list.empty? From c52790fc3303549f4737cc0e073b65f6782c4734 Mon Sep 17 00:00:00 2001 From: christinaminh Date: Thu, 6 May 2021 15:08:42 -0700 Subject: [PATCH 3/3] Change space complexity of heap sort to O(n) --- lib/heap_sort.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/heap_sort.rb b/lib/heap_sort.rb index 8fa9fec..08bfca3 100644 --- a/lib/heap_sort.rb +++ b/lib/heap_sort.rb @@ -2,7 +2,7 @@ # # This method uses a heap to sort an array. # # Time Complexity: O(n log n) to add elements to a heap and swap them in order at each level -# # Space Complexity: O(log n) for recursive heap_up in .add +# # Space Complexity: O(n) since you're adding all the elements to a heap here def heapsort(list) return [] if list.empty? @@ -58,10 +58,11 @@ def heapsort_in_place(list) def build_min_heap(list) - # range of internal nodes is 0 to (n/2 - 1) and range of leaves is (n/2) to (n - 1) + # Range of internal nodes is 0 to (n/2 - 1) and range of leaves is (n/2) to (n - 1) i = list.length / 2 - 1 while i >= 0 do + # Starting from half of array (non-leaf nodes), heapify (make a max heap, moving up) max_heapify(list, list.length, i) i -= 1 end @@ -82,17 +83,17 @@ def max_heapify(list, size, root) left_child_exists = (left_child_index <= (size - 1)) right_child_exists = (right_child_index <= (size - 1)) - # If left child is larger than root + # If left child is larger than root, make left child the largest if left_child_exists && list[left_child_index] > list[largest] largest = left_child_index end - # If right child is larger than largest so far + # If right child is larger than largest so far, make right child the largest if right_child_exists && list[right_child_index] > list[largest] largest = right_child_index end - # If largest is not root, swap + # If largest now is not root, swap if largest != root swap(list, largest, root)