排序分为内部排序和外部排序
内部排序是把待排数据元素全部调入内存中进行的排序。
外部排序是因数量太大,把数据元素分批导入内存,排好序后再分批导出到磁盘和磁带外存介质上的排序方法。
比较排序算法优劣的标准:
(1)时间复杂度:它主要是分析记录关键字的比较次数和记录的移动次数
(2)空间复杂度 :算法中使用的内存辅助空间的多少
(3)稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的
内部排序:
1)插入排序(直接插入排序、希尔排序)
2)交换排序(冒泡排序、快速排序)
3)选择排序(直接选择排序、堆排序)
4)归并排序
5)分配排序(基数排序)
插入排序的基本思想是:
每步将一个待排序的数据元素,按其关键码大小,插入到前面已经排好序的一组数据元素的适当位置上,直到数据元素全部插入为止。
常用的插入排序
(1)直接插入排序
(2)希尔排序
1.直接插入排序
其基本思想是:顺序地把待排序的数据元素按其关键字值的大小插入到已排序数据元素子集合的适当位置。
算法分析:
(1)时间效率: 因为在最坏情况下,所有元素的比较次数总和为(0+1+…+n-1)→O(n2)。其他情况下也要考虑移动元素的次数。 故时间复杂度为O(n2)
(2)空间效率:仅占用1个缓冲单元——O(1)
(3)算法的稳定性:稳定
[img]/upload/attachment/0113/0890/60b4644e-455e-33f2-825d-f2509c2937b9.jpg[/img]
2.希尔(shell)排序(又称缩小增量排序)
(1)基本思想:把整个待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;小组的个数逐次缩小,当完成了所有数据元素都在一个组内的排序后排序过程结束。
(2)技巧:小组的构成不是简单地“逐段分割”,而是将相隔某个增量dk的记录组成一个小组,让增量dk逐趟缩短(例如依次取5,3,1),直到dk=1为止。
(3)优点:让关键字值小的元素能很快前移,且序列若基本有序时,再用直接插入排序处理,时间效率会高很多。
[img]/upload/attachment/0113/0892/ea32962b-436c-3089-ba2a-aa9b775de536.jpg[/img]
选择排序
基本思想是:每次从待排序的数据元素集合中选取关键字最小(或最大)的数据元素放到数据元素集合的最前(或最后),数据元素集合不断缩小,当数据元素集合为空时选择排序结束。
常用的选择排序算法:
(1)直接选择排序
(2)堆排序
1.直接选择排序
基本思想是:从待排序的数据元素集合中选取关键字最小的数据元素并将它与原始数据元素集合中的第一个数据元素交换位置;然后从不包括第一个位置上数据元素的集合中选取关键字最小的数据元素并将它与原始数据元素集合中的第二个数据元素交换位置;如此重复,直到数据元素集合中只剩一个数据元素为止。
优点:实现简单
缺点:每趟只能确定一个元素,表长为n时需要n-1趟
算法分析
时间效率: O(n2)——虽移动次数较少,但比较次数仍多。
空间效率:O(1)——没有附加单元(仅用到1个temp)
算法的稳定性:不稳定
[img]/upload/attachment/0113/0894/411a34fa-d04b-3b0c-a2fd-9919cc651abd.jpg[/img]
2.堆排序
基本思想:把待排序的数据元素集合构成一个完全二叉树结构,则每次选择出一个最大(或最小)的数据元素只需比较完全二叉树的高度次,即log2n次,则排序算法的时间复杂度就是O(nlog2n)。
堆的定义
堆分为最大堆和最小堆两种。定义如下:
设数组a中存放了n个数据元素,数组下标从0开始,如果当数组下标2i+1<n时有:a[i].key≥a[2i+1].key(a[i].key≤a[2i+1].key);如果当数组下标2i+2<n时有:a[i].key≥a[2i+2].key (a[i].key≤a[2i+2].key),则这样的数据结构称为最大堆(最小堆)。
[img]/upload/attachment/0113/0896/1c2d5717-0f50-3065-b9df-e79d88c636d8.jpg[/img]
性质:
(1)最大堆的根结点是堆中值最大的数据元素,最小堆的根结点是堆中值最小的数据元素,我们称堆的根结点元素为堆顶元素。
(2)对于最大堆,从根结点到每个叶结点的路径上,数据元素组成的序列都是递减有序的;对于最小堆,从根结点到每个叶结点的路径上,数据元素组成的序列都是递增有序的。
堆排序的基本思想是:
循环执行如下过程直到数组为空:
(1)把堆顶a[0]元素(为最大元素)和当前最大堆的最后一个元素交换;
(2)最大堆元素个数减1;
(3)由于第(1)步后根结点不再满足最大堆的定义,所以调整根结点使之满足最大堆的定义
[img]/upload/attachment/0113/0898/ae74899e-13a0-3d56-bc7b-0db45db69a2a.jpg[/img]
算法分析:
时间效率:O(nlog2n)。因为整个排序过程中需要调用n-1次堆顶点的调整,而每次堆排序算法本身耗时为log2n;
空间效率:O(1)。仅在第二个for循环中交换记录时用到一个临时变量temp。
稳定性: 不稳定。
优点:对小文件效果不明显,但对大文件有效。
交换排序
基本思想是:利用交换数据元素的位置进行排序的方法。
交换排序的主要算法有:
1)冒泡排序
2)快速排序
1.冒泡排序
基本思想:每趟不断将数据元素两两比较,并按“前小后大”(或“前大后小”)规则交换。
优点:每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;一旦下趟没有交换发生,还可以提前结束排序。
[img]/upload/attachment/0113/0900/cf896ab7-4c48-3d9f-a14c-323db1263044.jpg[/img]
算法分析:
[img]/upload/attachment/0113/0902/a7c9cd6c-bf60-35b9-9edd-bcc902aa9918.jpg[/img]
2 .快速排序
基本思想:从待排序列中任取一个元素 (例如取第一个) 作为中心,所有比它小的元素一律前放,所有比它大的元素一律后放,形成左右两个子表;然后再对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个。此时便为有序序列了。
优点:因为每趟可以确定不止一个元素的位置,而且呈指数增加,所以特别快。
[img]/upload/attachment/0113/0904/c1d9c633-9fd7-383c-8312-35a968ccc720.jpg[/img]
[img]/upload/attachment/0113/0906/190b51f9-707b-3388-af4b-0acc750cc632.jpg[/img]
算法分析:
时间效率:O(nlog2n) —因为每趟确定的元素呈指数增加
空间效率:O(log2n)—因为递归要用堆栈
稳 定 性: 不 稳 定 —因为有跳跃式交换。
归并排序
归并排序主要是二路归并排序,基本思想是:可以把一个长度为n 的无序序列看成是 n 个长度为 1 的有序子序列 ,首先做两两归并,得到 n / 2 个长度为 2 的有序子序列 ;再做两两归并,…,如此重复,直到最后得到一个长度为 n 的有序序列。
[img]/upload/attachment/0113/0908/ea005034-c50b-3209-b27e-1b77ef27ca36.jpg[/img]
基数排序
其基本思想是:
设待排序的数据元素关键字是m位d进制整数(不足m位的关键字在高位补0),设置d个桶,令其编号分别为0,1,2,…,d-1。首先按关键字最低位的数值依次把各数据元素放到相应的桶中,然后按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素,这样就形成了数据元素集合的一个新的排列,我们称这样的一次排序过程为一次基数排序;再对一次基数排序得到的数据元素序列按关键字次低位的数值依次把各数据元素放到相应的桶中,然后按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素;这样的过程重复进行,当完成了第m次基数排序后,就得到了排好序的数据元素序列。
[img]/upload/attachment/0113/0910/036e03b5-7c2b-3739-91bd-ecfce89af13b.jpg[/img]
基数排序算法进出桶中的数据元素序列满足先进先出的原则,桶实际就是队列。实现基数排序算法时,有基于顺序队列和基于链式队列两种不同的实现方法。
在基于链式队列的基数排序算法中,可以把d个队列设计成一个队列数组(设队列数组名为tub),队列数组的每个元素中包括两个域: front域和rear域。front域用于指示队头,rear域用于指示队尾。当第i(i=0,1,2,…,d-1)个队列中有数据元素要放入时,就在队列数组的相应元素tub[i]中的队尾位置插入一个结点。基于链式队列基数排序算法的存储结构示意图如下图所示。
[img]/upload/attachment/0113/0912/3e0b4c84-d14f-3444-a9b5-17aa1ec5a63f.jpg[/img]
基数排序算法分析
特点:不用比较和移动,改用分配和收集,时间效率高!
时间复杂度为:O (mn)。
空间复杂度:O(n).
稳定性:稳定。(一直前后有序)。
总结:
[img]/upload/attachment/0113/0914/5496bd1b-6279-37a7-9b82-de15a542b3ca.jpg[/img]
java中具体代码实现
冒泡排序 简单选择排序 直接插入排序 希尔排序
import java.util.Random; public class Demo { public static void main(String[] args) { int [] src=createArray(5); System.out.println("********冒泡排序前数组原始数据:"); printArray(src); System.out.println("********冒泡排序后:"); printArray(maopao(src)); src=createArray(5); System.out.println("********简单选择排序前数组原始数据:"); printArray(src); System.out.println("********简单选择排序后:"); printArray(xuanze(src)); src=createArray(5); System.out.println("********直接插入排序前数组原始数据:"); printArray(src); System.out.println("********直接插入排序后:"); printArray(charu(src)); src=createArray(5); System.out.println("********希尔排序前数组原始数据:"); printArray(src); System.out.println("********希尔排序后:"); printArray(shell(src)); } //生成一个乱序的 指定长度的原始数组 public static int[] createArray(int size){ //初始化数组 int [] x=new int[size]; for(int i=0;i<x.length;i++){ //创建一个随机对象 Random r=new Random(); x[i]=r.nextInt(100); } return x; } //打印出数组中的元素 public static void printArray(int [] x){ //如果要打印的数组为null,则不打印 if(x==null){ return ; } for(int i=0;i<x.length;i++){ System.out.println(x[i]); } } //冒泡排序 public static int[] maopao(int [] x){ for(int i=0;i<x.length;i++){ for(int j=i+1;j<x.length;j++){ if(x[i]>x[j]){ int temp=x[j]; x[j]=x[i]; x[i]=temp; } } } return x; } //简单选择排序 public static int[] xuanze(int [] x){ for(int i=0;i<x.length;i++){ int lowerindex=i; //找出最小的一个索引 for(int j=i+1;j<x.length;j++){ if(x[j]<x[lowerindex]){ lowerindex=j; } } //交换 int temp=x[i]; x[i]=x[lowerindex]; x[lowerindex]=temp; } return x; } //直接插入排序 public static int[] charu(int [] x){ for(int i=1;i<x.length;i++){ for(int j=i;j>0;j--){ if(x[j]<x[j-1]){ int temp=x[j]; x[j]=x[j-1]; x[j-1]=temp; } } } return x; } //希尔排序 public static int[] shell(int [] x){ //分组 for(int increment=x.length/2;increment>0;increment/=2){ //每个组内排序 for(int i=increment;i<x.length;i++){ int temp=x[i]; int j=0; for(j=i;j>=increment;j-=increment){ if(temp<x[j-increment]){ x[j]=x[j-increment]; }else { break; } } x[j]=temp; } } return x; } }
快速排序
import java.util.Random; public class kaisu { public static void main(String[] args) { int [] src=createArray(5); System.out.println("********快速排序前数组原始数据:"); printArray(src); System.out.println("********快速排序后:"); printArray(kuaisu(src)); } public static int[] createArray(int size){ //初始化数组 int [] x=new int[size]; for(int i=0;i<x.length;i++){ //创建一个随机对象 Random r=new Random(); x[i]=r.nextInt(100); } return x; } public static void printArray(int [] x){ //如果要打印的数组为null,则不打印 if(x==null){ return ; } for(int i=0;i<x.length;i++){ System.out.println(x[i]); } } public static int[] kuaisu(int [] x){ quick(x); return x; } public static void quick(int [] x){ if(x.length>0){ quicksort(x,0,x.length-1); } } public static void quicksort(int [] x,int low,int high){ //如果不加这个判断递归会无法退出导致堆栈溢出异常 if(low<high){ int middle=getmiddle(x,low,high); quicksort(x,0,middle-1); quicksort(x,middle+1,high); } } public static int getmiddle(int [] x,int low,int high){ int temp=x[low];//基准元素 while(low<high){ //找到比基准元素小的元素位置 while(low<high&&x[high]>=temp){ high--; } x[low]=x[high]; while(low<high&&x[low]<=temp){ low++; } x[high]=x[low]; } x[low]=temp; return low; } }
堆排序
import java.util.Random; public class Heap { public static void main(String[] args) { int [] src=createArray(5); System.out.println("********堆排序前数组原始数据:"); printArray(src); System.out.println("********堆排序后:"); printArray(heap(src)); } public static int[] createArray(int size){ //初始化数组 int [] x=new int[size]; for(int i=0;i<x.length;i++){ //创建一个随机对象 Random r=new Random(); x[i]=r.nextInt(100); } return x; } public static void printArray(int [] x){ //如果要打印的数组为null,则不打印 if(x==null){ return ; } for(int i=0;i<x.length;i++){ System.out.println(x[i]); } } public static int[] heap(int [] x){ //循环建堆 for(int i=0;i<x.length;i++){ //建堆 buildMaxHeap(x,x.length-1-i); //交换堆顶和最后一个元素 swap(x,0,x.length-1-i); } return x; } //对x数组从0到lastIndex建大顶堆 public static void buildMaxHeap(int [] x,int lastindex){ //从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(x[biggerindex]<x[biggerindex+1]){ //biggerIndex总是记录较大子节点的索引 biggerindex++; } } //如果k节点的值小于其较大的子节点的值 if(x[k]<x[biggerindex]){ //交换他们 swap(x,k,biggerindex); //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值 k=biggerindex; }else { break; } } } } //交换 public static void swap(int[] x,int i,int j){ int temp=x[i]; x[i]=x[j]; x[j]=temp; } }
归并排序
import java.util.Random; public class Merge { public static void main(String[] args) { int [] src=createArray(8); System.out.println("********归并排序前数组原始数据:"); printArray(src); System.out.println("********归并排序后:"); int [] tem=sort(src); printArray(tem); } public static int[] createArray(int size){ //初始化数组 int [] x=new int[size]; for(int i=0;i<x.length;i++){ //创建一个随机对象 Random r=new Random(); x[i]=r.nextInt(100); } return x; } public static void printArray(int [] x){ //如果要打印的数组为null,则不打印 if(x==null){ return ; } for(int i=0;i<x.length;i++){ System.out.println(x[i]); } } public static int[] sort(int [] x){ mergesort(x,0,x.length-1); return x; } public static void mergesort(int[] x,int left,int right){ if(left<right){ int middle=(left+right)/2; //对左边进行递归 mergesort(x,left,middle); //对右边进行递归 mergesort(x,middle+1,right); //合并 merge(x,left,middle,right); } } public static void merge(int[] x,int left,int middle,int right){ int [] temArr=new int[x.length]; int mid=middle+1;//右边的起始位置 int tmp=left; int third=left; while(left<=middle&&mid<=right){ //从两个数组中选取较小的数放入中间数组 if(x[left]<=x[mid]){ temArr[third++]=x[left++]; }else { temArr[third++]=x[mid++]; } } //将剩余的部分放入中间数组 while(left<=middle){ temArr[third++]=x[left++]; } while(mid<=right){ temArr[third++]=x[mid++]; } //将中间数组复制回原数组 while(tmp<=right){ x[tmp]=temArr[tmp++]; } } }
直接插入排序 希尔排序 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 基数排序的算法分析和具体实现 ...