快速排序不能变成稳定排序吗?
Posted
技术标签:
【中文标题】快速排序不能变成稳定排序吗?【英文标题】:Can't quick sort become stable sort? 【发布时间】:2017-06-02 14:19:27 【问题描述】:方法 1
C.A.R Hoare 介绍了在学校教授的划分逻辑(如下所示),
low = pivot = 0;
i = 1;
j = high = listSize-1;
while (true)
while (a[i] <= a[pivot] && (i < high))
i = i + 1;
while (a[j] >= a[pivot] && (j > low))
j = j - 1;
if (i >= j)
break;
swap(a[i], a[j])
swap(a[j], a[pivot]); // pivot element is positioned(once)
return j;
方法2
基本上尝试使其稳定排序,而不是j
指向最后一个索引(listSize-1
),如果j
指向listSize/2
(即mid
),那么,
我们遇到j > high
或i >= mid
的场景,其中a[i]
没有对应的a[j]
可与之交换,反之亦然。在这种情况下,将a[i]
与a[pivot]
交换也没有意义,这看起来是一种不正确的方法,确认一下,
我的问题:
采用方法 2,
通过保持快速排序的本质,我们不能用枢轴元素(在任何索引处)进行分区吗?
注意:分析快速排序,而不是家庭作业
【问题讨论】:
顺便说一句,我的印象是修改快速排序以使其稳定可能会牺牲足够的 quick 部分,因此最好使用自然稳定的排序。 @Hurkyl 牺牲quick是什么意思?我从来没想过这个算法中quick的含义 快速排序很受欢迎,因为它往往比其类别中的其他算法(如合并排序和堆排序)运行得更快。但它的优势并不是很大——如果你让快速排序做更多的工作,你很容易失去这种优势。而且我希望使快速排序稳定所需的工作足以失去优势。 任何排序都可以变得稳定,但代价是使用更复杂的比较。如果您的比较通过比较原始索引来打破平局,那么排序将是稳定的。 @Hurkyl 快速排序比合并排序快,尽管在快速排序中compare()
比合并排序要快 39%。因为,更少的swap()
操作发生在快速排序中。在机器级别,swap()
比 compare()
更昂贵。
【参考方案1】:
这看起来像家庭作业,所以我不会完全解决它:
通过确保没有 2 个元素比较相等,可以使快速排序变得稳定。
单独选择不同的枢轴并不能解决此问题。
既然你说这不是功课,下面是如何使快速排序稳定:
创建一个指向原始数组的指针数组。使用快速排序通过函数以这种方式比较指向的值来对该数组进行排序:
int sortptr(const void *a, const void *b)
const my_type * const *pa = a;
const my_type * const *pb = b;
int cmp = original_compare_function(*pa, *pb);
return cmp ? cmp : (pa > pb) - (pa < pb);
将已排序的项目复制到已排序的数组中。
请注意,这种方法可以在原地工作,但这样做很棘手,并且仍然需要分配指针数组。合并排序对于稳定排序要可靠得多,但需要的工作空间大约是原始数组大小的一半。
【讨论】:
查询已更新。你是说,如果元素不同,快速排序可以稳定吗?如果是,那不是使其稳定的解决方案。因为您正在更改问题以确保解决方案有效。 不,我是说比较函数不应该返回0
:如果相同的元素根据它们在数组中的原始位置比较非零,那么您的排序将是稳定的。
@overexchange:您的比较函数没有实现我提到的技巧,此外,它仅在原始数组元素的地址严格递增时才有效。在您的情况下,该数组已经是一个指针数组,但是您不能保证元素满足约束,并且您没有使用额外的测试实现比较功能。
我不依赖地址比较(容易出错)逻辑。我依赖严格的值比较,如果您看到值比较是在 arraySwap()
之前完成的
地址比较不容易出错,这是使快速排序稳定所需的技巧,对于基于值的比较比较相等的元素并且如果您有原始地址。恐怕你不明白这个方法。您的代码已经解决了一种特殊情况:您对指针数组进行排序。如果这些指针以严格的递增顺序分配,则可以通过比较值相等的元素的地址来使qSort()
稳定。以上是关于快速排序不能变成稳定排序吗?的主要内容,如果未能解决你的问题,请参考以下文章