【算法】排序(3),基数排序,排序算法时间复杂度比较

大家好,我是被白菜拱的猪。

一个热爱学习废寝忘食头悬梁锥刺股,痴迷于girl的潇洒从容淡然coding handsome boy。

一、写在前言

我哭了,写的博客没有保存,睡了一觉之后电脑不知道怎么关机了。啊啊啊啊蓝瘦想哭,木有心情bb了,直接上干货,奥利给。

二、排序

(一)基数排序

基数排序是桶排序的扩展,是一种稳定性排序算法,这里稳定性是指:比如小明小红按照身高排队,他俩一样高,本身小红就站在小明前面,假如经过排序之后小红依旧站在小明前面,说明这个排序算法是稳定的,假如变成了小明站在了小红前面,说明这个算法就是不稳定的。下面讲讲具体的基数排序思路。

1、思路讲解

基数排序是将数变成同样的长度(不足的前面补零),然后对每个位进行比较,直到比完搞完,例如 24, 1 ,883, 56这四个数,最高位883有三位,所以要比较三次。然后有十个桶,0-9,位数的数字为几就装进哪个桶,最后遍历桶,在讲桶中的数据取出来继续比较,这样说可能不懂,下面具体演示:

第一次比较个位:24进序号为4的桶,1进序号为1的桶,883进序号为3的桶,56进序号为6的桶,然后按照桶的顺序(0-9)取出数字,为1,883,24,56.

第二次比较十位,1进序号为0的桶,883进序号为8的桶,24进序号为2的桶,56进序号为5的桶,最后按照桶的顺序取出数字,为 1,24,56,883,。

第三次比较百位,这里看似已经排好了,其实还没有,同样道理1,24,56进序号为0的桶,因为他们百位为零,883进序号为8的桶,最后取出数字为1,24,56,883,这样才比较成功。

这里需要注意的是我们要另设一个数组来记录每个桶中元素的个数,这样方便我们遍历。talk is shit,show me the code。

2、代码实现

package com.codingboy.sort;

import java.util.Arrays;

/**
 * @author: ljl
 * @date: 2020/8/15 12:04
 * @description: 基数排序
 */
public class RadixSort {
    public static void main(String[] args) {
        int[] arr1 = {53, 3, 542, 748, 14,1,1,214};
        System.out.println(Arrays.toString(arr1));
        radixSort(arr1);
        System.out.println(Arrays.toString(arr1));

        //下面对八百万条数据进行测试
        int[] arr = new int[8000000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random() * 8000000); //Math.random [0,1)
        }
        long before = System.currentTimeMillis();
        radixSort(arr);
        long after = System.currentTimeMillis();
        System.out.println("八百万条数据测试用时(s):" + (after - before)/1000.0);

    }
    //基数排序
    public static void radixSort(int[] arr) {
        //创建十个桶来装数,即二维数组,避免溢出(arr中所有的数装在一个桶里),长度为arr.length
        int[][] buckets = new int[10][arr.length];
        //创建一个数组存储每个桶的数量是多少,方便日后遍历
        int[] bucketCount = new int[10];

        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if(max < arr[i]) {
                max = arr[i];
            }
        }
        int times = (max+ "").length();
        //要进行几次排序,取决于最大值的长度
        for (int i = 0; i < times; i++) {
            //对数组的每一个数进行遍历
            for (int j = 0;j < arr.length;j++) {
                //第一轮取出个位数,第二轮取出十位数...
                int digit = arr[j] / (int)(Math.pow(10,i)) % 10;
                buckets[digit][bucketCount[digit]] = arr[j];
                //加入之后,计数的数组对应的数量+1
                bucketCount[digit] = bucketCount[digit] + 1;
            }
            //索引用来取元素
            int index = 0;
            //一轮过后将桶中的元素取出放到原先的数组中
            for (int j = 0;j < 10;j++) {
                if (bucketCount[j] != 0) {
                    for(int k = 0;k < bucketCount[j];k++) {
                        arr[index++] = buckets[j][k];
                    }
                    //取出来之后将桶中的数量置为0
                    bucketCount[j] = 0;
                }
            }
        }

    }
}

在这里插入图片描述
好快,八百万都没有一秒,基数排序不是浪的虚名的,但是他是借助空间换取时间,所以非常的耗内存,当我把数据调到八千万时,就出现了内存不足的错误。另外这里这段代码中没有考虑负数的情况,有兴趣的小伙伴们可以试试,思路不变。

三、排序算法时间复杂度比较

在这里插入图片描述
要想变牛逼,这个表是一定要背的,这里一定要记住平均复杂度和稳定性,最差最好可以不用记,还是记不住怎么办?利哥献上马士兵老师的宋词记忆法
在这里插入图片描述
oh耶music,让我们来首hip-hop。选择一个大炮插死你,学统计的基友快归西,~~

四、结束语

快乐的时光总是如此短暂,小伙伴们是否发现堆排序木有讲,堆排序的设计二叉树,二叉树我们现在还不会,后续讲到二叉树我们再来认识认识堆排序。

下面通过我此时的记忆回忆一下学过的排序算法。

  • 选择排序:找到最小值,交换位置。
  • 冒泡排序:两两比较,把大的往后移,像泡泡一样。
  • 插入排序:分成有序表为无序表,无序的元素向有序插入。
  • 快速排序:给基准元素找位置pivot。
  • 归并排序:先分在合并,递归。
  • 希尔排序:对插入进行改进,先分组,在对每组进项插入排序。
  • 基数排序:搞十个桶来装数,然后比较每个位的大小来决定放哪个桶。

还记得大二上数据结构考试要写归并排序流程,一脸懵逼的我实在无从下手,跟隔壁同学大眼瞪小眼,没错我就是那个小眼睛,学了那么多天,妈妈再也不用担心我不会排序啦。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值