城市直播房产教育博客汽车
投稿投诉
汽车报价
买车新车
博客专栏
专题精品
教育留学
高考读书
房产家居
彩票视频
直播黑猫
投资微博
城市上海
政务旅游

高级排序算法之快速排序

2月2日 虎狼旗投稿
  前言
  今天继续算法学习,本次学习的是高级排序之快速排序。本文代码部分存在调用公共方法,可在文章:简单排序算法之冒泡、插入和选择排序Java实现版,高级排序之归并排序、希尔排序。中查找相关方法,另外,本文也提供测试时使用的完整代码,对其他算法不感兴趣,可在文末找到完整源代码。快速排序
  快速排序的本质就是把一个数组划分为两个子数组,然后递归地调用自身为每一个数组进行快速排序来实现的。这里就涉及一个问题:如何划分?
  在快速排序中,为了划分数组,提出了枢纽这个词,它代表待排序序列中的一个数据项。快速排序利用枢纽将数组划分成两部分,一部分(枢纽左侧)是所有小于该枢纽表示的数据项,另一部分(枢纽右侧)则都大于该枢纽表示的数据项(注意,此时左右两侧的数据项是无序的),枢纽放置在左右两侧的中间,此时该枢纽已经处在排序的正确位置上了(枢纽是快速排序所排序的对象,实现排序的关键点就是调整枢纽的位置)。通过递归的这样划分,最终出现左侧只有一个数据项(该数据项既是左侧子数组又是枢纽),则结束左侧递归,开始划分右侧数组以此类推。这里又产生了一个问题,如何选择枢纽?
  选择枢纽的算法:最简单的选择枢纽算法就是每次递归都选择数组的最后一个数据项做为枢纽(或者选择最左侧的数据项作枢纽)。
  上图演示了以子数组最右侧数据项做枢纽的排序过程代码演示(枢纽始终使用子数组的最右侧数据项)privatestaticintpartitionIt(intleft,intright,intpivot,int〔〕array){intleftPtrleft1;intrightPwhile(true){查找左侧子数组大于枢纽的位置while(compare(array〔leftPtr〕,pivot)0){}查询右侧子数组小于枢纽的位置while(rightPtr0compare(array〔rightPtr〕,pivot)0){}if(leftPtrrightPtr){}交换需要调整位置的两个数据项swap(leftPtr,rightPtr,array);}将枢纽挪到两侧中间swap(leftPtr,right,array);returnleftP}privatestaticvoidrecQuickSort(intleft,intright,int〔〕array){if(rightleft0){}选择的枢纽intpivotarray〔right〕;intpartitionpartitionIt(left,right,pivot,array);recQuickSort(left,partition1,array);recQuickSort(partition1,right,array);}publicstaticvoidquickSort(int〔〕array){compareCount0;swapCount0;longstartnewDate()。getTime();recQuickSort(0,array。length1,array);longendnewDate()。getTime();printResult(快速排序,交换次数,start,end);}运行结果
  冒泡排序比较次数:49995000,交换次数:25153125,耗时:173毫秒
  选择排序比较次数:49995000,交换次数:9987,耗时:65毫秒
  插入排序比较次数:25075792,复制次数:25075793,耗时:42毫秒
  归并排序比较次数:120459,复制次数:267232,耗时:3毫秒
  希尔排序比较次数:233896,复制次数:238266,耗时:5毫秒
  对随机序列排序:快速排序比较次数:165181,交换次数:31700,耗时:3毫秒
  对逆序序列排序:快速排序比较次数:49825881,交换次数:9976,耗时:54毫秒
  从运行结果中,可以看到对于随机序列,快速排序算法执行速度是非常快的,和归并排序相同,但给逆序序列排序时,效果则非常差,几乎和选择排序一样的效率了。那这是为什么呢?
  根本原因,就在于枢纽的选择上,快速排序期望枢纽能够将数组拆分成两个长度几乎相同的子数组,这样能最优的平衡比较次数和交换次数。对于随机序列,简单的选择最右侧数据项做为枢纽不至于频繁出现左右子数组极度不平衡的情况,而对于逆序序列,则几乎每次划分都是极度不平衡的两个子数组,最终导致较大侧的子数组要被划分更多次。优化枢纽选择算法
  快速排序的最优划分结果是划分的两个数组长度相等,为了达到这个目的,我们每次都要在划分前,先找到子数组的中间数据项吗?显然,不能这么做的,因为很可能你找中值的时间远远大于你排序的时间了。所以,我们只能选择一个实现既不是过于复杂,又比选择最右侧数据项做为枢纽的算法更具普适性。
  上图所示取枢纽的方法叫三数据项取中,即从数组中选择第一个、最后一个以及中间的数据项,从这三个数据项中取出大小在中间的项做为枢纽,在选择过程中,我们实际上也对这三个数据项做了排序,那顺便把分别大于、小于枢纽的那两个数据项也放到正确的位置(即左侧小于枢纽,右侧大于枢纽)。
  该方法每次划分都需要至少有三个数据项,所以当子数组项数不大于3个的时候,就可以结束递归划分了,此时的排序可以通过其他排序算法实现,如使用手动排序(待排序的数据项不大于3个,所以手动排序完全可以轻松搞定),也可以使用插入排序(如果使用插入排序,我们甚至可以当数据项不大于10(这个10没有具体意义,你也可以20、30)的时候就可以用插入排序来收尾了)。下面我们用该方法来选择枢纽(用插入排序来收尾),对代码进行修改。代码演示privatestaticintmedianOf3(intleft,intright,int〔〕array){intcenter(leftright)2;if(array〔left〕array〔center〕){swap(left,center,array);}if(array〔left〕array〔right〕){swap(left,right,array);}if(array〔center〕array〔right〕){swap(center,right,array);}swap(center,right1,array);returnarray〔right1〕;}privatestaticvoidinsertSort(intleft,intright,int〔〕array){for(intileft1;i){inttemparray〔i〕;右移数字,实现cur下标的左侧都是有序的while(cur0compare(temp,array〔cur1〕)0){copy(cur1,cur,array);}如果curi则说明数据项已经在正确的位置了,不需要进行交换if(cur!i){不满足temp0){}if(leftPtrrightPtr){}交换需要调整位置的两个数据项swap(leftPtr,rightPtr,array);}将枢纽挪到两侧中间swap(leftPtr,right1,array);returnleftP}privatestaticvoidrecQuickSort(intleft,intright,int〔〕array){intsizerightleft1;if(size10){insertSort(left,right,array);}else{intmedianmedianOf3(left,right,array);intpartitionpartitionIt(left,right,median,array);recQuickSort(left,partition1,array);recQuickSort(partition1,right,array);}}publicstaticvoidquickSort(int〔〕array){compareCount0;swapCount0;longstartnewDate()。getTime();recQuickSort(0,array。length1,array);longendnewDate()。getTime();printResult(快速排序,交换次数,start,end);}运行结果
  冒泡排序比较次数:49995000,交换次数:25138570,耗时:170毫秒
  选择排序比较次数:49995000,交换次数:9990,耗时:65毫秒
  插入排序比较次数:25069178,复制次数:25069176,耗时:34毫秒
  归并排序比较次数:120483,复制次数:267232,耗时:2毫秒
  希尔排序比较次数:231598,复制次数:235991,耗时:6毫秒
  对随机序列排序:快速排序比较次数:154857,交换次数:44570,耗时:3毫秒
  对逆序序列排序:快速排序比较次数:188034,交换次数:20067,耗时:1毫秒
  从执行结果可以看出,优化过枢纽选择算法后,无论是随机序列排序还是逆序序列排序,排序速度都非常快。
  至此,本文结束完整代码packageteam。ngup。importjava。util。Aimportjava。util。Dimportjava。util。concurrent。ThreadLocalRauthorzwwdate20228410:35publicclassSortStudy{staticfinalintARRAYSIZE10000;staticintcompareCount0;staticintswapCount0;生成随机数数组returnpublicstaticint〔〕buildRandomArray(){int〔〕arraynewint〔ARRAYSIZE〕;for(inti0;iARRAYSIZE;i){intrandomWithThreadLocalRandomThreadLocalRandom。current()。nextInt(0,1000000);array〔i〕randomWithThreadLocalR}}a和b位置的数据交换paramaparambparamarraypublicstaticvoidswap(inta,intb,int〔〕array){swapCinttemparray〔a〕;array〔a〕array〔b〕;array〔b〕}复制位置abparamaparambparamarrayprivatestaticvoidcopy(inta,intb,int〔〕array){swapCarray〔b〕array〔a〕;}复制数值a位置bparamaparambparamarrayprivatestaticvoidcopyData(inta,intb,int〔〕array){swapCarray〔b〕a;}dataa大于datab返回1,否则返回1paramdataaparamdatabreturnpublicstaticintcompare(intdataa,intdatab){compareCif(dataadatab){return1;}return1;}输出排序结果paramname排序方法名paramoperName交换复制paramstart开始时间paramend结束时间privatestaticvoidprintResult(Stringname,StringoperName,longstart,longend){System。out。print(name比较次数:compareCount,operName:swapCount,耗时:(endstart)毫秒);}冒泡排序publicstaticvoidmaopao(int〔〕array){compareCount0;swapCount0;待排序序列长度,intlengtharray。longstartnewDate()。getTime();while(length0){for(inta0;alength1;a){if(compare(array〔a〕,array〔a1〕)0){交换位置swap(a,a1,array);}}一次从头到尾的遍历,找出了最右端的值(最大值),这将缩短第二次遍历的序列长度}longendnewDate()。getTime();输出排序结果printResult(冒泡排序,交换次数,start,end);}选择排序publicstaticvoidxuanze(int〔〕array){compareCount0;swapCount0;intlength0;longstartnewDate()。getTime();while(length!array。length1){最小值位置intminPosition1;最小值intminarray〔length〕;for(intilength1;iarray。i){if(compare(array〔i〕,min)0){minarray〔i〕;minP}}存在比当前值还要小的值,则进行位置对换,否则无需对调位置if(minPosition!1){交换位置swap(length,minPosition,array);}}longendnewDate()。getTime();printResult(选择排序,交换次数,start,end);}插入排序publicstaticvoidcharu(int〔〕array){swapCount0;compareCount0;第一次排序无需比较,所以直接从1开始longstartnewDate()。getTime();for(inti1;iarray。i){inttemparray〔i〕;右移数字,实现cur下标的左侧都是有序的while(cur0compare(temp,array〔cur1〕)0){copy(cur1,cur,array);}如果curi则说明数据项已经在正确的位置了,不需要进行交换if(cur!i){不满足temp0){for(outerh1compare(array〔innerh〕,temp)0){copy(innerh,inner,array);}copyData(temp,inner,array);}h(h1)3;}longendnewDate()。getTime();printResult(希尔排序,复制次数,start,end);}快速排序privatestaticintmedianOf3(intleft,intright,int〔〕array){intcenter(leftright)2;if(compare(array〔left〕,array〔center〕)0){swap(left,center,array);}if(compare(array〔left〕,array〔right〕)0){swap(left,right,array);}if(compare(array〔center〕,array〔right〕)0){swap(center,right,array);}swap(center,right1,array);returnarray〔right1〕;}privatestaticvoidinsertSort(intleft,intright,int〔〕array){for(intileft1;i){inttemparray〔i〕;右移数字,实现cur下标的左侧都是有序的while(cur0compare(temp,array〔cur1〕)0){copy(cur1,cur,array);}如果curi则说明数据项已经在正确的位置了,不需要进行交换if(cur!i){不满足temp0){}if(leftPtrrightPtr){}交换需要调整位置的两个数据项swap(leftPtr,rightPtr,array);}将枢纽挪到两侧中间swap(leftPtr,right1,array);returnleftP}privatestaticvoidrecQuickSort(intleft,intright,int〔〕array){intsizerightleft1;if(size10){insertSort(left,right,array);}else{intmedianmedianOf3(left,right,array);intpartitionpartitionIt(left,right,median,array);recQuickSort(left,partition1,array);recQuickSort(partition1,right,array);}}publicstaticvoidquickSort(int〔〕array){compareCount0;swapCount0;longstartnewDate()。getTime();recQuickSort(0,array。length1,array);longendnewDate()。getTime();printResult(快速排序,交换次数,start,end);}publicstaticvoidmain(String〔〕args){int〔〕arraybuildRandomArray();int〔〕array2Arrays。copyOf(array,ARRAYSIZE);int〔〕array3Arrays。copyOf(array,ARRAYSIZE);int〔〕array4Arrays。copyOf(array,ARRAYSIZE);int〔〕array5Arrays。copyOf(array,ARRAYSIZE);int〔〕array6Arrays。copyOf(array,ARRAYSIZE);maopao(array);System。out。println();xuanze(array2);System。out。println();charu(array3);System。out。println();mergeSort(array4);System。out。println();xierSort(array5);System。out。println();随机数据项进行快速排序System。out。print(对随机序列排序:);quickSort(array6);System。out。println();将array6逆序int〔〕array6anewint〔array6。length〕;for(intiarray6。length1,j0;i0;i){array6a〔j〕array6〔i〕;j;}对逆序的数组使用快速排序System。out。print(对逆序序列排序:);quickSort(array6a);}}
  原文格式更佳:高级排序算法之快速排序NGUP的个人技术博客
