排序
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实现
另外简单提及外部排序。
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);//参考快速排序中此方法 } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 在命令行用 sort 进行排序
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序