我正在阅读Sedgewick的《算法》中有关排序的章节。在此过程中,我编写了三个基本的排序算法:选择排序、插入排序和希尔排序。书中指出,尽管这三种算法的最坏情况时间复杂度都为二次方级别,但在随机数据上,希尔排序应该比插入排序快得多。书中给出了600倍的性能提升。
但是在我的笔记本电脑上,我得到了以下的乘数(几乎不随数组大小增加而改变):
- 选择排序:5.5倍 - 插入排序:1倍 - 希尔排序:1.8倍!
我困扰的问题是:为什么希尔排序几乎比插入排序慢两倍?
我猜测,可能是我的希尔排序实现有问题。但我几乎是从书中抄袭的。
但是在我的笔记本电脑上,我得到了以下的乘数(几乎不随数组大小增加而改变):
- 选择排序:5.5倍 - 插入排序:1倍 - 希尔排序:1.8倍!
我困扰的问题是:为什么希尔排序几乎比插入排序慢两倍?
我猜测,可能是我的希尔排序实现有问题。但我几乎是从书中抄袭的。
class ShellSort extends Sort {
//precalculate sequence: 1, 4, 13, 40, 121, 364, 1093, ...
//(3^20 - 1)/2 is enough for any array I sort
private static final int[] SEQUENCE = new int[20];
static {
for(int i = 1; i <= SEQUENCE.length; i++)
SEQUENCE[i - 1] = (int)(Math.pow(3, i) - 1) / 2;
}
public void sort(int[] a) {
int length = a.length;
int seqLen = SEQUENCE.length;
int nth;
int j;
for(int seqi = seqLen - 1; seqi >= 0; seqi--) {
if(SEQUENCE[seqi] < length / 3) {
nth = SEQUENCE[seqi];
for(int n = 0; n < length; n+=nth) {
j = n;
while(j > 0 && a[j] < a[j - nth]) {
exch(a, j, j-nth);
j -= nth;
}
}
}
}
}
}
以下是其余代码,供那些想在自己的计算机上运行测试的人使用(通过JVM热身加倍数组大小测试对结果没有显著影响,因此这个简单的测试对于N > ~ 200 000已经足够).
主函数:
int N = 500_000;
Random rand = new Random();
int[] a = new int[N];
for(int i = 0; i < N; i++)
a[i] = rand.nextInt();
//insertion sort
int[] aCopy = Arrays.copyOf(a, a.length);
long start = System.nanoTime();
new InsertionSort().sort(aCopy);
System.out.println("insert:\t" + (System.nanoTime() - start));
//shell sort
aCopy = Arrays.copyOf(a, a.length);
start = System.nanoTime();
new ShellSort().sort(aCopy);
System.out.println("shell:\t" + (System.nanoTime() - start));
插入排序和排序类:
class InsertionSort extends Sort {
public void sort(int[] a) {
int length = a.length;
int j;
int x;
for(int i = 1; i < length; i++) {
j = i;
x = a[i];
while(j > 0 && x < a[j-1]) {
a[j] = a[--j];
}
a[j] = x;
}
}
}
abstract class Sort {
abstract public void sort(int[] a);
protected static final void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}