【问题标题】:How does one iteratively write merge sort?如何迭代地编写归并排序?
【发布时间】:2012-01-11 16:31:40
【问题描述】:

我写了一个递归版本的归并排序。它使用了一个单独的merge 例程:

def merge(lst1, lst2):
    i = j = 0
    merged = []
    while i < len(lst1) and j < len(lst2):
        if lst1[i] <= lst2[j]:
            merged.append(lst1[i])
            i += 1
        else:
            merged.append(lst2[j])
            j += 1
    merged.extend(lst1[i:])
    merged.extend(lst2[j:])
    return merged

def merge_sort(lst):
    if len(lst) < 2:
        return lst
    else:
        middle = len(lst) / 2
        return merge(merge_sort(lst[:middle]), merge_sort(lst[middle:]))

为了节省堆栈空间(以及为了踢球/学习算法的纯粹乐趣),我正在尝试以迭代方式编写此函数。但是,我觉得这很困难,因为我不确定最终如何组合不同的列表。

谢谢!

【问题讨论】:

标签: python algorithm sorting mergesort


【解决方案1】:

您将需要一个merge 函数(相同或几乎相同的merge 函数),它将被重复调用。因此,您无需更改 merge 函数。

这是一个多通道解决方案。以 2 的块大小开始,每次传递都将块大小加倍。

在每一次传递中,将列表划分为不重叠的大小块。将每个块分成 2 部分并在 2 部分上调用 merge

这是自下而上的版本。

【讨论】:

    【解决方案2】:

    我根据 Divya 的描述进行了扩展(还添加了一个测试功能进行验证)。下面的代码可以通过消除子数组(data_1和data_2)和就地排序来优化。

    def merge_sort_iterative(data):
      """ gets the data using merge sort and returns sorted."""
    
      for j in range(1, len(data)):
        j *= 2
        for i in range(0,len(data),j):
          data_1 = data[i:i+(j/2)]
          data_2 = data[i+(j/2):j-i]
          l = m = 0
          while l < len(data_1) and m < len(data_2):
            if data_1[l] < data_2[m]:
              m += 1
            elif data_1[l] > data_2[m]:
              data_1[l], data_2[m] = data_2[m], data_1[l]
              l += 1
          data[i:i+(j/2)], data[i+(j/2):j-i] = data_1, data_2
    
      return data
    
    def test_merge_sort():
      """test function for verifying algorithm correctness"""
    
      import random
      import time
    
      sample_size = 5000
      sample_data = random.sample(range(sample_size*5), sample_size)
      print 'Sample size: ', sample_size
    
      begin = time.time()
      sample_data = [5,4,3,2,1]
      result = merge_sort_iterative(sample_data)
      end = time.time()
      expected = sorted(sample_data)
      print 'Sorting time: %f \'secs'%(end-begin)
    
      assert result == expected, 'Algorithm failed'
      print 'Algorithm correct'
    
    if __name__ == '__main__':
      test_merge_sort()
    

    【讨论】:

      【解决方案3】:

      这里是 Java 实现

      public static <T extends Comparable<? super T>> void iterativeMergeSort(T[] seed) {
      
          for (int i = 1; i <seed.length; i=i+i)
          {
              for (int j = 0; j < seed.length - i; j = j + i+i)
              {
                  inPlaceMerge(seed, j, j + i-1, Math.min(j+i + i -1, seed.length -1));
              }
          }       
      }
      
      public static <T extends Comparable<? super T>>  void inPlaceMerge(T[] collection, int low, int mid, int high) {
          int left = low;
          int right = mid + 1;
      
          if(collection[mid].equals(collection[right])) {
              return ;//Skip the merge if required
          }
          while (left <= mid && right <= high) {          
              // Select from left:  no change, just advance left
              if (collection[left].compareTo(collection[right]) <= 0) {
                  left ++;
              } else { // Select from right:  rotate [left..right] and correct
                  T tmp = collection[right]; // Will move to [left]
                  rotateRight(collection, left, right - left);
                  collection[left] = tmp;
                  // EVERYTHING has moved up by one
                  left ++; right ++; mid ++;
              }
          }       
      }
      

      这是单元测试

      private Integer[] seed;
      
      @Before
      public void doBeforeEachTestCase() {
          this.seed = new Integer[]{4,2,3,1,5,8,7,6};
      }
      @Test
      public void iterativeMergeSortFirstTest() {
          ArrayUtils.<Integer>iterativeMergeSort(seed);
          Integer[] result = new Integer[]{1,2,3,4,5,6,7,8};
          assertThat(seed, equalTo(result));  
      }
      

      【讨论】:

        【解决方案4】:

        递归更直观,因此我更喜欢递归,除非在某些情况下我想避免显着的堆栈深度(例如,在使用某些协同例程实现时)。然而,在合并排序的情况下,迭代版本实际上更容易遵循(至少是伪代码)。

        所需要的只是一个嵌套循环,内循环对 2^k 元素对执行合并,外循环负责增加 k。

        需要的额外步骤是将任何未配对的组与之前的合并组合并。如果元素的数量不是 2 的幂,则会遇到不成对的组。不成对的组将始终在迭代结束时。

        例如 [5, 7, 3, 4, 1, 9] -> [5, 7] [3, 4] [1, 9] -> [3, 4, 5, 7] [1, 9] -> [1 , 3, 4, 5, 7, 9]

        在上面的示例中,[1, 9] 是一个最初没有要合并的其他组的组。因此它与前一组(已经合并和排序)合并了

        这是一个python实现:

        from MergeSort import merge
        
        def sort(arr):
            n = len(arr) - 1
            c = 1
            start = 0
            mid = 0
            end = 0
            while c <= n:
                while end < n:
                    mid = start + c//2
                    end = start + c
                    if (start < n) and (end <= n):
                        merge(arr, start, mid, end)
                        start = end + 1
                    else:
                        merge(arr, start - c - 1, start-1, n)
                c = 2*c + 1
                start = 0
                mid = 0
                end = 0
        

        我使用了常规(递归)版本的合并功能。虽然上面的代码不是最优雅的,但它可以工作并且与递归版本具有相同的复杂性。 (我没有彻底检查过,但我一眼就觉得是这样)

        这是一个单元测试:

        def test_merge_sort_iterative(self):
            for i in range(1, 100):
                length = randint(10, 5000)
                data = [randint(1, 10000) for x in range(1, length)]
                IterativeMergeSort.sort(data)
                issorted = True
                i = 0
                while (i < len(data) - 1) & issorted:
                    if data[i] > data[i + 1]:
                        issorted = False
                    i += 1
            self.assertTrue(issorted, data)
            return
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-09-22
          • 1970-01-01
          • 1970-01-01
          • 2012-07-01
          • 1970-01-01
          • 2014-02-02
          • 2012-09-15
          相关资源
          最近更新 更多