5.希尔排序

思想:希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序的本质是插入排序,同时它也称为缩小增量排序。将记录的下标按照一定的增量dk进行分组,对于每一组的记录我们使用直接插入排序算法。而增量dk的选择初始值一半设置为数组长度的一半,序列{dk,dk/2,dk/4...1},称之为增量序列。

代码实现如下:

public class Test5 {public static void main(String[] args) {int a[]=new int[]{10,9,8,7,6,5,4,3,2,1};System.out.print("排序前的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}Test5 test5=new Test5();int dk=a.length/2; //初始的增量设置为数组长度的一半while(dk>=1){test5.shellSort(a,dk);dk/=2;  //增量序列每次除以2,当增量序列为1时候等同于直接插入排序}System.out.print("排序后的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}}public void shellSort(int []a,int dk){int len=a.length,i,j;for(i=dk;i<len;i++){int temp=a[i];for(j=i-dk;;){while(j>=0 && a[j]>temp){a[j+dk]=a[j];j=j-dk;}a[j+dk]=temp;break;}}}
}

排序前的结果为: 10 9 8 7 6 5 4 3 2 1

排序后的结果为: 1 2 3 4 5 6 7 8 9 10

该代码与直接插入排序的代码几乎相同,注意增量dk每次要除以2,用上递归即可解决。

结论:在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。希尔排序与直接插入排序一样,最好的时间复杂度为,最坏的时间复杂度是。而希尔排序的平均时间复杂度是, 1<s<2, s根据所选的分组而定。希尔排序同直接插入排序一样,不需要借助额外的辅助空间,空间复杂度为

6.归并排序

思想:归并排序法是将两个(或者两个以上)有序表合成新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

代码如下:

