各种排序算法的分析及java&python实现

排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面讲的排序都是属于内排序。
内排序有可以分为以下几类:(1)、插入排序:直接插入排序、二分法插入排序、希尔排序。(2)、选择排序:简单选择排序、堆排序。(3)、交换排序:冒泡排序、快速排序。(4)、归并排序(5)、基数排序
插入排序
思想
每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置,直到全部插入排序完为止。
1.1 直接插入排序
基本思想
每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。
图示
java代码实现
packagesort;publicclassstraightinsertionsort{publicstaticvoidmain(string[]args){int[]arr={49,38,65,97,76,13,27,49,78,34,12,64,1};system.out.println(排序之前:);printarray(arr);for(inti=0;i=0;j--){if(arr[j]>temp)arr[j+1]=arr[j];elsebreak;}arr[j+1]=temp;}system.out.println();system.out.println(排序之后:);printarray(arr);}privatestaticvoidprintarray(int[]arr){for(intx:arr){system.out.print(x+);}}}
python代码实现
definsertion_sort(a_list):forindexinrange(1,len(a_list)):current_value=a_list[index]#这里不能减1position=index#这里比较的是前一个位置的值whileposition>0anda_list[position-1]>current_value:a_list[position]=a_list[position-1]position-=1a_list[position]=current_valuea_list=[54,26,93,15,77,31,44,55,20]print(a_list)insertion_sort(a_list)print(a_list)
算法分析
直接插入排序是稳定的排序。直接插入排序的平均时间复杂度为o(n^2)。
文件初态不同时,直接插入排序所耗费的时间有很大差异。若文件初态为正序,则每个待插入的记录只需要比较一次就能够找到合适的位置插入,故算法的时间复杂度为o(n),这时最好的情况。若初态为反序,则第i个待插入记录需要比较i+1次才能找到合适位置插入,故时间复杂度为o(n^2),这时最坏的情况。
1.2 二分法插入排序
基本思想二分法插入排序的思想和直接插入一样,只是找合适的插入位置的方式不同,这里是按二分法找到合适的位置,可以减少比较的次数。
图示
java代码实现
packagesort;publicclassinsertionsortbinarysearch{publicstaticvoidmain(string[]args){int[]arr={49,38,65,97,76,13,27,49,78,34,12,64,1};system.out.println(排序之前:);printarray(arr);sort(arr);system.out.println();system.out.println(排序之后:);printarray(arr);}privatestaticvoidsort(int[]arr){for(inti=1;i=left;j--){arr[j+1]=arr[j];}arr[left]=temp;}}privatestaticvoidprintarray(int[]arr){for(intx:arr){system.out.print(x+);}}}
python代码实现
definsertion_sort_binarysearch(a_list):forindexinrange(1,len(a_list)):low=0high=index-1current_value=a_list[index]position=indexwhilelowcurrent_value:high=middle-1else:low=middle+1whileposition>low:a_list[position]=a_list[position-1]position-=1a_list[low]=current_valuea_list=[54,26,93,15,77,31,44,55,20]insertion_sort_binarysearch(a_list)print(a_list)
算法分析
二分法插入排序也是稳定的。
二分插入排序的比较次数与待排序记录的初始状态无关,仅依赖于记录的个数。当n较大时,比直接插入排序的最大比较次数少得多。但大于直接插入排序的最小比较次数。算法的移动次数与直接插入排序算法的相同,最坏的情况为n2/2,最好的情况为n,平均移动次数为o(n2)。
1.3 希尔排序
基本思想
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2temp){arr[j+d]=arr[j];j=j-d;}arr[j+d]=temp;}}system.out.println();system.out.println(排序之后:);printarray(arr);}privatestaticvoidprintarray(int[]arr){for(intx:arr){system.out.print(x+);}}}
python代码实现
defshell_sort(a_list):#howmanysublists,alsohowmanyelementsinasublistsublist_count=len(a_list)//2whilesublist_count>0:forstart_positioninrange(sublist_count):gap_insertion_sort(a_list,start_position,sublist_count)print(afterincrementsofsize,sublist_count,thelistis,a_list)sublist_count=sublist_count//2defgap_insertion_sort(a_list,start,gap):#start+gapisthesecondelementinthissublistforiinrange(start+gap,len(a_list),gap):current_value=a_list[i]position=iwhileposition>=gapanda_list[position-gap]>current_value:a_list[position]=a_list[position-gap]#movebackwardposition=position-gapa_list[position]=current_valuea_list=[54,26,93,17,77,31,44,55,20,88]shell_sort(a_list)print(a_list)
算法分析
我们知道一次插入排序是稳定的,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。
希尔排序的时间性能优于直接插入排序,原因如下:
(1)当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
(2)当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度o(n)和最坏时间复杂度0(n2)差别不大。
(3)在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插人排序有较大的改进。
希尔排序的平均时间复杂度为o(nlogn)。
选择排序
基本思想
每趟从待排序的记录序列中选择关键字最小的记录放置到已排序表的最前位置,直到全部排完。
2.1 直接选择排序
基本思想
在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
图示
java代码实现
packagesort;publicclassselectionsort{publicstaticvoidmain(string[]args){int[]arr={49,38,65,97,76,13,27,49,78,34,12,64,1};system.out.println(排序之前:);printarray(arr);for(inti=0;i 算法分析
简单选择排序是不稳定的排序。
时间复杂度:t(n)=o(n^2)。
2.2 堆排序
基本思想
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
、堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi=0;i--)percedown(arr,i,n);for(inti=n-1;i>=0;i--){inttemp=arr[i];arr[i]=arr[0];arr[0]=temp;percedown(arr,0,i);}}privatestaticvoidpercedown(int[]arr,inti,intn){//i:要替换的元素//n:终止的位置inttemp=arr[i];intchild=i*2+1;while(child python代码实现
defpercedown(a_list,i,n):temp=a_list[i]while(2*i+1) 算法分析
堆排序也是一种不稳定的排序算法。
堆排序优于简单选择排序的原因:直接选择排序中,为了从r[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在r[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可通过树形结构保存部分比较结果,可减少比较次数。
堆排序的最坏[时间复杂度为o(nlogn)。堆序的平均性能较接近于最坏性能。由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
有关堆排序的知识,可以参照另一篇帖子:https://www.jianshu.com/p/541d166ce6c2
交换排序
3.1 冒泡排序
基本思想
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
图示
java代码实现
packagesort;publicclassbubblesort{publicstaticvoidmain(string[]args){int[]arr={49,38,65,97,76,13,27,49,78,34,12,64,1};system.out.println(排序之前:);printarray(arr);for(inti=0;i0andexchanges:exchanges=falseforiinrange(pass_num):ifa_list[i]>a_list[i+1]:a_list[i],a_list[i+1]=a_list[i+1],a_list[i]exchanges=truepass_num-=1
算法分析
冒泡排序是一种稳定的排序方法。
若文件初状为正序,则一趟起泡就可完成排序,排序码的比较次数为n-1,且没有记录移动,时间复杂度是o(n)
若文件初态为逆序,则需要n-1趟起泡,每趟进行n-i次排序码的比较,且每次比较都移动三次,比较和移动次数均达到最大值∶o(n^2)
起泡排序平均时间复杂度为o(n^2)
3.2 快速排序
基本思想选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
图示
java代码实现
packagesort;publicclassquicksort{publicstaticvoidmain(string[]args){int[]arr={49,38,65,97,76,13,27,49,78,34,12,64,1};system.out.println(排序之前:);printarray(arr);quicksort(arr,0,arr.length-1);system.out.println();system.out.println(排序之后:);printarray(arr);}privatestaticvoidquicksort(int[]arr,intstart,intend){if(start python代码实现
defquick_sort(a_list):quick_sort_helper(a_list,0,len(a_list)-1)defquick_sort_helper(a_list,first,last):iffirst 算法分析
归并排序是稳定的排序方法。
归并排序的时间复杂度为o(nlogn)。
速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。
基数排序
基本思想将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
图示
java代码实现
packagecom.sort;//不稳定importjava.util.arrays;publicclassheapsort{publicstaticvoidmain(string[]args){int[]a={49,38,65,97,76,13,27,49,78,34,12,64};intarraylength=a.length;//循环建堆for(inti=0;i=0;i--){//k保存正在判断的节点intk=i;//如果当前k节点的子节点存在while(k*2+1<=lastindex){//k节点的左子节点的索引intbiggerindex=2*k+1;//如果biggerindex小于lastindex,即biggerindex+1代表的k节点的右子节点存在if(biggerindex python代码实现
fromrandomimportrandintdefradix_sort(lis,d):foriinxrange(d):#d轮排序s=[[]forkinxrange(10)]#因为每一位数字都是0~9,故建立10个桶forjinlis:s[j/(10**i)%10].append(i)li=[aforbinsforainb]returnli
算法分析
基数排序是稳定的排序算法。
基数排序的时间复杂度为o(d(n+r)),d为位数,r为基数。
整体分析
6.1 稳定性
稳定:冒泡排序、插入排序、归并排序和基数排序
不稳定:选择排序、快速排序、希尔排序、堆排序
6.2 平均时间复杂度
o(n^2):直接插入排序,简单选择排序,冒泡排序。
在数据规模较小时(9w内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。性能为o(n^2)的算法基本上是相邻元素进行比较,基本上都是稳定的。
o(nlogn):快速排序,归并排序,希尔排序,堆排序。
其中,快排是最好的, 其次是归并和希尔,堆排序在数据量很大时效果明显。
6.3 排序算法的选择
1.数据规模较小
(1)待排序列基本序的情况下,可以选择直接插入排序;(2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
2.数据规模不是很大
(1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(n)的额外空间。(2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
3.数据规模很大
(1)对稳定性有求,则可考虑归并排序。(2)对稳定性没要求,宜用堆排序
4.序列初始基本有序(正序),宜用直接插入,冒泡

管脚呈圆形分布的元器件PCB封装如何设计
Bloodhound使用无源无线电测向技术创建无人机定位无线电信标位置项目
启明信息正式成为CNCF成员 为各行业数字化战略保驾护航
荣耀9什么时候上市?最新消息:华为荣耀9、小米6对比评测!颜值、配置、价格大对决!谁才是国产旗舰王者
速锐得从冷链管理数字网关看工业物联网的发展与创新
各种排序算法的分析及java&python实现
苹果新机人脸识别?然而吃瓜网友只注意到了iphonex忽略了小米note3
蘑菇OS让车变成在路上跑的智能设备
安科瑞远程预付费系统支持微信支付宝多用户计量电表集中安装
苹果,华为各种手机分类,让你丢掉选择犹豫症
Apollo与GPS串口通信的数据格式
为什么采用索雷工艺材料进行电机轴密封位磨损修复
回顾TMR3004磁敏角度传感器的性能介绍和应用
你知道在Linux中fcntl()、lockf、flock的区别?
千元全视屏影音实力派 华为畅享10 Plus震撼上市
小米8评测 到底值不值得购买
小马智行携手合作伙伴推进自动驾驶技术应用落地
编码译码显示实验电路的结构、设计及仿真研究
基于MC33905设计的MCU功率管理方案
什么是RAP