Java八种常见排序算法
来源:互联网 发布:苹果cms论坛 编辑:程序博客网 时间:2024/05/22 10:13
Java八种常见排序算法
排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面讲的排序都是属于内排序。
内排序又可以分为以下几类:
(1)、冒泡排序
(2)、选择排序
(3)、插入排序
(4)、快速排序
(5)、归并排序
(6)、希尔排序
(7)、堆排序
(8)、基数排序
下面逐个接受各个排序方法:
1、冒泡排序:
时间复杂度:O(n^2)
稳定性:稳定
private static int[] bubbleSort(int[] a) { // TODO Auto-generated method stub int len=a.length; for(int i=0;i<len-1;i++) { for(int j=len-1;j>i;j--) { if(a[j]<a[j-1]) // 轻的向上冒 { a[j]=a[j]^a[j-1]; a[j-1]=a[j]^a[j-1]; a[j]=a[j]^a[j-1]; } } } return a; }
2、插入排序:
从第一个元素开始,该元素可以认为已经被排序,取出下一个元素,在已经排序的元素序列中从后向前扫描,如果该元素(已排序)大于新元素,将该元素移到下一位置,直到找到已排序的元素小于或者等于新元素的位置,将新元素插入到该位置中
时间复杂度:O(n^2)
稳定性:稳定
private static int[] insertSort(int[] a) { int temp; int len=a.length; int j; for(int i=1;i<len;i++) { temp=a[i]; j=i; while(temp<=a[j-1]) { a[j]=a[j-1]; j--; } a[j]=temp; } return a;}
3、选择排序:
选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了
时间复杂度:O(n^2)
稳定性:不稳定
private static int[] selectSort(int[] a) { int len=a.length; int min; int index; for(int i=0;i<len-1;i++) { min=a[i]; index=i; for(int j=i+1;j<len;j++) { if(min>a[j]) { min=a[j]; index=j; } } a[index]=a[i]; a[i]=min; } return a;}
4、快排:
快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。交换a[j]和a[center_index],完成一趟快速排序
时间复杂度:O(nlgn)
稳定性:不稳定
private static void quickSort(int[] a, int start, int end) { if(start>=end||a==null) { return ; } int p=partition(a,start,end); quickSort(a, start, p-1); quickSort(a, p+1, end); } private static int partition(int[] a, int start, int end) { int first=a[start]; int i=start; int j=end; while(i<j){ while(a[i]<=first&&i<end) { i++; } while(a[j]>first&&j>=start){ j--; } if(i<j){ a[i]=a[i]^a[j]; a[j]=a[i]^a[j]; a[i]=a[i]^a[j]; } } if(j!=start){ a[start]=a[j]; a[j]=first; } return j; }
5、归并排序:
基本思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
时间复杂度:O(nlogn)
稳定性:稳定
具体代码如下:
public class SortTest { public static void main(String[] args) { // TODO Auto-generated method stub int[] A={2,5,9,3,4,7,8,156,46,7,26,79,14,4,25,4,496,269}; int[] res=sorting(A); for(int i=0;i<A.length;i++){ System.out.print(res[i]+" "); } } // 分代处理 private static int[] sorting(int[] a) { // TODO Auto-generated method stub int len=a.length; for(int gap=1;gap<len;gap=2*gap) { MergePass(a,gap,len); } return a; } // 按每一代的gap长度,进行分段,组合 private static void MergePass(int[] a, int gap, int len) { // TODO Auto-generated method stub int i=0; for(i=0;i+2*gap-1<len;i=i+2*gap){ Merge(a,i,i+gap-1,i+2*gap-1); } if(i+gap<len){ Merge(a,i,i+gap-1,len-1); } } private static void Merge(int[] a, int low, int mid, int high) { // TODO Auto-generated method stub int i=low; int j=mid+1; int k=0; int[] array2=new int[high-low+1]; while(i<=mid&&j<=high) { if(a[i]<=a[j]) { array2[k]=a[i]; i++; k++; }else { array2[k]=a[j]; j++; k++; } } while(i<=mid){ array2[k]=a[i]; i++; k++; } while(j<=high){ array2[k]=a[j]; j++; k++; } System.arraycopy(array2, 0, a, low, high-low+1); }}
6、希尔排序:
基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。
时间复杂度:O(nlogn)
稳定性:不稳定
private static void shellSort(int[] a) { int d=a.length; int temp; while(true){ d=(int)Math.ceil(d/2); for(int x=0;x<d;x++){ for(int i=x+d;i<a.length;i+=d) { temp=a[i]; int j=i; for(;j>=d&&temp<a[j-d];j-=d){ a[j]=a[j-d]; } a[j]=temp; } } if(d==1){ break; } } }
7、基排序:
基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
时间复杂度:O(nlogn)
稳定性:稳定
private static void radixSort(int[] array) { int max=array[0]; for(int i=1;i<array.length;i++){ if(array[i]>max){ max=array[i]; } } int time=0; //判断位数; while(max>0){ max/=10; time++; } //建立10个队列; @SuppressWarnings("rawtypes") List<ArrayList> queue=new ArrayList<ArrayList>(); for(int i=0;i<10;i++){ ArrayList<Integer> queue1=new ArrayList<Integer>(); queue.add(queue1); } //进行time次分配和收集; for(int i=0;i<time;i++){ //分配数组元素; for(int j=0;j<array.length;j++){ //得到数字的第time+1位数; int x=array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i); @SuppressWarnings("unchecked") ArrayList<Integer> queue2=queue.get(x); queue2.add(array[j]); queue.set(x, queue2); } int count=0;//元素计数器; //收集队列元素; for(int k=0;k<10;k++){ while(queue.get(k).size()>0){ @SuppressWarnings("unchecked") ArrayList<Integer> queue3=queue.get(k); array[count]=queue3.get(0); queue3.remove(0); count++; } } } }
8、堆排序:
基本思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进。堆的定义如下:具有n个元素的序列(h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
时间复杂度:O(nlogn)
稳定性:不稳定
private static void heapSort(int[] a) { int arrayLength=a.length; //循环建堆 for(int i=0;i<arrayLength-1;i++){ //建堆 buildMaxHeap(a,arrayLength-1-i); //交换堆顶和最后一个元素 a[0]=a[0]^a[arrayLength-1-i]; a[arrayLength-1-i]=a[0]^a[arrayLength-1-i]; a[0]=a[0]^a[arrayLength-1-i]; System.out.println(Arrays.toString(a)); } }private static void buildMaxHeap(int[] data, int lastIndex) { for(int i=(lastIndex-1)/2;i>=0;i--){ //k保存正在判断的节点 int k=i; //如果当前k节点的子节点存在 while(k*2+1<=lastIndex){ //k节点的左子节点的索引 int biggerIndex=2*k+1; //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在 if(biggerIndex<lastIndex){ //若果右子节点的值较大 if(data[biggerIndex]<data[biggerIndex+1]){ //biggerIndex总是记录较大子节点的索引 biggerIndex++; } } //如果k节点的值小于其较大的子节点的值 if(data[k]<data[biggerIndex]){ //交换他们 data[k]=data[k]^data[biggerIndex]; data[biggerIndex]=data[k]^data[biggerIndex]; data[k]=data[k]^data[biggerIndex]; //将biggerIndex赋予k,开始while循环的下一次循环, //重新保证k节点的值大于其右子节点的值 k=biggerIndex; }else{ break; } } }}
总结:
一、稳定性:
稳定:冒泡排序、插入排序、归并排序和基数排序
不稳定:选择排序、快速排序、希尔排序、堆排序
二、平均时间复杂度
O(n^2):直接插入排序,简单选择排序,冒泡排序。
在数据规模较小时(9W内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。性能为O(n^2)的算法基本上是相邻元素进行比较,基本上都是稳定的。
O(nlogn):快速排序,归并排序,希尔排序,堆排序。
其中,快排是最好的, 其次是归并和希尔,堆排序在数据量很大时效果明显。
三、排序算法的选择
1、数据规模较小
(1)待排序列基本序的情况下,可以选择直接插入排序;
(2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
2、数据规模不是很大
(1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
(2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
3、数据规模很大
(1)对稳定性有求,则可考虑归并排序。
(2)对稳定性没要求,宜用堆排序
4、序列初始基本有序(正序),宜用直接插入,冒泡
- Java八种常见排序算法
- 八种常见排序算法
- 八种常见排序算法总结
- 八种常见的排序算法
- Java 八种排序算法比较实践
- 八种排序算法-Java实现
- java常见排序算法
- 常见Java排序算法
- Java常见排序算法
- java常见排序算法
- Java常见排序算法
- Java常见排序算法
- Java常见排序算法
- Java常见排序算法
- java常见排序算法
- Java--常见排序算法
- 常见的八种排序
- 八种排序算法
- MATLAB实现多分类问题,使用libsvm,1-vs-rest和1-vs-1两种方法代码
- Service Intent must be explicit 原因和解决方法
- 学习RAC小记-适合给新手看的RAC用法总结
- Android resultCode 总为0
- 潜行-----ScrollView的基本的xml设置
- Java八种常见排序算法
- 陶哲轩实分析--推荐
- 安卓百度地图的简单使用
- MyEclipse+Tomcat+MAVEN+SVN项目完整环境搭建
- Contiki 了解(2)-HelloWorld的编译过程
- Android培训 六种常见Dialog
- css笔记
- New Year Permutation(并查集+动态容器)
- multipart/form-data 上传文件