排序算法-桶排序的时间复杂度分析

Posted talk.push

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法-桶排序的时间复杂度分析相关的知识,希望对你有一定的参考价值。

桶排序思想

桶排序,是一种基于非比较的排序方式,时间复杂度O(n),因此它是是属于一种“线性排序”。

思想:桶排序的思想是将一组数据分到几个有序的桶里,每个桶里的数据再单独进行快速排序。每个桶内都排序完成后,再加上本身桶之间已经是有序的,那么按照桶的顺序依次取出每个桶内的数据,最终组成的序列就是有序的。

桶排序的时间复杂度分析:

假如排序数组的元素个数为n,均匀分布在个数为m的桶中,那么每个桶中的元素个数为k=n/m,因为每个桶快速排序的时间复杂度为klogk即n/mlog(n/m),那么m个桶一起就是nlog(n/m),假如桶的个数m跟元素个数n十分接近,那么最终的时间复杂度为O(n).

如果极端情况下,所有的元素n个都分在一个桶里呢?这种情况下时间复杂度就退化成O(nlogn)了。

所以,可以看出桶排序对数据及其分布的要求是十分苛刻的:

1  要求各个桶之间是有序的,这样每个桶排好序之后,才可以直接根据桶的顺序得到最终排序。

2 每个桶之间数据分布式平均的,如果出现上述极端情况,则时间复杂度就会退化为O(nlogn)了。

 

代码

package com.study.algorithm.sort;

/**
 * @Description:桶排序算法
 */
public class BucketSort 

    /**
     * 桶排序
     *
     * @param arr 数组
     * @param bucketSize 桶容量
     */
    public static void bucketSort(int[] arr, int bucketSize) 
        if (arr.length < 2) 
            return;
        

        // 数组最小值
        int minValue = arr[0];
        // 数组最大值
        int maxValue = arr[1];
        for (int i = 0; i < arr.length; i++) 
            if (arr[i] < minValue) 
                minValue = arr[i];
             else if (arr[i] > maxValue) 
                maxValue = arr[i];
            
        

        // 桶数量
        int bucketCount = (maxValue - minValue) / bucketSize + 1;
        int[][] buckets = new int[bucketCount][bucketSize];
        int[] indexArr = new int[bucketCount];

        // 将数组中值分配到各个桶里
        for (int i = 0; i < arr.length; i++) 
            int bucketIndex = (arr[i] - minValue) / bucketSize;
            if (indexArr[bucketIndex] == buckets[bucketIndex].length) 
                ensureCapacity(buckets, bucketIndex);
            
            buckets[bucketIndex][indexArr[bucketIndex]++] = arr[i];
        

        // 对每个桶进行排序,这里使用了快速排序
        int k = 0;
        for (int i = 0; i < buckets.length; i++) 
            if (indexArr[i] == 0) 
                continue;
            
            quickSortC(buckets[i], 0, indexArr[i] - 1);
            for (int j = 0; j < indexArr[i]; j++) 
                arr[k++] = buckets[i][j];
            
        
    

    /**
     * 数组扩容
     *
     * @param buckets
     * @param bucketIndex
     */
    private static void ensureCapacity(int[][] buckets, int bucketIndex) 
        int[] tempArr = buckets[bucketIndex];
        int[] newArr = new int[tempArr.length * 2];
        for (int j = 0; j < tempArr.length; j++) 
            newArr[j] = tempArr[j];
        
        buckets[bucketIndex] = newArr;
    

    /**
     * 快速排序递归函数
     *
     * @param arr
     * @param p
     * @param r
     */
    private static void quickSortC(int[] arr, int p, int r) 
        if (p >= r) 
            return;
        

        int q = partition(arr, p, r);
        quickSortC(arr, p, q - 1);
        quickSortC(arr, q + 1, r);
    

    /**
     * 分区函数
     *
     * @param arr
     * @param p
     * @param r
     * @return 分区点位置
     */
    private static int partition(int[] arr, int p, int r) 
        int pivot = arr[r];
        int i = p;
        for (int j = p; j < r; j++) 
            if (arr[j] <= pivot) 
                swap(arr, i, j);
                i++;
            
        

        swap(arr, i, r);
        return i;
    

    /**
     * 交换
     *
     * @param arr
     * @param i
     * @param j
     */
    private static void swap(int[] arr, int i, int j) 
        if (i == j) 
            return;
        

        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    

 

桶排序的应用场景

桶排序适合用在外部排序中,就是数据存储在外部磁盘上,由于服务器内存有限无法一次性加载到内存中。

比如现在有10G订单数据存在外部磁盘的一个文件中,我们想将这10G的订单数据按照从小到大进行排序,但是由于服务器内存有限只有几百M,无法一次性加载内存,这时候就可以利用桶排序进行解决了。

思路:

可以先找出这批订单的最大值和最小值,比如扫描一遍文件,最大值是10万,最小值是1元,那么我们可以将这10G的订单分别划分到100个桶中,比如:1元到1000元的放在第一个桶内,1001到2000元的放到第二个桶内,以此类推。每个桶我们最终都会生成一个文件,这个小文件中存放的都是之前划分的那些订单数据,我们进行一个编号,比如(00,01,02,...99).

理想情况:

理想情况下,这10万的订单数据均匀分布到100个桶中,每个桶都存放1000个订单,每个桶即每个子文件大约是存储100M的数据,那么这100个子文件依次加载到内存中进行快速排序,排序完成后。我们依次读取每个子文件的数据并写入到一个大文件中,这样以来这10G的数据就排序完成了。

实际情况:

实际情况是这10G订单数据不可能是均匀分布的,一般人买东西肯定是金额低的多一些,金额高的就很少了,所以每个桶内不可能是很均匀的,这种情况的处理办法就是继续划分这个数据量大的桶为几个子桶,一直到每个桶的数据可以被一次性读入内存为止。

 

桶排序是稳定的排序算法吗?

由于桶排序是如果基于快排实现,就不是稳定的排序算法

如果是基于归并排序实现的,则可以完成稳定的排序。

桶排序是原地排序算法吗?

桶排序每个桶需要用到外部存储空间,所以不是原地排序算法。

 

以上是关于排序算法-桶排序的时间复杂度分析的主要内容,如果未能解决你的问题,请参考以下文章

排序算法-桶排序的时间复杂度分析

九种经典排序算法详解(冒泡排序,插入排序,选择排序,快速排序,归并排序,堆排序,计数排序,桶排序,基数排序)

冒泡排序算法详解

排序算法(七)---桶排序

排序优化

桶排序