您的位置:首页 > 编程语言 > Java开发

排序

2016-01-06 21:43 471 查看
本文将详细介绍插入排序、希尔排序、堆排序、归并排序、快速排序及快速选择算法,并用Java实现。

另外简单提及外部排序。

1.时间复杂度分析

1)插入排序:O(N^2);——适用于对少量数据或接近排序的数据进行排序。

2)希尔排序:

使用希尔增量时(gap=N/2;gap/=2),最坏情形运行时间:O(N^2);

使用Hibbard增量时,最坏情形运行时间:O(N^(3/2));

Sedgewick提出的几种增量序列,最坏情形运行时间是O(N^(4/3));平均运行时间猜测为O(N^(7/6)).

3)堆排序:

给出了至今最佳运行时间:O(NlogN).(主要问题是,使用了一个额外的数组)

4)归并排序:

所使用的比较次数几乎是做少的,最坏情形运行时间:O(NlogN).

它是标准Java类库中泛型排序所使用的算法(泛型排序时,比较是昂贵的)

5)快速排序:——适用于对大量数据进行排序

最坏情形运行时间(枢纽元始终是最小元素):O(N^2);

平均情形运行时间(最好情况下,枢纽元位于中间):O(NlogN);

在C++或对Java基本类型排序中特别有用。

6)快速选择

最坏情形运行时间:O(N^2);

平均情形运行时间:O(N);

7)桶式排序



2.外部排序

以上介绍的排序算法都需要将数据装入内存(内存可直接寻址)。

有一些应用程序,数据量太大装不进内存,此时就需要进行外部排序。

磁带上的元素只能被顺序访问。若只有一个磁带驱动器,那么任何排序算法都将需要O(N^2)次磁带访问。

外部排序对设备的依赖性较强。

基本的外部排序算法使用归并排序中的合并算法。

1)多路合并,k-路 需要2k盘磁带。

需要进行的趟数

,其中N为总的记录数,M为内存可一次容纳的记录数。(每组排过序的记录叫做一个顺串)

2)多项合并,不要求2k盘磁带。

2-路合并:如果顺串的个数是一个**斐波那契数**F(N),那么分配顺串最好的方式是把它们分裂成两个斐波那契数F(N-1)和F(N-2).否则,须用一些哑顺串来填补磁带,将顺串个数补足成一个斐波那契数。

k-路合并:需要k阶斐波那契数用于分配顺串。

3)替换选择(产生顺串的一个算法)

替换选择产生平均长度为2M的顺串。

3.插入排序、希尔排序、堆排序、归并排序、快速排序及快速选择算法的Java实现

/**
* 插入排序
* 时间复杂度:O(N^2),反序输入可以达到该界
* 若输入数据已预先排序,则运行时间O(N).(内层for循环总是立即判定不成立而终止)
* 插入排序的运行时间为O(I+N),I为原始数组中逆序数。
* @param a 待排序数组
*/
public <Type extends Comparable<? super Type>> void insertionSort(Type[] a){
//避免明显的使用交换,算法所需时间较短
int j;
for(int p=1;p<a.length;p++){
Type tmp = a[p];
for(j=p;j>0 && tmp.compareTo(a[j-1])<0;j--){//避免明显的使用交换
a[j] = a[j-1];
}
a[j] = tmp;
}
}
public <Type extends Comparable<? super Type>> void insertionSort1(Type[] a){
//明显的使用了交换,算法所需时间较长
for(int i=1;i<a.length;i++){
for(int j=i;j>0;j--){
if(a[j].compareTo(a[j-1])<0){
Type tmp = a[j];a[j] = a[j-1];a[j-1] = tmp;
}else{
break;
}
}
}
}


/**
* 希尔排序(希尔增量):最坏运行时间:O(N^2)
* (Hibbard增量的希尔排序最坏运行时间:O(N^(3/2))).
* @param a
*/
public <Type extends Comparable<? super Type>> void shellSort(Type[] a){
int j;
for(int gap=a.length/2;gap>0;gap=gap/2){
for(int i=gap;i<a.length;i++){
Type tmp = a[i];
for(j=i;j>=gap && tmp.compareTo(a[j-gap])<0;j-=gap){
a[j] = a[j-gap];
}
a[j] = tmp;
}
}
}


/**
* 堆排序(MAX堆)
* 有至今见到的最佳运行时间:O(NlogN).
* @param a
*/
public static <Type extends Comparable<? super Type>> void heapSort(Type[] a){
for(int i=a.length/2;i>=0;i--){  //创建MAX堆
percDown(a,i,a.length);
}
for(int i=a.length-1;i>0;i--){  //删除堆中最大值,同时完成排序
swapReferences(a,0,i);
percDown(a,0,i);
}
}
private static int leftChild(int i){
return 2*i+1;
}
/**
* 创建MAX堆
* @param a 待排序数组
* @param i 下滤起始位置
* @param n 二叉堆大小
*/
private static <Type extends Comparable<? super Type>> void percDown(Type[] a,      int i,int n){
int child;
Type tmp;
for(tmp=a[i];leftChild(i)<n;i=child){
child = leftChild(i);
if(child!=n-1 && a[child].compareTo(a[child+1])<0){
child++;
}
if(a[child].compareTo(tmp)>0){
a[i] = a[child];
}else{
break;
}
}
a[i] = tmp;
}
/**
* 交换值
*/
private static <Type extends Comparable<? super Type>> void swapReferences(Type[] a, int i,int n){
Type tmp = a[i];
a[i] = a
;
a
= tmp;
}


