Java实现常见排序算法(二)

简介: Java实现常见排序算法(二)

问题描述

上次的博客讨论了排序算法中的插入排序和交换排序两个大类,今天将剩下的常见排序算法全部梳理出来。

选择排序

简单选择排序

基本思想:每一趟排序从待排序的序列中选择出最小的元素,顺序放入到元素序列中,直到排序完成。该算法是一个不稳定的算法并且效率与初始数据顺序无关。

空间复杂度为O(1)

时间复杂度最高,平均,最低都为O(n2)
Java实现:

public static int[] selectInsert(int[] n) {

    int minPos,  temp;

    for (int i =  0; i < n.length; i++) {

        minPos =  i;

        //遍历找出最小数的位置

        for (int  j = i + 1; j < n.length; j++) {

            if  (n[j] < n[minPos]) {

                 minPos = j;

            }

        }

        //如果最小数位置不等于当前位置指针,就交换位置,即把数按照从小到大的顺序依次排列

        if  (minPos != i) {

             temp  = n[i];

            n[i]  = n[minPos];

             n[minPos] = temp;

        }

    }

    return n;

}

堆排序

基本原理:堆排序是一种树形选择排序算法,其原理是将R[1…n]看成一棵完全二叉树的顺序存储结构。利用完全二叉树中双亲节点和孩子结点的关系,在当前无序区中选择关键字最大(最小)的元素构建成大根堆(小根堆)。堆排序的主要流程便是,建立大(小)根堆,然后输出元素(顶部和底部元素进行交换),再调整堆,直到元素全部输出。堆排序是一个不稳定的算法。

堆的定义为:n个关键字序列R[1…n]称为堆,堆通常可以被看做一棵树的数组对象。

