我在以下情况下遇到了去年的问题:
当要排序的项目列表包含很多重复值时,我们可以通过将所有与中心点相等的值组合在一起来改进QuickSort,然后递归对左侧的那些值和右侧的这些值进行快速排序。对分区方法进行必要的更改以实现此目的。
这是当前使用的实现:
// Quick Sort algorithm
public class QuickSort {
public static void quickSort(int[] a) {
quickSort(a, 0, a.length-1);
}
private static void quickSort(int[] a, int i, int j) {
if (i < j) {
int pivotIdx= partition(a, i, j);
quickSort(a, i, pivotIdx-1);
quickSort(a, pivotIdx+1, j);
}
}
private static void swap(int[] a, int pos1, int pos2) {
int temp = a[pos1];
a[pos1] = a[pos2];
a[pos2] = temp;
}
private static int partition(int[] a, int i, int j) {
// partition data items in a[i..j]
int p = a[i]; // p is the pivot, the i-th item
int m = i; // Initially S1 and S2 are empty
for (int k=i+1; k<=j; k++) { // process unknown region
if (a[k] < p) { // case 2: put a[k] to S1
m++;
swap(a,k,m);
}
}
swap(a,i,m); // put the pivot at the right place
return m; // m is the pivot's final position
}
public static void printArray(int[] a) {
for (int i = 0; i < a.length; i++)
System.out.print(a[i] +"");
System.out.println();
}
public static void main(String[] args) {
int[] arr = { 7, 12, 3, 5, -6, 3, 8, 2, 10, -3 };
printArray(arr);
quickSort(arr);
printArray(arr);
}
}
我对这里介绍的quicksort算法有一些基本的了解,但是我并不太了解这个问题是否实际上给了我有关如何实现该算法的提示,因为我认为quicksort必须遍历列表以创建2个分区,并且动态决定位置X放置枢轴的位置,在此实现中将其选择为输入数组的最左侧元素。如果此位置X是动态确定的,那么您如何精确地"将等于枢轴的元素分组"到中间,以及如何精确地改进算法?
三向分隔也称为荷兰国旗问题。
主要思想是使用三向分区策略解决此问题。有关更多详细信息,请参见荷兰国旗问题。
如果您有很多重复元素,那么您的快速排序将尝试将每个重复元素分别放置在正确的位置。但是您不需要这样做。
让我们来看一个我在上述声明中声明的示例:
假设您有一个像{4,6,4,3,4,2,5,4,1,4}这样的数组。在此数组中,元素4重复5次。而且,当应用快速排序并将4放置在正确位置时,您会将数组划分为两部分,以使左侧部分包含所有小于或等于4的元素(但没有特定顺序),并且右侧部分包含所有大于4的元素。但这是幼稚的方法。
让我们看看如何改善这一点(假设我们有很多重复的元素)
当您的快速排序找到4并将其划分数组以将该4放置在正确位置时,您还可以跟踪所有相等的元素(数组中与4相等的其他元素)以及左侧较小,右侧较大。
因此,当进行分区而不是具有2个索引left和right时(子数组0到left包含小于或等于数据透视表的所有元素,而子数组left到right包含所有元素大于轴,并且right至len(array)-1是尚待探索的元素),您可以具有3个索引,如下所述:
[0,left)-元素小于支点的子数组
[left, mid)-元素等于数据透视表的子数组
[mid, right]-元素大于支点的子数组
[right, len(array))-尚未探索的元素。
这样,您修改后的快速排序将只进行较少次数的数据透视(特别是说,等于数组中唯一元素的数量)。因此,递归调用的数量将减少。
因此,此解决方案利用了存在大量重复项的特定输入的情况(因此,数组中包含的重复项越多,quicksort的修改后的变体效果越好)
给我看一些代码。
import java.util.Arrays;
import java.util.stream.IntStream;
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[]{2, 3, 4, 1, 2, 4, 3, 5, 6, 2, 2, 2, 1, 1, 1};
quickSort(arr);
System.out.print("Sorted array:");
Arrays.stream(arr).forEach(i -> System.out.print(i +""));
System.out.println();
}
public static void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int start, int end) {
if (start > end)
return; // base condition
System.out.print("Recursive call on:");
IntStream
.rangeClosed(start, end)
.map(i -> arr[i])
.forEach(i -> System.out.print(i +""));
System.out.println();
int n = arr.length;
if (start < 0 || start >= n || end < 0 || end >= n)
throw new IllegalArgumentException("the indices of the array are not valid");
int pivot = arr[end];
/*
[start,left) - sub-array with elements lesser than pivot
[left, mid) - sub-array with elements equal to pivot
[mid, right] - sub-array with elements greater than pivot
[right, end) - elements yet to be explored.
*/
int left = start, mid = start, right = start;
while (right != end) {
if (arr[right] < pivot) {
swap(arr, left, right);
swap(arr, mid, right);
left++;
right++;
mid++;
} else if (arr[right] == pivot) {
swap(arr, mid, right);
mid++;
right++;
} else if (arr[right] > pivot) {
right++;
}
}
swap(arr, mid, right);
System.out.println("Placed" + pivot +" at it's correct position");
System.out.println();
quickSort(arr, start, left - 1);
quickSort(arr, mid + 1, end);
}
private static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
上面代码的输出是:
Recursive call on: 2 3 4 1 2 4 3 5 6 2 2 2 1 1 1
Placed 1 at it's correct position
Recursive call on: 2 4 3 5 6 2 2 2 3 4 2
Placed 2 at it's correct position
Recursive call on: 4 3 5 3 4 6
Placed 6 at it's correct position
Recursive call on: 4 3 5 3 4
Placed 4 at it's correct position
Recursive call on: 3 3
Placed 3 at it's correct position
Recursive call on: 5
Placed 5 at it's correct position
Sorted array: 1 1 1 1 2 2 2 2 2 3 3 4 4 5 6
上面的输出清楚地表明,将透视图放置在正确的位置后,我们在两个不同的数组上进行递归,但是这两个数组中都没有先前的透视图。 (这是优化)
感谢您的回答,我认为从理论上讲它是可行的,但是我还不知道如何使用实际代码来实现它。 似乎有很多情况涉及需要进行何种掉期才能进入"中间"位置。 我当前的想法只是将mid的位置初始化为数组的中间位置,随着我们在该区域中放置更多的东西,该位置应该会增加,因此Im不太相信我的想法是如何工作的。
我已经编辑了答案,以添加我用言语解释的内容的代码。