排序算法
我们前面已经介绍了 3 种最常见的排序算法:
java 实现冒泡排序讲解
q2016年中元节uicksort 快速排序到底快在哪里?
lectionsort 选择排序算法详解(java 实现)
然而天下排序千千万,今天老马就和大家一起把最常见的几种都学习一遍。
堆排序(英语:heapsort)是指利用堆这种数据结构所设计的一种排序算法。
堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
基础知识 jcip-11-二叉堆
若以升序排序说明,把数组转换成最大堆(max-heap heap),这是一种满足最大堆性质(max-heap property)的二叉树:对于除了根之外的每个节点i, a[parent(i)] ≥ a[i]。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
重复从最大堆取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆维持最大堆性质。
输入图片说明
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子:
输入图片说明
通常堆是通过一维数组来实现的。
在数组起始位置为0的情形中:
则父节点和子节点的位置关系如下:
(01) 索引为i的左孩子的索引是 (2*i+1);
(02) 索引为i的左孩子的索引是 (2*i+2);
(03) 索引为i的父结点的索引是 floor((i-1)/2);
堆节点的访问
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。
堆中定义以下几种操作:
最大堆调整(max heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆(build max heap):将堆中的所有数据重新排序
堆排序(heapsort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
这个图解来自 图解排序算法(三)之堆排序,画的非常漂亮。
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
a. 假设给定无序序列结构如下
输入图片说明
b. 此时我们从最后一个非叶子结点开始(叶子结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。
输入图片说明
c. 找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。
输入图片说明
d. 这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
输入图片说明
此时,我们就将一个无序的序列构造成了一个大顶堆。
将堆顶元素与末尾元素进行交换,使末尾元素最大。
然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
a. 将堆顶元素9和末尾元素4进行交换
输入图片说明
b. 重新调整结构,使其继续满足堆定义
输入图片说明
c. 再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.
输入图片说明
后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
输入图片说明
再简单总结下堆排序的基本思路:
a. 将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b. 将堆顶元素美不胜收什么意思与末尾元素交换,将最大元素”沉”到数组末端;
c. 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
为了和前面的逻辑保持一致,我们暂时依然使用 list 去实现这个堆排序。
packagecom.github.houbb.sort.core.api;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importcom.github.houbb.sort.core.util.innersortutil;importjava.util.list;/***堆排序**@authorbinbin.hou*@since0.0.4*/publicclassheapsortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(heapsort.class);@override@suppresswarnings("all")protectedvoiddosort(list<?>original){finalintmaxindex=original.size()-1;/**第一步:将数组堆化*beginindex=第一个非叶子节点。*从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。*叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。*/intbeginindex=original.size()/2-1;for(inti=beginindex;i>=0;i--){maxheapify(original,i,maxindex);}/**第二步:对堆化数据排序*每次都是移出最顶层的根节点a[0],与最尾部节点位置调换,同时遍历长度-1。*然后从新整理被换到根节点的末尾元素,使其符合堆的特性。*直至未排序的堆长度为0。*/for(inti=maxindex;i>0;i--){innersortutil.swap(original,0,i);maxheapify(original,0,i-1);}}/***调整索引为index处的数据,使其符合堆的特性。**@paramlist列表*@paramindex需要堆化处理的数据的索引*@paramlen未排序的堆(数组)的长度*@since0.0.4*/@suppresswarnings("all")privatevoidmaxheapify(finallistlist,intindex,intlen){intli=(index*2)+1;//左子节点索引intri=li+1;//右子节点索引intcmax=li;//子节点值最大索引,默认左子节点。//左子节点索引超出计算范围,直接返回。if(li>len){return;}//先判断左右子节点,哪个较大。if(ri<=len&&innersortutil.gt(list,ri,li)){cmax=ri;}if(innersortutil.gt(list,cmax,index)){innersortutil.swap(list,cmax,index);//如果父节点被子节点调换,maxheapify(list,cmax,len);//则需要继续判断换下后的父节点是否符合堆的特性。}}}
插入排序(英语:inrtion sort)是一种简单直观的排序算法。
它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序在实现上,通常采用in-place排序(即只需用到 o(1) 的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
从第一个元素开始,该元素可以认为已经被排序取出下一个元素,在已经排序的元素序列中从后向前扫描如果该元素(已排序)大于新元素,将该元素移到下一位置重复步骤3,直到找到已排序的元素小于或者等于新元素的位置将新元素插入到该位置后重复步骤2~5排序
packagecom.github.houbb.sort.core.api;importcom.github.houbb.heaven.annotation.threadsafe;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importcom.github.houbb.sort.core.util.innersortutil;importjava.util.list;/***冒泡排序*@authorbinbin.hou*@since0.0.5*/@threadsafepublicclassinrtsortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(inrtsort.class);@override@suppresswarnings("all")publicvoiddosort(listoriginal){for(inti=1;i<original.size();i++){comparablecurrent=(comparable)original.get(i);intj=i-1;//从后向前遍历,把大于当前元素的信息全部向后移动。while(j>=0&&innersortutil.gt(original,j,current)){//元素向后移动一位original.t(j+1,original.get(j));j--;}//将元素插入到对应的位置original.t(j+1,current);}}}
也称递减增量排序算法,是插入排序的一种更高效的改进版本。
希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好抗美援朝著名战役序的数据操作时,效率高,即可以达到线性排序的效率但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。
这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为o(n^2)的排序(冒泡排序或插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。
而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
一个更好理解的希尔排序实现:将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。
将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size而不是i++)。
例如,假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样:
13149433822559946523452773253910
然后我们对每列进行排序:
10147325231327943339255994658245
将上述四行数字,依序接在一起时我们得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ]。
这时10已经移至正确位置了,然后再以3为步长进行排序:
10147325231327943339255994658245
排序之后变为:
10141325233327255939657345948294
最后以1步长进行排序(此时就是简单的插入排序了)。
donald shell 最初建议步长选择为 n/2 并且对步长取半直到步长达到1。
虽然这样取可以比 o(n^2) 类的算法(插入排序)更好,但这样仍然有减少平均时间和最差时间的余地。
已知的最好步长序列是由 dgewick 提出的(1, 5, 19, 41, 109,…),
另一个在大数组中表现优异的步长序列是(斐波那契数列除去0和1将剩余的数以黄金分割比的两倍的幂进行运算得到的数列):
(1, 9, 34, 182, 836, 4025, 19001, 90358, 428481, 2034035, 9651787, 45806244, 217378076, 1031612713,…)
这里为了简单,我们步长直接选择列表长度的一半,并且依次折半。
importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importcom.github.houbb.sort.core.util.innersortutil;importjava.util.list;/***希尔排序**@authorbinbin.hou*@since0.0.6*/publicclassshellsortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(shellsort.class);@override@suppresswarnings("all")protectedvoiddosort(list<?>original){//步长从大到小for(intstep=original.size()/2;step>0;step/=2){//从第step个元素,逐个执行插入排序for(inti=step;i<original.size();i++){intj=i;while((j-step>=0)&&innersortutil.lt(original,j,j-step)){//执行交换innersortutil.swap(original,j,j-step);if(log.isdebugenabled()){log.debug("step:{},j:{},j-step:{},list:{}",step,j,j-step,original);}//更新步长j-=step;}}}}}
整体实现也不难,大家可以回顾下 插入排序
这里为了便于大家理解,我们特意添加了日志。
是创建在归并操作上的一种有效的排序算法,效率为 o(nlogn)(大o符号)。1945年由约翰·冯·诺伊曼首次提出。
该算法是采用分治法(divide and conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
采用分治法:
分割:递归地把当前序列平均分割成两半。
集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。
实际上代码实现也不难,不过递归多多少少让人看起来不太习惯。
我们后面会结合测试日志,再进行讲解。
packagecom.github.houbb.sort.core.api;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importjava.util.arraylist;importjava.util.list;/***归并排序-递归实现**@authorbinbin.hou*@since0.0.7*/publicclassmergerecursivesortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(mergerecursivesort.class);@override@suppresswarnings("all")protectedvoiddosort(list<?>original){//存放归并的结果//直接将数组填满,避免t出现越界list<?>resultlist=newarraylist<>(original);sortrecursive(original,resultlist,0,original.size()-1);}/***递归排序*@paramoriginallist原始列表*@paramresultlist存放结果的列表*@paramstartix开始*@paramendix结果*@since0.0.7*/@suppresswarnings("all")privatevoidsortrecursive(listoriginallist,listresultlist,intstartix,intendix){//循环结束if(startix>=endix){return;}//找到中间位置,将列表一分为二intmidix=(startix+endix)/2;intleftstart=startix;intleftend=midix;intrightstart=midix+1;intrightend=endix;if(log.isdebugenabled()){log.debug("拆分:ls:{},le:{},rs:{},re:{}",leftstart,leftend,rightstart,rightend);}//递归调用sortrecursive(originallist,resultlist,leftstart,leftend);sortrecursive(originallist,resultlist,rightstart,rightend);if(log.isdebugenabled()){log.debug("操作:ls:{},le:{},rs:{},re:{}",leftstart,leftend,rightstart,rightend);}//这里需要通过k记录一下开始的位置intk=startix;while(leftstart<=leftend&&rightstart<=rightend){//相对小的元素放入到合并空间,并移动指针到下一位置comparableleft=(comparable)originallist.get(leftstart);comparableright=(comparable)originallist.get(rightstart);//左边较小,则放入合并空间if(left.compareto(right)<0){resultlist.t(k++,left);leftstart++;}el{resultlist.t(k++,right);rightstart++;}}//如果列表比较结束,将剩下的元素,全部放入到队列中。while(leftstart<=leftend){resultlist.t(k++,originallist.get(leftstart++));}while(rightstart<=rightend){resultlist.t(k++,originallist.get(rightstart++));}//将结果统一拷贝到原始集合中fo安慰失去亲人的话r(inti=startix;i<=endix;i++){originallist.t(i,resultlist.get(i));}}}
相信很多小伙伴都知道迭代可以使得代码变得简洁,但是会让调试和理解变得复杂。
我们来一起学习一下迭代的实现方式。
原理如下(假设序列共有 n 个元素):
将序列每相邻两个数字进行归并操作,形成 ceil(n/2) 个序列,排序后每个序列包含两/一个元素若此时序列数不是1个则将上述序列再次归并,形成 ceil(n/4) 个序列,每个序列包含四/三个元素重复步骤2,直到所有元素排序完毕,即序列数为1相对递归,这个代码就要显得复杂很多。
不过这种迭代的方式性能更好,实现如下。
packagecom.github.houbb.sort.core.api;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importcom.github.houbb.sort.core.util.innersortutil;importjava.util.arraylist;importjava.util.list;/***归并排序-迭代实现**@authorbinbin.hou*@since0.0.7*/publicclassmergesortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(mergesort.class);@overrideprotectedvoiddosort(list<?>original){//存放归并的结果//直接将数组填满,避免t出现越界list<?>resultlist=newarraylist<>(original);//起始,子序列长度为1。对长度为1的序列进行两两合并intk=1;finalintlength=original.size();while(k<length){mergepass(original,resultlist,k,length);//将原先无序的数据两两归并入归并数组k=2*k;//子序列长度加倍mergepass(resultlist,original,k,length);//将归并数组中已经两两归并的有序序列再归并回数组originalk=2*k;//子序列长度加倍}}/***负责将数组中的相邻的有k个元素的字序列进行归并**@paramoriginal原始列表*@paramresults结果列表*@params子序列长度*@paramlen长度*@since0.0.7*/@suppresswarnings("all")privatestaticvoidmergepass(listoriginal,listresults,ints,intlen){inti=0;//写成(i+2*k-1<len),就会把(i+2*k-1)当做一个整体看待//从前往后,将2个长度为k的子序列合并为1个。//对于序列{3,4,2,5,7,0,9,8,1,6},当k=8的时候,因为i>(len-2*k+1),所以根本没有进入while循环while(i<len-2*s+1){merge(original,results,i,i+s-1,i+2*s-1);//两两归并i=i+2*s;}//将那些“落单的”长度不足两两merge的部分和前面merge起来。//(连接起来之前也是要进行排序的,因此有了下面的merge操作)if(i<len-s+1){merge(original,results,i,i+s-1,len-1);//归并最后两个序列}el{for(intj=i;j<len;j++){//若最后只剩下单个子序列results.t(j,original.get(j));}}}/***将两个有序数组合并成一个有序数组*@paramoriginal原始*@paramresult结果*@paramlow开始*@parammid中间*@paramhigh结束*@since0.0.7*/@suppresswarnings("all")privatestaticvoidmerge(listoriginal,listresult,intlow,intmid,inthigh){intj,k,l;//将记录由小到大地放进temp数组for(j=mid+1,k=low;low<=mid&&j<=high;k++){if(innersortutil.lt(original,low,j)){result.t(k,original.get(low++));}el{result.t(k,original.get(j++));}}//接下来两循环是为了将剩余的(比另一边多出来的个数)放到temp数组中if(low<=mid){for(l=0;l<=mid-low;l++){result.t(k+l,original.get(low+l));}}if(j<=high){for(l=0;l<=high-j;l++){result.t(k+l,original.get(j+l));}}}}
计数排序(counting sort)是一种稳定的线性时间排序算法。
该算法于1954年由 harold h. ward 提出。
通过计数将时间复杂度降到了 o(n)。
packagecom.github.houbb.sort.core.api;importcom.github.houbb.heaven.annotation.threadsafe;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importcom.github.houbb.sort.core.util.innersortutil;importjava.util.arraylist;importjava.util.arrays;importjava.util.list;/***计数排序**@authorbinbin.hou*@since0.0.8*/@threadsafepublicclasscountingsortbasicextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(countingsortbasic.class);@override@suppresswarnings("all")publicvoiddosort(listoriginal){//1.获取最大的元素intmax=integer.min_value;for(objectobject:original){integerinteger=(integer)object;max=math.max(max,integer);}//2.构建count列表int[]counts=newint[max+1];//3.遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。for(objectobject:original){integerinteger=(integer)object;counts[integer]++;}//4.结果构建intindex=0;//遍历计数数组,将计数数组的索引填充到结果数组中for(inti=0;i<counts.length;i++){while(counts[i]>0){//i实际上就是元素的值//从左到右遍历,元素自然也就排序好了。//相同的元素会出现多次,所以才需要循环。original.t(index++,i);counts[i]--;if(log.isdebugenabled()){log.debug("结果数组:{}",original);}}}}}
实际上我们创建一个比最大元素还要大1的数组,只是为了放下所有的元素而已。
但是它有一个缺陷,那就是存在空间浪费的问题。
比如一组数据{101,109,102,110},其中最大值为110,按照基础版的思路,我们需要创建一个长度为111的计数数组,但是我们可以发现,它前面的[0,100]的空间完全浪费了,那怎样优化呢?
将数组长度定为 max-min+1,即不仅要找出最大值,还要找出最小值,根据两者的差来确定计数数组的长度。
importcom.github.houbb.heaven.annotation.threadsafe;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importjava.util.arrays;importjava.util.list;/***计数排序**@authorbinbin.hou*@since0.0.8*/@threadsafepublicclasscountingsortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(countingsort.class);@override@suppresswarnings("all")publicvoiddosort(listoriginal){//1.获取最大、最小的元素intmax=integer.min_value;intmin=integer.max_value;for(objectobject:original){integerinteger=(integer)object;max=math.max(max,integer);min=math.min(min,integer);}//2.构建count列表int[]counts=newint[max-min+1];//3.遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。for(objectobject:original){integerinteger=(integer)object;//元素要减去最小值,再作为新索引counts[integer-min]++;}if(log.isdebugenabled()){log.debug("counts.length:{}",counts.length);}//4.结果构建intindex=0;//遍历计数数组,将计数数组的索引填充到结果数组中for(inti=0;i<counts.length;i++){while(counts[i]>0){//i实际上就是元素的值//从左到右遍历,元素自然也就排序好了。//相同的元素会出现多次,所以才需要循环。//这里将减去的最小值统一加上original.t(index++,i+min);counts[i]--;if(log.isdebugenabled()){log.debug("结果数组:{}",original);}}}}}
这个算法的本质是什么呢?
个人理解只需要保证两点:
(1)每一个元素,都有自己的一个元素位置
(2)相同的元素,次数会增加。
算法的巧妙之处在于直接利用数值本身所谓索引,直接跳过了排序比较;利用技数,解决了重复数值的问题。
这个算法的巧妙之处,同时也是对应的限制:那就是只能直接比较数字。如果是字符串呢?
我最初的想法就是可以使用类似于 hashmap 的数据结构。这样可以解决元素过滤,次数统计的问题。
但是无法解决排序问题。
当然了,如果使用 treemap 就太赖皮了,因为本身就是利用了树进行排序。
我们这里使用 treemap 主要有下面的目的:
(1)让排序不局限于数字。
(2)大幅度降低内存的浪费,不多一个元素,也不少一个元素。
思想实际上依然是技术排序的思想。
packagecom.github.houbb.sort.core.api;importcom.github.houbb.heaven.annotation.threadsafe;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importjava.util.list;importjava.util.map;importjava.util.treemap;/***计数排序-treemap**@authorbinbin.hou*@since0.0.8*/@threadsafepublicclasscountingsorttreemapextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(countingsorttreemap.class);@override@suppresswarnings("all")publicvoiddosort(listoriginal){treemap<comparable,integer>countmap=newtreemap<>();//初始化次数for(objectobject:original){comparablecomparable=(comparable)object;integercount=countmap.get(comparable);if(count==null){count=0;}count++;countmap.put(comparable,count);}/电影房/4.结果构建intindex=0;//遍历计数数组,将计数数组的索引填充到结果数组中for(map.entry<comparable,integer>entry:countmap.entryt()){intcount=entry.getvalue();comparablekey=entry.getkey();while(count>0){//i实际上就是元素的值//从左到右遍历,元素自然也就排序好了。//相同的元素会出现多次,所以才需要循环。original.t(index++,key);count--;}}}}
或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。
每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。
桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间 o(n)。
桶排序是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。
桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效。
桶排序以下列程序进行:
设置一个定量的数组当作空桶子。寻访序列,并且把项目一个一个放到对应的桶子去。对每个不是空的桶子进行排序。从不是空的桶子里把项目再放回原来的序列中。算法
packagecom.github.houbb.sort.core.api;importcom.github.houbb.heaven.annotation.threadsafe;importcom.github.houbb.log.integration.core.log;importcom.github.houbb.log.integration.core.logfactory;importjava.util.arraylist;importjava.util.collections;importjava.util.list;/***桶排序**@authorbinbin.hou*@since0.0.9*/@threadsafepublicclassbucketsortextendsabstractsort{privatestaticfinalloglog=logfactory.getlog(bucketsort.class);@override@suppresswarnings("all")publicvoiddosort(listoriginal){finalintstep=10;//计算最小值intmin=integer.max_value;intmax=integer.min_value;for(objectobject:original){integerinteger=(integer)object;min=math.min(min,integer);max=math.max(max,integer);}//2.计算桶的个数intbucketnum=(max-min)/step+1;;//2.1初始化临时列表list<list<integer>>templist=newarraylist<>(bucketnum);for(inti=0;i<bucketnum;i++){templist.add(newarraylist<integer>());}//3.将元素放入桶中//这里有一个限制:要求元素必须一个左边的桶元素,要小于右边的桶。//这就限制了只能是数字类的比较,不然没有范围的概念for(objecto:original){integerinteger=(integer)o;intindex=(integer-min)/step;templist.get(index).add(integer);}//4.针对单个桶进行排序//可以选择任意你喜欢的算法for(inti=0;i<bucketnum;i++){collections.sort(templist.get(i));}//5.设置结果intindex=0;for(inti=0;i<bucketnum;i++){list<integer>integers=templist.get(i);for(integerval:integers){original.t(index++,val);}}}}
本文发布于:2023-04-05 17:11:52,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/9730d20147a2d36021bded6a62c792aa.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:常用的算法描述方法有哪些(详解这9种算法).doc
本文 PDF 下载地址:常用的算法描述方法有哪些(详解这9种算法).pdf
留言与评论(共有 0 条评论) |