Ruby中的递归合并排序

时间:2015-07-10 22:41:38

标签: ruby sorting recursion mergesort

我正在尝试编写一个以递归方式执行合并排序的ruby方法。我的方法有效,但这是我无意中使用它的时间之一,所以我不知道为什么它有效,并且很想了解我编写的代码是如何工作的。在伪代码中,我遵循的步骤如下所示。

  1. 拆分长度为n的原始数组,直到我有n个长度为1的数组
  2. 合并并排序长度为m的2个数组,以返回长度为m * 2
  3. 的数组
  4. 重复上面的步骤,直到我有一个现在已排序的长度为n
  5. 的数组

    基本上这对我来说是一个大树分支到n个分支,每个分支包含一个长度为1的数组。然后我需要取这些n个分支并以某种方式将它们合并回到方法中的单个分支

    def merge_sort(arr)
      return arr if arr.length == 1
      merge(merge_sort(arr.slice(0, arr.length/2)), 
            merge_sort(arr.slice(arr.length/2, arr[-1])))
    end
    
    def merge(arr1, arr2)
      sorted = []
      begin
        less_than = arr1[0] <=> arr2[0]
        less_than = (arr1[0] == nil ? 1 : -1) if less_than == nil
        case less_than
        when -1  
          sorted << arr1[0]
          arr1 = arr1.drop(1)
        when 0
          sorted << arr1[0]
          sorted << arr2[0]
          arr1 = arr1.drop(1)
          arr2 = arr2.drop(1)
        when 1
          sorted << arr2[0]
          arr2 = arr2.drop(1)
        end
      end until (arr1.length == 0 && arr2.length == 0)
      sorted
    end
    
    merge_sort([1,6,3,8,22,3,11,24,54,68,79,80,98,65,46,76,53])
    
    #Returns => [1, 3, 3, 6, 8, 11, 22, 24, 46, 53, 54, 65, 68, 76, 79, 80, 98]
    

    我实际上正确对列表进行排序的方法,但我不完全确定该方法如何组合每个分支然后返回已排序的合并列表,而不仅仅是它组合的前两个长度的数组。

    此外,如果有人想知道如何让合并方法更漂亮,看起来更像我喜欢的红宝石代码,请告诉我。

2 个答案:

答案 0 :(得分:5)

这是我在Ruby中的mergesort实现

def mergesort(array)
  return array if array.length == 1
  middle = array.length / 2
  merge mergesort(array[0...middle]), mergesort(array[middle..-1])
end


def merge(left, right)
  result = []
  until left.length == 0 || right.length == 0 do
    result << (left.first <= right.first ? left.shift : right.shift)
  end
  result + left + right
end

正如您所看到的,mergesort方法与您的方法基本相同,这就是递归发生的地方,这就是我将关注的内容。

首先,你有基本情况:return array if array.length == 1这是允许递归工作而不是无限期地继续下去的原因。

接下来,在我的实现中,我定义了一个变量middle来表示数组的中间部分:middle = array.length / 2

最后,第三行是所有工作发生的地方:merge mergesort(array[0...middle]), mergesort(array[middle..-1])

你在这里做的是告诉merge方法将mergesorted left half和mergesorted右半边合并。

如果您认为输入数组是[9, 1, 5, 4],那么您所说的是merge mergesort([9, 1]), mergesort([5, 4])

为了执行合并,首先必须合并[9,1]和mergesort [5,4]。递归然后变为

merge((merge mergesort([9]), mergesort([1])), (merge mergesort([5]), mergesort([4])))

当我们再次递归时,mergesort([9])已达到基本情况并返回[9]。同样,mergesort([1])也已达到基本情况并返回[1]。现在您可以合并[9][1]。合并的结果是[1, 9]

现在是合并的另一面。在将merge mergesort([5]), mergesort([4])[1, 9]合并之前,我们必须弄清[5]的结果。按照与左侧相同的步骤,我们得到[4][4, 5]的基本情况,并合并这些以获得[1, 9]

现在我们需要将[4, 5]result合并。

  1. 在第一次通过时,1收到result = [1],因为1 <= 4。
  2. 在下一个合格中,我们正在使用left = [9]right = [4, 5]left.first <= right.first。当我们看到right.shift我们发现它是错误时,我们会返回4result = [1, 4]。现在result = [1, 4]
  3. 在第三次传递中,我们正在使用left = [9]right = [5]left.first <= right.first。当我们看到right.shift我们发现它是错误时,我们会返回5result = [1, 4, 5]。现在right.length == 0
  4. 此处循环结束,因为result + left + right
  5. 我们只是连接[1, 4, 5] + [9] + []或{{1}},这会产生一个排序数组。

答案 1 :(得分:0)

这是我针对Ruby的递归merge_sort方法的版本。上面的功能完全相同,但略有不同。

    def merge_sort(array)
        array.length <= 1 ? array : merge_helper(merge_sort(array[0...array.length / 2]), merge_sort(array[array.length / 2..-1]))
    end


    def merge_helper(left, right, merged = [])
        left.first <= right.first ? merged << left.shift : merged << right.shift until left.length < 1 || right.length < 1 
        merged + left + right
    end


p merge_sort([]) # => []
p merge_sort([20, 8]) # => [8, 20]
p merge_sort([16, 14, 11]) # => [11, 14, 16]
p merge_sort([18, 4, 7, 19, 17]) # => [4, 7, 17, 18, 19]
p merge_sort([10, 12, 15, 13, 16, 7, 19, 2]) # => [2, 7, 10, 12, 13, 15, 16, 19]
p merge_sort([3, 14, 10, 8, 11, 7, 18, 17, 2, 5, 9, 20, 19]) # => [2, 3, 5, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20]
相关问题