投诉 评论 转载

离自力更生还有多远?盘一盘近年来国产自研芯片的高光时刻智能手机领域经过长达十五年的发展变迁,现如今俨然已经成为我们绝大多数人日常接触和使用频次最高的消费类电子产品。作为智能手机十几年发展历程的见证者和亲历者,近年来,我们经历了从单……经典游戏天河传说,曾经被誉为国产单机的剧情天花板国产单机游戏史上有这样一部作品,论知名度远不及国产三剑,论质量也不属于出类拔萃的那种。即便如此,在很多玩家的心目中,它的剧情足以被誉为国产单机的天花板。这部作品便是由新瑞……浅谈死亡搁浅和小岛秀夫的内心世界本文作者:星游社红薯友情提示,本文含部分剧透前言《死亡搁浅》发行于20年7月份,是小岛秀夫的全新力作,该作以一种逆流而上的全新玩法赋予了3A游戏新的定义标准。《死亡……一公司要求员工下班前先发手机电量截图,防止上班摸鱼IT之家4月21日消息,4月19日一公司要求员工下班发手机电量截图的话题冲上微博热搜。原来,近日武汉一位网友发帖吐槽,称公司领导发布群公告要求员工下班前将手机电量消耗截图……詹姆斯常规赛30场次达490场,40场次超越杰里韦斯特昨天詹姆斯出场仅29分钟就砍下43分14篮板4助攻2抢断2盖帽的全能数据,成为联盟最年长的能在29分钟内拿到40分10篮板5三分的球员。这样詹姆斯常规赛生涯30场次达到了……美媒欲成为影响未来技术的全球领军者,中国并不掩饰自己的雄心来源:环球时报美国《华尔街日报》12月7日文章,原题:在科技领域,中国很快会领先美国中情局局长伯恩斯10月宣布正在建立两个新任务中心,一个侧重于中国,另一个侧重于前沿技术……7年倒闭6万家,曾经的年轻人聚会圣地,为什么如今无人问津了?文财图说编辑财图说根据天眼查上面的数据显示,截止到今年3月,我们国家现存的KTV企业仅剩6。4万家,相比于2015年的12万家,几乎腰斩。而这仅剩的6。4万家……Faker荣获我独自满级奖,LCK颁奖典礼结束,T1的翻译当前言:S11赛季的比赛已经全部结束了,相信绝大多数的玩家都关注了这段时间的休赛期,lpl赛区和lck赛区的各大战队都进行了很多的人员变动,就在最近,lck赛区的年度颁奖仪式也已……2022最值得入手的十大手机!性价比极高手机应该是我们日常生活中必不可少的智能产品。最开始手机大概是用来通讯的,但是随着科技的进步,现在手机有了很多功能。目前市面上的手机品牌很多。以下是2022年最值得购买的手机清单……高级排序算法之快速排序前言今天继续算法学习,本次学习的是高级排序之快速排序。本文代码部分存在调用公共方法,可在文章:简单排序算法之冒泡、插入和选择排序Java实现版,高级排序之归并排序、希尔排……阿根廷女足梅西是世界第一人!他是聚餐后唯一留下来的男足原创根据8月10日的报道,有阿根廷女足国家队球员FlorenciaBonsegundo接受采访,谈论到自己在阿根廷足坛的大佬梅西。她表示当时阿根廷女足跟男足在各自的世界杯……那个句子,你第一次读就被惊艳到的呢1。很抱歉啊,我白天都没什么机会和你说话,只能憋到晚上给你发句晚安。但你可别小看了这两个字,它可包含着我今天清晨见到的阳光,中午看到的白云,傍晚遇见的微风,包含着我这一天每句想……
还让不让我说话了吃饭快和吃饭慢哪个更好?零投资赚钱项目哪里有让你边玩边挣钱参加聚划算的前期准备工作有哪些亚特兰蒂斯属于几级文明亚特兰蒂斯为什么会消失走进雨季备孕路上的那些事妈妈的爱明星股东陶虹曾分红张庭传销的4。2亿?官方实锤!曾经闺蜜该如口红种草测评推荐涂上气场十足,优雅又复古,还特别的有韵味托捷里卡医生不服不行!CBA菜鸟出道即高峰,七冠教头果然有两把刷子有用的网址一段不值得付出的说说勒布朗詹姆斯生涯100佳比赛(200350寒假记事的主题作文奶嘴吸不住怎么办产后手脚冰凉怎么办产后手脚冰凉怎么调理幼儿园音乐活动教案就任北京大学校长之演说教案实话实说与俺的初恋那也造句用那也造句大全电脑上如何登录两个微信账号?轻轻松松一招搞定不要上当哦

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找