当且仅当序列满足R[i≤R[2i]且R[i]≤R[2i+1]时称为该堆为大根堆,其中1≤i≤⌊ n/2⌋

当且仅当序列满足R[i]≥R[2i]且R[i]≥R[2i+1]时称为该堆为小根堆,其中1≤i≤⌊n/2⌋

比如在大根堆中,最大的元素放在根节点中,且对于任一非根节点,它的值小于等于其双亲节点的值。

对于堆排序来说关键在于构造堆,而建堆是一个反复调整筛选的过程。首先从树的最后一个非叶子节点开始调整,按照堆的性质移动元素位置。如下图便是一个大顶推的调整过程。


初始堆建立完成后,就是进行排序操作,排序操作的主要步骤是:以大根堆为例,每一次排序将堆顶元素与堆尾元素进行交换,然后再调用调整堆的算法使除了堆尾元素以外剩下的堆再次调整成一个大根堆,这样循环length-1次就可以将元素调整为从小到大的顺序,完成排序。

空间复杂度为O(1)

时间复杂度的最高,平均,最低都为O(nlog2n)

Java实现:

//堆排序

private static int[] heapSort(int[] n) {

    //建立堆,从最后一个非叶子节点开始

    for (int i =  (n.length - 2) / 2; i >= 0; i--) {

         adjustHeap(n, i, n.length);

    }

    int temp;

    //完成排序,从最后一个元素进行输出,每次循环确定一个元素的位置

    for (int i =  n.length - 1; i >= 1; i--) {

        temp =  n[0];

        n[0] =  n[i];

        n[i] =  temp;

        // 筛选 R[0] 结点,得到i个结点的堆

         adjustHeap(n, 0, i);

    }

    return n;

}

 

//调整堆

public static void adjustHeap(int[] n, int k, int  length) {

    //设置堆顶的值

    int temp =  n[k];

     //从左孩子开始判断

    for (int i =  k * 2 + 1; i <= length - 1; i = 2 * i + 1) {

        //如果左孩子小于右孩子

        if (i + 1  < length && n[i] < n[i + 1]) {

             //取右孩子

             i++;

        }

        //如果堆顶的值大于左右孩子中的较大者,无需调整

        if (temp  >= n[i]) {

             break;

        } else {

            //否则的话,将左右孩子中的较大者换到双亲节点

            n[k]  = n[i];

            //然后将k值更新,方便继续向下调整

            k =  i;

        }

 

    }

    //将原堆顶位置当入最后调整出来的地方

    n[k] = temp;

}

归并排序和基数排序

归并排序

基本思想:“归并”的意思是指将两个或者两个以上的有序表合并成一个新的有序表。假设一个待排序的序列长度为n,首先我们可以将其视为n个有序表,即每个表中元素个数为1,然后我们将n个有序表进行两两归并,得到⌈ \lceil⌈n/2⌉ \rceil⌉个长度为2或者1的有序表然后再再次基础上进行两两归并直到得到一个长度为n的有序表为止。这种方法就称为2路归并排序。归并排序是一个稳定的排序算法。PS.归并排序的发明者是约翰·冯·诺伊曼,其速度仅次于快速排序(平均状况下)

例如我们有一个初始值为[22,11,32,2,12,83,10]的序列,才有2路归并排序

第一趟归并后:[11,22],[2,32],[12,83],[10]

第二趟归并后:[2,11,22,32],[10,12,83]

第三趟归并后:[2,10,11,12,22,32,83]

空间复杂度为O(n)

时间复杂度的最高,平均,最低都为O(nlog2n)

Java实现:

//归并排序

public static int[] mergeSort(int[] n, int low, int  high) {

    int mid =  (low + high) / 2;

    if (low <  high) {

         mergeSort(n, low, mid);

         mergeSort(n, mid + 1, high);

        merge(n,  low, mid, high);

    }

    return n;

}

 

//调整

public static void merge(int[] n, int low, int mid, int  high) {

    int[]  tempArr;

    int i = low,  j = mid + 1, k = 0;

    tempArr =  Arrays.copyOf(n, high - low + 1);

    // 把较小的数先移到新数组中

    while (i  <= mid && j <= high) {

        if (n[i]  < n[j]) {

            tempArr[k++] = n[i++];

        } else {

             tempArr[k++] = n[j++];

        }

    }

    // 把左边剩余的数移入数组

    while (i  <= mid) {

         tempArr[k++] = n[i++];

    }

    // 把右边边剩余的数移入数组

    while (j  <= high) {

         tempArr[k++] = n[j++];

    }

    // 把新数组中的数覆盖nums数组

     System.arraycopy(tempArr, 0, n, low, tempArr.length);

}

基数排序(桶子排序)

基本思想:基数排序是一种不基于比较的排序,而采用多关键字排序思想,即基于关键字各位的大小进行排序,主要使用“分配”和“收集”两种基本操作对单逻辑关键字进行排序。基数排序又分为最高位优先(MSD)排序和最低位优先(LSD)排序。他的主要思想是将待排序的整数按位数切割成不同的数字,然后对每一位的数进行单独的比较,具体做法是:将所有待比较数值统一为同样的数位长度,如有不同位数则在前面进行补零操作,然后从最低位开始,依次进行一次排序,这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

举个栗子:

待排序的数据为[222,123,580,996,965,854,775],使用最低位优先排序

第一趟将最低位进行排序:

[580,222,123,854,965,775,996]

第二趟将中间位进行排序:

[222,123,854,965,775,580,996]

第三趟将最高位进行排序:

[123,222,580,775,854,965,996]

空间复杂度:一趟排序需要的辅助空间为O®(r个队列或桶)用于存放d待排序的数

时间复杂度:O(d(n+r)),d趟的分配和收集,一趟分配为O(n),一趟收集为O®。

Java实现(代码参考百度百科)

//d表示最大的数有多少位

public static int[] radixSort(int[] number, int d) {

    //控制键值排序依据在哪一位

    int k = 0,n =  1,m = 1;

    //数组的第一维表示可能的余数0-9

    int[][] temp  = new int[10][number.length];

    //数组orderp[i]用来表示该位是i的数的个数

    int[] order =  new int[10];

    while (m  <= d) {

        for (int  value : number) {

            int  lsd = ((value / n) % 10);

             temp[lsd][order[lsd]] = value;

            order[lsd]++;

        }

        for (int  i = 0; i < 10; i++) {

            if  (order[i] != 0)

                 for (int j = 0; j < order[i]; j++) {

                     number[k] = temp[i][j];

                     k++;

                }

            order[i]  = 0;

        }

        n *= 10;

        k = 0;

        m++;

    }

    return  number;

}

总结

就此常见的几个排序算法就总结得差不多了,下一次的博客准备写一下JDK8中默认的排序算法是如何实现的以及介绍的这几种排序算法的实际使用案例和场景。


目录
相关文章
|
1月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
69 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
3月前
|
负载均衡 NoSQL 算法
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
这篇文章是关于Java面试中Redis相关问题的笔记,包括Redis事务实现、集群方案、主从复制原理、CAP和BASE理论以及负载均衡算法和类型。
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
|
3月前
|
搜索推荐 算法 Java
手写快排:教你用Java写出高效排序算法!
快速排序(QuickSort)是经典的排序算法之一,基于分治思想,平均时间复杂度为O(n log n),广泛应用于各种场合。在这篇文章中,我们将手写一个Java版本的快速排序,从基础实现到优化策略,并逐步解析代码背后的逻辑。
145 1
|
1月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
105 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
1月前
|
算法 Java Linux
java制作海报一:java使用Graphics2D 在图片上写字,文字换行算法详解
这篇文章介绍了如何在Java中使用Graphics2D在图片上绘制文字,并实现自动换行的功能。
97 0
|
1月前
|
算法 Java 测试技术
数据结构 —— Java自定义代码实现顺序表,包含测试用例以及ArrayList的使用以及相关算法题
文章详细介绍了如何用Java自定义实现一个顺序表类,包括插入、删除、获取数据元素、求数据个数等功能,并对顺序表进行了测试,最后还提及了Java中自带的顺序表实现类ArrayList。
21 0
|
3月前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
63 2
|
3月前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
3月前
|
算法 Java
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
51 6
|
3月前
|
存储 算法 Java
LeetCode经典算法题:打家劫舍java详解
LeetCode经典算法题:打家劫舍java详解
70 2