1.问题: 对一组长度为N的数组,找到其中排序为第K个的数
回答:采用快速排序的方法
import java.util.*;
public static Comparable select (Comparable[] a, int K)
{
if(k<0||k>= a.length)
{ throw new IndexOutOfBoundsException("k is out of the bounds!"); }
Collections.shuffle(a);
int lo = 0, hi=a.length - 1;
while(lo < hi)
{
int j = partition(a, lo, hi);
if(k>j) lo = j+1;
else if (k<j) hi = j-1;
else return a[j];
}
return a[lo];
}
时间复杂度:
最差是二次,但是在前面做了shuffle的情况下,基本可以保证是线性的
N+N/2+N/4+....+1 ~ 2N
2. 问题: Dijkstra提出的“荷兰国旗问题”(The Dutch National Flag Problem)
待排序序列的关键字中有很多重复的值,把数据分成三段,使相同的值在中间,左边的都比其小,右边的都比其大。
比如我们想对所有的学生按照年龄进行排序,按照性别进行排序等,这样每一类别中会有很多的重复的值。理论上,这些重复的值只需要处理一次就行了。但是一般的快速排序会递归进行划分,因为一般的快速排序只是将序列划分为了两部分,小于或者大于等于这两部分。
既然要利用连续、相等的元素不需要再参与排序这个事实,一个直接的想法就是通过划分让相等的元素连续地摆放:
Dijkstra的方法如图:
从左至右扫描数组,维护一个指针lt使得[lo…lt-1]中的元素都比v小,一个指针gt使得所有[gt+1….hi]的元素都大于v,以及一个指针i,使得所有[lt…i-1]的元素都和v相等。元素[i…gt]之间是还没有处理到的元素,i从lo开始,从左至右开始扫描:
· 如果a[i]<v: 交换a[lt]和a[i],lt和i自增
· 如果a[i]>v:交换a[i]和a[gt], gt自减
· 如果a[i]=v: i自增
代码:
public static void sort(Comparable[] a)
{
Collections.shuffle(a);
sort(a, 0, a.length-1);
}
private static void sort(Comparable[] a, int lo, int hi)
{
//小序列,采用插入排序算法
if(hi - lo <= CUTOFF - 1)
{
insertionSort(a, int lo, int hi);
return;
}
int lt = lo, gt = hi;
Comparable v = a[lo];
int i = lo;
while (i <= gt)
{
int cmp = a[i].compareTo(v);
if (cmp < 0) {exch(a, i++, lt++); }
else if(cmp > 0) {exch(a, i, gt--); }
else i++;
}
}
private static void insertionSort(Comparable[] a, int lo, int hi)
{
for(int i = lo; i <= hi; i++)
{
for(int j = i; j>lo&&less(a, j, j-1); j--)
exch(a, j, j-1);
}
}