/**
* 归并排序:运行时间O(NlogN).
* 明显问题:合并两个已排序的表用到附加内存(可以审慎的避免)
* 归并排序使用所有流行排序算法中最少的比较次数
* !!它是标准Java类库中泛型排序所使用的算法.!!
* @param a
*/
public static <Type extends Comparable<? super Type>> void mergeSort(Type[] a){
Type[] tmpArray = (Type[]) new Comparable[a.length];
mergeSort(a,tmpArray,0,a.length-1);
}
private static <Type extends Comparable<? super Type>> void mergeSort(Type[] a,Type[] tmpArray,int left,int right){
if(left<right){
int center = (left+right)/2;
mergeSort(a,tmpArray,left,center);
mergeSort(a,tmpArray,center+1,right);
merge(a,tmpArray,left,center+1,right);
}
}
private static <Type extends Comparable<? super Type>> void merge(Type[] a,Type[] tmpArray,int leftPos,int rightPos,int rightEnd){
int leftEnd = rightPos-1;
int tmpPos = leftPos;
int num = rightEnd-leftPos+1;
while(leftPos<=leftEnd && rightPos<=rightEnd){
if(a[leftPos].compareTo(a[rightPos])<0)
tmpArray[tmpPos++] = a[leftPos++];
else
tmpArray[tmpPos++] = a[rightPos++];
}
while(leftPos<=leftEnd){
tmpArray[tmpPos++] = a[leftPos++];
}
while(rightPos<=rightEnd){
tmpArray[tmpPos++] = a[rightPos++];
}
for(int i=0;i<num;i++){
a[rightEnd] = tmpArray[rightEnd--];//???为什么一定要用rightEnd
}
}


/**
* 快速排序
* 平均运行时间:O(NlogN).(最好情况下,枢纽元正好位于中间)
* 最坏运行时间:O(N^2).(枢纽元始终是最小元素)
* 对于很小的数组,快速排序不如插入排序.
* @param a
*/
public <Type extends Comparable<? super Type>> void quickSort(Type[] a){
quickSort(a,0,a.length-1);
}
private static final int CUTOFF = 3;
private static <Type extends Comparable<? super Type>> void quickSort(Type[] a,int left,int right){
if(left+CUTOFF<=right){
Type pivot = median3(a,left,right);
int i = left; int j = right-1;
for(;;){
while(a[++i].compareTo(pivot)<0){
}
while(a[--j].compareTo(pivot)>0){
}
if(i<j)
swapReferences(a,i,j);
else
break;
}
swapReferences(a,i,right-1);//重置枢纽元

quickSort(a,left,i-1);//对所有小元素进行排序
quickSort(a,i+1,right);//对所有大元素进行排序
}else{
insertionSort(a,left,right);//对子矩阵进行插入排序
}
}
/**
* 为快速排序算法选取枢纽元
* @param a
* @param left
* @param right
* @return
*/
private static <Type extends Comparable<? super Type>> Type median3(Type[] a,int left,int right){
int center = (left+right)/2;
if(a[left].compareTo(a[right])>0)
swapReferences(a,left,right);
if(a[center].compareTo(a[left])<0)
swapReferences(a,left,center);//参考堆排序中此方法
if(a[center].compareTo(a[right])>0)
swapReferences(a,center,right);
swapReferences(a,center,right-1);//枢纽元放在(right-1)位置
return a[right-1];
}
/**
* 应用于快速排序算法(子矩阵进行插入排序)
* @param a
* @param left
* @param right
*/
private static <Type extends Comparable<? super Type>> void insertionSort(Type[] a,int left,int right){
for(int p=left+1;p<right+1;p++){
Type tmp = a[p];
int j;
for(j=p;j>left && tmp.compareTo(a[j-1])<0;j--){
a[j] = a[j-1];
}
a[j] = tmp;
}
}


/**
* 快速选择
* 最坏运行时间:O(N^2)
* 平均运行时间:O(N).
* @param a 可比较元素的数组
* @param k
*/
public <Type extends Comparable<? super Type>> void quickSelect(Type[] a,int k){
quickSelect(a,0,a.length-1,k);
}
private static <Type extends Comparable<? super Type>> void quickSelect(Type[] a,int left,int right,int k){
if(left+CUTOFF<=right){
Type pivot = median3(a,left,right);
int i = left;int j = right-1;
for(;;){
while(a[++i].compareTo(pivot)<0){
}
while(a[--j].compareTo(pivot)>0){
}
if(i<j)
swapReferences(a,i,j);//参考堆排序中此方法
else
break;
}
swapReferences(a,i,right-1);
if(k<=i){
quickSelect(a,left,i-1,k);
}else if(k>i+1){
quickSelect(a,i+1,right,k);
}
}else{
insertionSort(a,left,right);//参考快速排序中此方法
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息