【问题标题】:Implementing a mergesort without using an additional array?在不使用附加数组的情况下实现合并排序?
【发布时间】:2011-01-11 09:45:45
【问题描述】:

我最近阅读了很多关于合并排序的内容,我想知道是否有一种方法可以在不使用至少一个额外数组的情况下进行合并排序。有可能吗?

【问题讨论】:

    标签: algorithm mergesort


    【解决方案1】:

    显然是这样。 This paper 描述了就地合并排序:

    详细分析了经典归并排序算法的两个就地变体。第一个直接变体最多执行 N log 2 N + O(N ) 比较和 3N log 2 N + O(N ) 移动以对 N 个元素进行排序。第二个更高级的变体最多需要 N log 2 N + O(N ) 比较和“N log 2 N 移动,对于任何固定”? 0 和任何 N ? N (")。理论上,第二个优于高级版本的堆排序。实际上,由于索引操作的开销,我们最快的就地合并排序仍然比自下而上的堆排序慢 50% . 但是,与基于就地合并的合并排序算法相比,我们的实现是实用的。

    Jyrki Katajainen、Tomi Pasanen、Jukka Teuhola,“Practical In-Place Mergesort”(1996 年)。

    【讨论】:

    • 这里不打算总结论文了,没看过。但是我在摘要和参考中添加了,这应该有助于跟踪任何死链接。
    【解决方案2】:

    根据Wikipedia 确实有可能,但可能不会产生任何性能提升:

    就地排序是可能的(例如,使用列表而不是数组),但非常复杂,并且在实践中几乎不会提供性能提升,即使算法运行在 O(n log n) 时间。在这些情况下,像堆排序这样的算法通常可以提供相当的速度,而且要简单得多。此外,与标准归并排序不同,就地归并排序不是稳定排序。在链表的情况下,算法使用的空间不超过列表表示已经使用的空间,而是用于递归跟踪的 O(log(k))。有些人会争辩说,对链表进行排序是不合适的,因为即使您在给定的数据结构中排序,数据结构本身也有 O(n) 个您正在操作的额外数据(例如,链接在列表中)。

    【讨论】:

      【解决方案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));  
      }
      

      【讨论】:

      • 能否提供rotateRight方法的代码?
      【解决方案4】:

      不,您总是需要一个额外的数据结构来合并已排序的元素。否则,您只会覆盖已排序的内容。

      【讨论】:

      • 为了记录,很难证明是否定的。
      猜你喜欢
      • 2021-09-03
      • 2014-02-23
      • 1970-01-01
      • 2021-07-11
      • 2016-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-02
      • 2021-12-15
      相关资源
      最近更新 更多