【发布时间】:2011-01-11 09:45:45
【问题描述】:
我最近阅读了很多关于合并排序的内容,我想知道是否有一种方法可以在不使用至少一个额外数组的情况下进行合并排序。有可能吗?
【问题讨论】:
我最近阅读了很多关于合并排序的内容,我想知道是否有一种方法可以在不使用至少一个额外数组的情况下进行合并排序。有可能吗?
【问题讨论】:
显然是这样。 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 年)。
【讨论】:
根据Wikipedia 确实有可能,但可能不会产生任何性能提升:
就地排序是可能的(例如,使用列表而不是数组),但非常复杂,并且在实践中几乎不会提供性能提升,即使算法运行在 O(n log n) 时间。在这些情况下,像堆排序这样的算法通常可以提供相当的速度,而且要简单得多。此外,与标准归并排序不同,就地归并排序不是稳定排序。在链表的情况下,算法使用的空间不超过列表表示已经使用的空间,而是用于递归跟踪的 O(log(k))。有些人会争辩说,对链表进行排序是不合适的,因为即使您在给定的数据结构中排序,数据结构本身也有 O(n) 个您正在操作的额外数据(例如,链接在列表中)。
【讨论】:
这是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方法的代码?
不,您总是需要一个额外的数据结构来合并已排序的元素。否则,您只会覆盖已排序的内容。
【讨论】: