排序算法----桶排序(java版)

桶排序

桶排序(Bucket Sort)顾名思义,会用到“桶”,我们可以将其想象成一个容器,核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了,换句话说:桶排序是将待排序集合中处于同一个值域的元素存入同一个桶中,也就是根据元素值特性将集合拆分为多个区域,则拆分后形成的多个桶,从值域上看是处于有序状态的。对每个桶中元素进行排序,则所有桶中元素构成的集合是已排序的。

桶排序过程中存在两个关键环节

  1. 元素值域的划分,也就是元素到桶的映射规则。映射规则需要根据待排序集合的元素分布特性进行选择,若规则设计的过于模糊、宽泛,则可能导致待排序集合中所有元素全部映射到一个桶上,若映射规则设计的过于具体、严苛,则可能导致待排序集合中每一个元素值映射到一个桶上。
  2. 从待排序集合中元素映射到各个桶上的过程,并不存在元素的比较和交换操作,在对各个桶中元素进行排序时,可以自主选择合适的排序算法,每个桶内的排序算法的复杂度和稳定性,决定了最终的算法的复杂度和稳定性。

桶排序比较适合用在非内存排序中。

所谓的非内存排序就是说数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。此外由桶排序的过程可知,当待排序集合中存在元素值相差较大时,对映射规则的选择是一个挑战,有时可能导致元素集中分布在某一个桶中或者绝大多数桶是空桶的现象,对算法的时间复杂度或空间复杂度有较大的影响,所以桶排序适用于元素值分布较为集中的序列,或者说待排序的元素能够均匀分布在某一个范围[MIN, MAX]之间。

桶排序算法具体的描述:

  • 人为设置一个 BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可 以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放 100 个 3);
  • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
  • 对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;
  • 从不是空的桶里把排好序的数据拼接起来。

注意如果递归使用桶排序为各个桶排序,则当桶数量为 1 时要手动减小BucketSize 增加下一循环桶的数量,否则会陷入死循环,导致内存溢出。

代码实现

public class BucketSort {
    
    
    public static void main(String[] args) {
    
    
        List<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(2);
        list.add(2);
        list.add(6);
        list.add(9);
        list.add(0);
        list.add(3);
        list.add(4);
        List<Integer> bucketSort = bucketSort(list, 2);
        System.out.println(bucketSort);

    }
    public static List<Integer> bucketSort(List<Integer> array,int bucketSize) {
    
    
        //合法性校验
        if (array == null || array.size() < 2 || bucketSize < 1) {
    
    
            return array;
        }
        //找出集合中元素的最大值和最小值
        int max = array.get(0);
        int min = array.get(0);
        for (int i = 0; i < array.size(); i++) {
    
    
            if (array.get(i) > max) {
    
    
                max = array.get(i);
            }
            if (array.get(i) < min) {
    
    
                min = array.get(i);
            }
        }
        //计算桶的个数   集合中的最小值到最大值 判断桶的个数
        int bucketCount = (max - min) / bucketSize + 1;
        //按照顺序创建桶 创建一个list带下标
        List<List<Integer>> bucketList = new ArrayList<>();
        for (int i = 0; i < bucketCount; i++) {
    
    
            bucketList.add(new ArrayList<Integer>());
        }
        //将待排序的集合依次添加到对应的桶中
        //首先找到元素所对应的桶的顺序  再将元素添加到桶中
        for (int j = 0; j < array.size(); j++) {
    
    
            int bucketIndex = (array.get(j) - min) / bucketSize;
            bucketList.get(bucketIndex).add(array.get(j));
        }

        //将桶内的元素进行排序
        List<Integer> resultList = new ArrayList<>();
        for (int j = 0; j < bucketList.size(); j++) {
    
    
            List<Integer> everyBucket = bucketList.get(j);
            if (everyBucket.size() >0) {
    
    
                if (bucketCount == 1) {
    
    
                    bucketSize--;
                }
                //递归调用  进行排序
                List<Integer> temp = bucketSort(everyBucket,bucketSize);
                for (int i = 0; i < temp.size(); i++) {
    
    
                    //合并数据
                    resultList.add(temp.get(i));
                }
            }
        }
        return resultList;
    }
}

1:桶排序的时间复杂度是多少?

桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,如果我们将待排序元素映射到某一个桶的映射规则做的很好的话,很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。我们一般对每个桶内的元素进行排序时采用快排也可以采用递归桶排序,通过我们刚开始的分析,当我们对每个桶采用快排时如果桶的个数接近数据规模 n 时,复杂度为 O(n),如果在极端情况下复杂度退化为 O(n* log n)。

2:桶排序的空间复杂度是多少?

由于需要申请额外的空间来保存元素,并申请额外的空间来存储每个桶,所以空间复杂度为 O(N+M),其中 M 代表桶的个数。所以桶排序虽然快,但它是采用了用空间换时间的做法。

扫描二维码关注公众号,回复: 13026888 查看本文章

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

桶排序是否稳定取决于对每一个桶内元素排序的算法的稳定性,如果我们对桶内元素使用快排时桶排序就是一个不稳定的排序算法。

猜你喜欢

转载自blog.csdn.net/qq_33626996/article/details/113175358