public class Test6 {public static void main(String[] args) {int a[]=new int[]{10,9,8,7,6,5,4,3,2,1};System.out.print("排序前的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}Test6 test6=new Test6();test6.mergeSort(a,0,a.length-1);System.out.print("排序后的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}}public void mergeSort(int a[],int l,int r){if(l<r){//当子序列中只有一个元素时结束递归int m=(l+r)/2;//划分为两个子序列mergeSort(a, l, m);//对左侧子序列进行递归排序mergeSort(a, m+1, r);//对右侧子序列进行递归排序merge(a,l, m, r);//合并}}public void merge(int a[] ,int l,int m,int r){//两个排好序的子序列合并为一个子序列int temp[]=new int[a.length];//辅助数组int p1=l,p2=m+1;int k=l;while(p1<=m && p2<=r){if(a[p1]>a[p2]){temp[k++]=a[p2++];}else{temp[k++]=a[p1++];}}while(p1<=m){  //左边的序列没有合并完temp[k++]=a[p1++];}while(p2<=l){  //右边的序列没有合并完temp[k++]=a[p2++];}for(int i=l;i<=r;i++){ //将合并好有序的数组复制到原数组中去a[i]=temp[i];}}
}

排序前的结果为: 10 9 8 7 6 5 4 3 2 1

排序后的结果为: 1 2 3 4 5 6 7 8 9 10

结论:归并排序是一个稳定的排序算法,并且最好最坏平均时间复杂度均为,由于排序过程时候需要额外的数组空间,故其空间复杂度为

7.桶排序

思想:把数组a划分为n个相同大小的相同的子区间(桶),每个子区间各自排序,最后合并。计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶只有一个元素的情况。

代码如下:

import java.util.ArrayList;
import java.util.Collections;public class Test7 {public static void main(String[] args) {int a[]=new int[]{21,8,6,11,36,50,27,42,0,12};System.out.print("排序前的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}Test7 test7=new Test7();test7.bucketSort(a);System.out.print("排序后的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}}public void bucketSort(int a[]){int min=Integer.MAX_VALUE;int max=Integer.MIN_VALUE;int len=a.length;for(int i=0;i<len;i++){ //获得数组中的最大值和最小值max=Math.max(max,a[i]);min=Math.min(min,a[i]);}int bucketNum=(max-min)/len+1; //注意一定要加1ArrayList<ArrayList<Integer>> bucketArr=new ArrayList<>(bucketNum);for(int i=0;i<bucketNum;i++){bucketArr.add(new ArrayList<Integer>());}for(int i=0;i<len;i++){int num=(a[i]-min)/len;  //每个元素应该放在桶中的位置bucketArr.get(num).add(a[i]);}//对某个桶进行排序for (int i=0;i<bucketArr.size();i++){Collections.sort(bucketArr.get(i));}int count=0;for (int i=0;i<bucketArr.size();i++){ //将桶内元素复制到数组中for(int j=0;j<bucketArr.get(i).size();j++)a[count++]=bucketArr.get(i).get(j);}}
}

排序前的结果为: 21 8 6 11 36 50 27 42 0 12

排序后的结果为: 0 6 8 11 12 21 27 36 42 50

结论:N为关键字的数量,M为桶的数量。桶排序的平均时间复杂度为线性的O(N+C),其中C=N*(logN-logM)。如果相对于同样的N,桶数量M越大,其效率越高,N=M时,即极限情况下每个桶只有一个数据时。桶排序的最好效率能够达到O(N)。桶排序的空间复杂度为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。

8.计数排序

思想:找出待排序的数组中最大和最小的元素,并且统计数组中每个值为i的元素出现的次数,存入数组a的第i项,对所有的计数累加(从a中的第一个元素开始,每一项和前一项相加),最后将每个元素i放在新数组的第a(i)项,每放一个元素就将a(i)减去1。

代码如下:

public class Test8 {public static void main(String[] args) {int a[]=new int[]{21,8,6,11,36,50,27,42,0,12};System.out.print("排序前的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}Test8 test8=new Test8();test8.countSort(a);System.out.print("排序后的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}}public void countSort(int a[]){//1.得到数列的最大值和最小值,并算出差值dint max =a[0];int min =a[0];for(int i=1; i<a.length; i++) {if(a[i] > max) {max = a[i];}else if(a[i] < min) {min = a[i];}}//2.创建统计数组并统计每个元素的个数,注意统计数组的长度int[]countArray = new int[max-min+1];for(int i=0; i<a.length; i++) {countArray[a[i]-min]++;}//3.统计数组做变形,后面的元素等于前面的元素之和int sum = 0;for(int i=0;i<countArray.length;i++) {sum += countArray[i];countArray[i] = sum;}//4.倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组int[] sortedArray = new int[a.length];for(int i=a.length-1;i>=0;i--) {sortedArray[countArray[a[i]-min]-1]=a[i];countArray[a[i]-min]--;}//5.将排完序的数组赋值到原数组里for(int i=0;i<a.length;i++){a[i]=sortedArray[i];}}
}

排序前的结果为: 21 8 6 11 36 50 27 42 0 12

排序后的结果为: 0 6 8 11 12 21 27 36 42 50

结论:计数排序是一个非基于比较的排序算法,假如数列的原始规模是n,最大最小整数的差值是m,则它的时间复杂度为,快于任何比较排序算法,当然这是一种牺牲空间换取时间的做法。只考虑统计数组的大小的话,它的空间复杂度为。同时计数排序算法是一个稳定的排序算法。

9.基数排序(桶子法)

思想:现将所有比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行依次排序。这样从最低位排序一直到最高位排序完成后,数列就变成了一个有序序列。

import java.util.ArrayList;public class Test9 {public static void main(String[] args) {int a[]=new int[]{21,80,6,1100,36,50,27,42,340,0,12};System.out.print("排序前的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}Test9 test9=new Test9();test9.radixSort(a);System.out.print("排序后的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}}public void radixSort(int a[]){//确定排序的趟数,即最大整数的位数int max=a[0];int len=a.length;for(int i=1;i<len;i++){if(a[i]>max){max=a[i];}}int time=String.valueOf(max).length();ArrayList<ArrayList<Integer>>queue=new ArrayList<>(10);for (int i=0;i<10;i++){queue.add(new ArrayList<>());}//进行time次分配和收集元素for(int i=0;i<time;i++){//分配数组元素for(int j=0;j<a.length;j++){//得到数字的第i+1位数int x=a[j]%(int)Math.pow(10,i+1)/(int)Math.pow(10,i);ArrayList<Integer>queue1=queue.get(x);queue1.add(a[j]);queue.set(x,queue1);}int count=0;//元素计数器//收集队列元素for(int k=0;k<10;k++){while (queue.get(k).size()>0){ArrayList<Integer>queue2=queue.get(k);a[count++]=queue2.get(0); //将队列中第i+1次排完序的结果赋值给数组queue2.remove(0);}}}}
}

排序前的结果为: 21 80 6 1100 36 50 27 42 340 0 12

排序后的结果为: 0 6 12 21 27 36 42 50 80 340 1100

结论:基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。时间效率 :设待排序列为n个记录,d个关键码(d表示最大数字的位数),关键码的取值范围为r,则进行链式基数排序的时间复杂度为O(d(n+r)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(r),共进行d趟分配和收集。 空间效率:需要2*r个指向队列的辅助空间,以及用与静态链表的n个指针。此外,基数排序是稳定的排序算法。

10.堆排序

思想:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后去掉该最大元素,将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次大值。如此反复执行,便能得到一个有序序列了。

代码如下:

public class Test10 {public static void main(String[] args) {int a[]=new int[]{21,80,6,1100,36,50,27,42,340,0,12};System.out.print("排序前的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}Test10 test10=new Test10();test10.heapSort(a);System.out.print("排序后的结果为: ");for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}}public void heapSort(int[] a) {int len=a.length;//索引从0开始,最后一个非叶子结点的下标是len/2-1for(int i=len/2-1;i>=0;i--){adjustHeap(a,i,len);}//建堆结束之后,开始调整堆for(int j=len-1;j>0;j--){//把大根堆的根元素与堆最后一个元素交换位置swap(a,0,j);//每一次的堆调整之后,都会有一个元素到达自己的最终位置adjustHeap(a,0,j);}}//交换元素public void swap(int a[],int i,int j){int temp=a[i];a[i]=a[j];a[j]=temp;}//i为要调整的根结点的元素下标,len为要调整的那部分的数组长度public void adjustHeap(int a[],int i,int len){//先取出当前元素int temp=a[i];//2*i+1为左子树i的左子树for(int k=2*i+1;k<len;k=2*k+1){//让k指向子节点中最大的结点if(k+1<len && a[k]<a[k+1]){k++;}if(a[k]>temp){swap(a,i,k);i=k;}else{break;}}}
}

排序前的结果为: 21 80 6 1100 36 50 27 42 340 0 12

排序后的结果为: 0 6 12 21 27 36 42 50 80 340 1100

结论:堆排序是一种不稳定的排序算法,它的最好最坏以及平均的时间复杂度均为,由于不需要借助额外的辅助空间,它的空间复杂度为

一次搞懂所有排序算法(二)相关推荐

  1. 一次搞懂所有排序算法(一)

    注:这里所有的排序均为从小到大排序. 算法的稳定性:假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面:并且排序之后,a[i]仍然在a[j]前面.则这个排序算法是稳定的. 1.直 ...

  2. 一文搞懂BPE分词算法

    大家好,我是Xueliang,又和大家见面了. 我最近在打机器翻译的一个比赛,主要使用基于BERT的模型.在这其中,一个小的知识点引起了我的好奇,就是在将英语训练语料输入到BERT模型之前,需要对其进 ...

  3. 数据结构和算法之五:排序算法二

    数据结构基础之排序算法二 学习算法,排序算法当然是不能少的,这次我们来学习一下基础的选择排序,冒泡排序,以及大名鼎鼎的快速排序. 选择排序 选择排序,非常好理解,就是找最小的数放到第一位,然后从第二个 ...

  4. 算法设计与分析——十大经典排序算法二(6--10)

    一个不知名大学生,江湖人称菜狗 original author: jacky Li Email : 3435673055@qq.com  Time of completion:2023.3.1 Las ...

  5. 最短路算法的证明_彻底搞懂最短路算法

    只想说:温故而知新,可以为师矣.我大二的<数据结构>是由申老师讲的,那时候不怎么明白,估计太理论化了(ps:或许是因为我睡觉了):今天把老王的2011年课件又看了一遍,给大二的孩子们又讲了 ...

  6. 排序算法二:归并排序(Merge sort)

    归并排序(Merge sort)用到了分治思想,即分-治-合三步,算法平均时间复杂度是O(nlgn). (一)算法实现 1 private void merge_sort(int[] array, i ...

  7. 一文搞定选择排序算法

    一.选择排序 本次内容概要: 1.选择排序原理 选择排序是一种比较简单而且直观的排序算法,它的工作原理是每一次从待排序的数据元素中选出最小或者最大的元素,存放在序列的起始位置,直到全部待排序的数据元素 ...

  8. 常用排序算法二 希尔-鸡尾酒-堆-桶-基数排序(C++)

    第二弹(C++)!!! 以下排序均考虑正负数...代码写得好难看 接着上次说~ 这次我主要写思想和放代码... - 希尔排序 - 鸡尾酒/双向冒泡排序 - 堆排序 - 桶排序 - 基数排序 Talk ...

  9. 黄金百战穿金甲,搞懂数据结构与算法!!!送福利

    *文末有 1 元解锁专栏福利 今天聊聊掌握了不一定能拿到大厂 Offer,但不掌握一定进不去大厂的神技「数据结构与算法」. 为什么突然提到了数据结构与算法呢?这要从一个朋友的吐槽开始. 我这位朋友一心 ...

最新文章

  1. 【MATLAB】变量 ( 变量引入 | 变量类型 )
  2. 产品「工具人」如何破局?
  3. 基于以太坊的去中心化宠物商店构建教程
  4. Page 56 WPF布局原则
  5. Linux本地yum源配置以及使用yum源安装gcc编译环境
  6. JSF 2,PrimeFaces 3,Spring 3和Hibernate 4集成项目
  7. 如何生成高性能的短链接?
  8. 一款简洁大气的商城官网介绍源码
  9. 新的开始 | Arthas GitHub Star 破万后的回顾和展望
  10. PyQt4开发环境搭建指导
  11. vs2015 + opencv3.4.0 + qt msvc2015_64-5.7.1 显示图像
  12. python psutil模块怎么在线下载_python之psutil模块
  13. 动态绑定OnClientClick的值
  14. 12-ubuntu:010 Editor
  15. 空手套白狼高手,一个小姑娘只用3天净赚5万,值得你去深思!
  16. Keil5 平台 S3C2440裸机程序开发环境配置
  17. patreon cg人物插画作品合集分享
  18. (PS教程)01-用PS给证件照排版
  19. 第3讲 旋转向量、欧拉角、四元数
  20. 51nod3148 松鼠聚会

热门文章

  1. RDKit toolkit实战三:描述符计算及可视化
  2. 其他算法-Dijkstra
  3. 决策树算法(四)——选取最佳特征划分数据集
  4. mysql表分区占用存储_MySQL 分区分表应用场景分析和分区中可能遇到的坑点
  5. struts2拦截器_Struts2 学习笔记(二)
  6. 零基础入门学习Python(14)-格式化字符串
  7. QIIME 2教程. 29参考数据库DataResources(2021.2)
  8. MPB:扬州大学王梦芝组-​​反刍动物瘤胃原虫的分离培养与形态学分析
  9. R语言可视化、编写自定义函数可视化水平排序条形图(horizontal bar plot)、自定义图像布局模仿经济学人杂志可视化效果、右侧添加标签数值图像方框、自定义背景色、水平条形图中间线条等
  10. seaborn使用boxplot函数进行箱图可视化(使用色彩调色板自定义设置箱图的颜色、在boxplot函数内设置palette参数自定义调色板)