我已经为Quicksort实现了经典的Hoare分区算法。它适用于任何唯一数字列表[3,5,231,43]。唯一的问题是当我有一个重复的列表[1,57,1,34]。如果我得到重复的值,我进入一个无限循环。
private void quicksort(int[]a, int lo, int hi) {
if (lo < hi) {
int q = hoare_partition(a, lo, hi);
quicksort(a, lo, q - 1);
quicksort(a, q + 1, hi);
}
}
private int hoare_partition(int[] a, int lo, int hi) {
int pivot = a[hi];
int i = lo;
int j = hi;
while (true) {
while (a[i] < pivot) i++;
while (a[j] > pivot) j--;
if (i < j) swap(a, i, j);
else return j;
}
}
Hoare的实施是否可能不正确且无法应对重复?
我知道这已被多次询问(我尝试了所有这些)但我很难找到这个实现的解决方案。
答案 0 :(得分:6)
algorithm partition(A, lo, hi) is
pivot := A[lo]
i := lo – 1
j := hi + 1
loop forever
do
i := i + 1
while A[i] < pivot
do
j := j – 1
while A[j] > pivot
if i >= j then
return j
swap A[i] with A[j]
上面的伪代码取自Wikipedia。我们将它与您的代码进行比较。
问题是你必须在交换后移动索引。伪代码使用do-while
循环而不是while
循环来在交换后移动索引。另请注意i
和j
的初始值。
algorithm quicksort(A, lo, hi) is
if lo < hi then
p := partition(A, lo, hi)
quicksort(A, lo, p)
quicksort(A, p + 1, hi)
对于递归步骤,您可能需要处理边缘情况(即索引)。如果您将quicksort(a, lo, q-1)
更改为quicksort(a, lo, q)
,则应该可以使用。
我刚写的一个完整的工作版本:
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
int[] input = {1, 57, 1, 34};
quicksort(input, 0, input.length - 1);
System.out.println(Arrays.toString(input));
}
private static void quicksort(int[]a, int lo, int hi) {
if (lo < hi) {
int q = hoare_partition(a, lo, hi);
quicksort(a, lo, q);
quicksort(a, q + 1, hi);
}
}
private static int hoare_partition(int[] a, int lo, int hi) {
int pivot = a[lo];
int i = lo - 1;
int j = hi + 1;
while (true) {
do {
i++;
}
while (a[i] < pivot);
do {
j--;
}
while (a[j] > pivot);
if (i >= j) {
return j;
}
swap(a, i, j);
}
}
private static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
如果您更喜欢while
循环而不是do-while
:
private static int hoare_partition(int[] a, int lo, int hi) {
int pivot = a[lo];
int i = lo;
int j = hi;
while (true) {
while (a[i] < pivot) i++;
while (a[j] > pivot) j--;
if (i >= j) {
return j;
}
swap(a, i, j);
i++;
j--;
}
}
答案 1 :(得分:1)
这是一个C ++示例,它实现了Hoare方案加上3个检查的中位数,一个重复的数据检查(以排除中间值),只在较小的部分使用递归,循环返回较大的部分(这可以防止堆栈溢出) )。最坏的情况时间复杂度仍为O(n ^ 2),但需要相当具体的数据模式才能产生这种情况(3的中位数必须始终保持接近最小值或接近最大值)。
void QuickSort(uint32_t a[], size_t lo, size_t hi) {
while(lo < hi){
size_t i = lo, j = (lo+hi)/2, k = hi;
uint32_t p;
if (a[k] < a[i]) // median of 3
std::swap(a[k], a[i]);
if (a[j] < a[i])
std::swap(a[j], a[i]);
if (a[k] < a[j])
std::swap(a[k], a[j]);
p = a[j];
i--; // Hoare partition
k++;
while (1) {
while (a[++i] < p);
while (a[--k] > p);
if (i >= k)
break;
std::swap(a[i], a[k]);
}
i = k++;
while(i > lo && a[i] == p) // exclude middle values == pivot
i--;
while(k < hi && a[k] == p)
k++;
// recurse on smaller part, loop on larger part
if((i - lo) <= (hi - k)){
QuickSort(a, lo, i);
lo = k;
} else {
QuickSort(a, k, hi);
hi = i;
}
}
}
答案 2 :(得分:1)
上面@Terry Li的答案的地址,因为我的声誉不足以发表评论。首先,非常感谢您提供的详细信息。但是,我相信您提供的while
循环的替代功能存在一些问题。它不返回原始数据透视表的索引,并且本身不准确。 (请尝试使用hoare_partition([6,7,8,9,1,2,3,4,5], 0, 8)
)此问题是由于同时增加i
和减少j
引起的,从而使您失去了对枢轴的跟踪。因此,在下面提出的修订中,我插入了一个小条件以确保存储枢轴的任何索引都不会更改。
如果我错了,请纠正我。
private static int hoare_partition(int[] a, int lo, int hi) {
int pivot = a[lo];
int i = lo;
int j = hi;
while (true) {
while (a[i] < pivot) i++;
while (a[j] > pivot) j--;
if (i >= j) {
return i;
}
swap(a, i, j);
if (a[j] == pivot)
i++;
elif (a[i] == pivot)
j--;
}
}
答案 3 :(得分:0)
以下是while循环的另一个例子
private static int partition(int[] a, int start, int end){
int i = start-1, j = end+1;
while(true){
while(a[++i] < a[start]);
while(a[start] < a[--j]);
if (i >= j) return j;
swap(a, i, j);
}
}