排序算法学习09_桶排序(Java)

桶排序是一种非比较排序算法,通过创建多个桶并将元素分配到相应的桶中,然后对每个桶进行排序,最终合并所有桶以得到排序结果。文章介绍了桶排序的工作原理、设计思路、代码实现以及时间复杂度分析,强调了桶的数量和区间的设定对性能的影响。在内存充足的情况下,适当增加桶的数量可以达到O(n)的时间复杂度。代码示例展示了如何使用Java实现桶排序,并利用Collections工具类进行桶内排序。
摘要由CSDN通过智能技术生成

桶排序


前言:在博客写这些文章的目的用于记录所学,怕以后忘了,如果哪里写的不对欢迎指正,谢谢!!

学习目标:掌握桶排序算法的原理和思想

一、前提知识

  排序算法概念、时间复杂度。可前往此网址 排序算法学习01_算法基础介绍阅读

二、桶排序介绍

​   桶排序(Bucket sort),桶排序是我们上篇讲过的计数排序的升级版,所以桶排序也是如计数排序一样,通过把原序列的元素放置到“一个新的位置”上,然后依次取出排序
  且桶排序也不是比较排序,桶排序是稳定的

三、桶排序工作原理

  学过计数排序,我们知道,计数排序把原序列的元素,放到一个新的数组上,进行排序。
  而升级后的桶排序,不再局限于一个数组,而是创建多个桶来放置原序列的元素,并为每一个桶设置一个范围,然后将原序列各个元素根据大小分布到专属的桶中,再然后按顺序依次从各个桶取出,则可得到一个排序好的结果

​ 图解:

四、桶排序设计思路

  一、升级后的桶排序,不像原先计数排序,按照索引来排序,而是在各个桶上设置一个范围,该范围将用来接收原序列中对应的元素,所以

  1. 首先要创建足够的桶,保证原序列的元素都可以存放。至少要大于数组长度

  2. 由于桶排序不是比较排序,那么我们需要一种区间(也就是范围),使从原序列进入桶的元素,该元素都能根据这个区间进入对应的桶

  二、当原序列元素存入完毕时,依次取出时,还要对每个桶内部进行排序,比如上图中,如果14在11前面时,那就需要再对每个桶内部进行排序

  1. 所采用桶内部的排序方式将会直接影响到桶排序的性能

根据以上两大点设计方案:目前我所知两种方案,1.桶数量根据原序列长度设置 2.桶数量根据最大最小值差来设置

  • 关于桶数量,我们此次选择使用原序列长度来设置。桶所采用的数据结构使用JDK提供的ArrayList
    • 两大优势:1.自动扩容
    • 2.使用该容器最主要还是因为,该容器里还可以放置容器作为元素,这样就可以把多个桶归纳到一起方便处理
  • 关于区间设置,这是桶排序最核心的问题了
    • 区间的选择
      • 使用原序列最大值减去最小值得到一个总区间
      • 然后使用总区间除以原序列长度,即可让每个桶平均分配到一个区间
    • 我们这时要再引入一个专业名词,用于数据入桶时的一个映射函数,该函数能帮助我们通过值的大小来找到对应区间所在桶
      • 关于映射函数,我目前所知有两种设置方式
      • 第一种,就是我使用的这种方案
        • 桶位置 == 待排入值 / 区间
      • 第二种,桶的数量根据最大最小值来设置时 (max - min) / arr.length + 1;
        • 桶位置 == (待排入值 - 序列最小值) / 序列长度
        • 使用该方案不需要去设置区间
        • 性能最优,但耗空间
  • 关于桶内部排序,我这边直接使用JDK提供的工具类 Collections 的sort方法

五、代码实现

package com.migu.sortingAlgorithm;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class BucketSort {
    public static void main(String[] args) {
        int[] arr = {50,350,150,250,500,0};
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void sort(int[] arr){
        int max = arr[0]; // 最大
        int min = arr[0]; // 最小

        for(int i=1; i<arr.length; i++) {
            if(arr[i] > max) {
                max = arr[i];
            }
            if(arr[i] < min) {
                min = arr[i];
            }
        }

        // 原序列最大小元素差,用于分配区间
        int differ = max - min;
        // 每个桶的存数区间,向上取整
        int section = (int)Math.ceil(differ / (arr.length - 1)) ;
        
        // 桶列表
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
        // 桶数量根据序列长度设置
        for(int i = 0; i < arr.length; i++){
            bucketList.add(new ArrayList<>());
        }

        // 遍历原序列值入桶
        for(int i = 0; i < arr.length; i++){
            // 当前数除以区间得出存放桶的位置 减1后得出桶的下标
            int num = arr[i] / section - 1;
            if(num < 0){
                num = 0;
            }
            bucketList.get(num).add(arr[i]);
        }

        // 桶内排序
        for(int i = 0; i < bucketList.size(); i++){
            // 调用JDK工具类帮助排序
            Collections.sort(bucketList.get(i));
        }

        // 写入原序列
        int index = 0;
        for(ArrayList<Integer> arrayList : bucketList){
            for(int value : arrayList){
                arr[index++] = value;
            }
        }
    }
}

六、时间复杂度

  只要合理分配桶的数量,时间复杂度为O(n + k),这个k就是桶内部排序的复杂度。如果在一个桶列表中,有一个桶存在大量数据的话,那么时间复杂度就是另一回事了

七、总结

  在内存量足够的情况下,建议提升桶的数量,当然也要随之改变区间的分配程度,这样就可避免桶内的排序操作,时间复杂度可到O(n)。

  桶排序对于分布不均匀的数据排序不友好,如果上面讲的一样,在某个区间有大量数据,就完全没有桶排序的意义了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值