最近事情比较多,没有好好看书。今天的的学习内容是高级排序。
高级排序主要介绍的就是希尔排序,快速排序,基数排序。
1-希尔排序
基本原理就是通过增加比较数据之间的步长,是数据快速移动到其排序后位置的附近,从而避免了像插入排序一样一步一步的移动到自己的位置上。希尔排序在一次排序中也是使用的插入排序。
希尔排序的设计关键就是确定步长。Knuth序列采用公式 h=3*h + 1。首先根据从h=1开始计算h的指,直到h大于数据个数。
然后在排序过程中在逆序计算h作为步长,执行插入排序,最后h=1,相当于执行了一个插入排序。但是此时数据已经基本有序了。
排序代码实现:
public class ShellSort {
private long[] ar;
private int nElems;
public ShellSort(int max) {
ar = new long[max];
nElems = 0;
}
public void insert(long value) {
if (nElems == ar.length) {
return;
}
ar[nElems] = value;
nElems++;
}
public void sort() {
int inner, outer;
long temp;
int h = 1;
// 在这里为什么使用的比较条件不是h<nElems呢?
// 因为条件满足的条件下应该是会再*3的,随意应该使用/3比较,保证下次判断的时候是正确的计算结果。
while (h <= nElems / 3) {
h = h * 3 + 1;
}
while (h > 0) {
for (outer = 0; outer < nElems; outer++) {
temp = ar[outer];
inner = outer;
while (inner > h - 1 && ar[inner - h] >= temp) {
ar[inner] = ar[inner - h];
inner = h;
}
ar[inner] = temp;
}
h = (h - 1) / 3;
}
}
}
希尔排序的步长计算公式不是固定的。算法设计之初采用二分方法计算步长,但是效率并不少。然后就是用3倍的计算方法。还有5倍的计算方法等。
目前为止还没有确定结论说明希尔排序的效率。但是一般应该就是O(N*(logN)²)。
2-快速排序
快速排序采用的就是划分和递归的算法处理。
实现代码如下:
public void quickSort(int left, int right) {
if (left >= right) {
return;
}
long pivot = ar[right];
int part = partint(left, right, pivot);// 执行一次划分
quickSort(left, part - 1);// 递归的方法,实现了子部分的快速排序
quickSort(part + 1, right);
}
public int partint(int left, int right, long pivot) {
int leftPtr = left - 1;
int rightPtr = right;
while (true) {
while (ar[++leftPtr] < pivot)// 查找左侧的大值
;
while (rightPtr > 0 && ar[--rightPtr] > pivot)// 查找右侧的小值
;
if (leftPtr >= rightPtr) {// 一趟划分执行结束
break;
} else {
swap(left, right);
}
}
swap(leftPtr, right);
return -1;
}
private void swap(int left, int right) {
long temp = ar[left];
ar[left] = ar[right];
ar[right] = temp;
}
快速排序的效率达到了O(N*LogN)。
以上的快速排序的枢纽选择为数据最右侧数据。在数列的顺序基本无序的情况下相对平均,性能还好。如果是对逆序排序,那么机会发生不停的交换,导致快速的性能下降到了O(N²)。
关键就在于枢纽的选择一只都是序列最右侧数据。可以通过改变枢纽的选择规则来弥补这种情况。
可以使用数据最左侧、中间、最右侧三个数之间的中间值作为枢纽,而且选择枢纽的过程中,这三个数就已经排好序了。
同时快速在大数据集中的排序性能优势非常明显,但是对于小数据集基本没什么优势。有时候可能还要更慢一些。
因此可以对快速排序的方法进行一个判定,如果数据集小于100,就采用插入排序。如果大于100,再使用快速排序,同时采用三数据项取中间的方法来获得枢纽值。本快速排序方法采用递归的方法,也可以通过引入栈结构消除递归。
3-基数排序
还有一种比较有意思的排序方法就是基数排序。这种方法的原理与之前的不同,是通过数字的分解,通过对应位数的比较来进行排序。
基数排序貌似非常高效,但实际上和快速排序差不多,而且需要的空间是快速排序的两倍。
本次学习的内容主要是高级排序中的基数排序和快速排序,下一次准备学习二叉树。