线性时间选择算法 Select() :对快速排序的改进,最坏时间O(n)。(大致的思路如下:)
- 将 n 个元素,分成⌈n/5⌉⌈n/5⌉组,取出每组的中位数(第三小的元素)
- 取出⌈n/5⌉⌈n/5⌉个中位数的中位数(Select函数可以取中位数)
- 利用快排中的分解函数 Partition(),以所求中位数为基准,划分 a[p : r] 为两段。
- 取其中一段进行递归。
注:做完前两点可以得到上图的数组
这是书上的伪代码:
java代码(可能有些小问题是我没测试到的)
public class sy02 {
public static void main(String[] args) {
int[] arr = {2, 9, 11, 3, 14, 7, 10, 8, 15, 4, 13, 16, 5, 12};
System.out.println(Select(arr, 0, arr.length - 1, 13));
bubbleSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
//交换两个元素的位置
public static void swap(int a[], int i, int j) {
int temp = a[j];
a[j] = a[i];
a[i] = temp;
}
//冒泡排序
public static void bubbleSort(int a[], int l, int r) {
for (int i = l; i < r; i++) {
for (int j = i + 1; j <= r; j++) {
if (a[j] < a[i])
swap(a, i, j);
}
}
}
public static int Partion(int a[], int l, int r, int p) {
swap(a, p, l);
int i = l;
int j = r;
int pivot = a[l];
while (i < j) {
while (a[j] >= pivot && i < j)
j--;
a[i] = a[j];
while (a[i] <= pivot && i < j)
i++;
a[j] = a[i];
}
a[i] = pivot;
return i;
}
public static int Select(int[] arr, int p, int r, int k) {
/*
* 如果传入的数组的元素个数小于5个直接排序进行查找
* */
if (r - p < 5) {
bubbleSort(arr, p, r);
return arr[p + k - 1];
}
/*
* 将数组分割成5个元素一个小组,每个小组都进行排序
* */
int s = 0;
int t = 0;
for (int i = 0; i <= (r - p - 4) / 5; i++) {
s = p + 5 * i;
t = s + 4;
bubbleSort(arr, s, t);
// System.out.println(s+"-"+t+":"+Arrays.toString(arr));
}
/*
* 将分割成的每个数组的中位数进行排序
* */
for (int i = (p+5) / 2; i <= r - p - 5; i = i + 5) {
if (arr[i] > arr[i + 5]) {
swap(arr, i, i + 5);
}
// System.out.println(i+":"+arr[i]);
}
int i = 0;
/*
* 通过快速排序的元素分割过程,将数组中间的数作为分割点,进行分割
* */
if ((r - p) > 13) {
i = Partion(arr, p, r, ((r - p - 4) / 5 - 1) * 5 + 2);
} else {
i = Partion(arr, p, r, ((r - p - 4) / 5) * 5 + 2);
}
/*
* 把上面返回的下标进行比较,进行递归调用
* */
int m = i - p + 1;
if (m == k) {
return arr[i];
}
if (m > k) {
return Select(arr, p, i - 1, k);
}
return Select(arr, i + 1, r, k - m);
}
}