C语言算法--桶排序

2023-11-05

1-什么是桶排序法

什么是桶排序法?其实说白了就是把需要排列的元素分到不同的桶中,然后我们对这些桶里的元素进行排序的一种方式,然后我们在根据桶的顺序进行元素的合并。(不过前提是要确定桶的数量以及大小)

按照稍微正式的说法是:

桶排序法是一种基于计数的排序算法。它的基本思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是排好序的序列。

具体实现时,首先确定桶的个数和每个桶所能容纳数据的范围,然后遍历待排序数据,将每个数据放入对应的桶中。接着对每个桶里的数据进行排序,可以使用其它排序算法,比如插入排序、快速排序等。最后依次取出每个桶里的有序数据,组成排序好的序列。

桶排序法的时间复杂度为O(n),但是其空间复杂度较高,需要额外的桶来存储数据。同时,桶的个数和桶的大小需要根据数据的分布情况来确定,如果数据分布不均匀,容易导致某些桶内数据过多而造成空间浪费或者桶内排序时间过长。

动画演示(来源于哔哩哔哩up主-究尽数学

请添加图片描述

1.1 大致应用步骤

  1. 确定桶的数量和范围:

    • 确定要排序的元素的范围,并选择合适的桶的数量。一般来说,桶的数量可以根据元素的分布情况和性能需求进行调整。
  2. 创建空桶:

    • 根据确定的桶的数量,创建对应数量的空桶,用于存放待排序的元素。
  3. 将元素分配到桶中:

    • 遍历待排序的元素列表,根据每个元素的值将其分配到相应的桶中。可以使用一定的映射规则或算法来确定元素应该分配到哪个桶中。
  4. 对每个桶进行排序:

    • 对每个非空桶中的元素进行排序。可以使用其他排序算法(如插入排序、快速排序等)来对每个桶中的元素进行排序。
  5. 合并桶中的元素:

    • 按照桶的顺序,将每个桶中的元素合并起来形成最终的有序序列。可以按照桶的顺序依次取出每个桶中的元素,并将它们按顺序组合起来形成有序序列。
  6. 返回有序序列:

    • 合并完成后,得到的就是排序后的有序序列。

    不过有一点一定要注意,那就是适用元素范围较小且数据分布相对均匀的情况。对于大范围的数据或分布不均匀的数据,可能不太使用。

    下面用个示例来说明:

假如我们有一个待排序的整数数组:[35, 17, 25, 10, 42, 29, 50, 22]。我们可以使用桶排序对该数组进行排序。

大致的步骤如下:

  1. 创建桶:根据待排序数组的范围,创建一定数量的桶。假设范围为0-100,我们可以创建10个桶,每个桶表示一个范围区间,如桶0表示0-9,桶1表示10-19,以此类推。

  2. 分配元素到桶:遍历待排序数组,将每个元素根据其值分配到对应的桶中。例如,35分配到桶3,17分配到桶1,25分配到桶2,以此类推。

    • 桶0:
    • 桶1: 17
    • 桶2: 25, 22
    • 桶3: 35, 29
    • 桶4:
    • 桶5:
    • 桶6:
    • 桶7:
    • 桶8:
    • 桶9: 10, 42, 50
  3. 对每个桶进行排序:对每个非空的桶应用排序算法进行排序,可以选择插入排序、快速排序等。在本例中,我们使用插入排序对每个桶内的元素进行排序。

    • 桶0:
    • 桶1: 17
    • 桶2: 22, 25
    • 桶3: 29, 35
    • 桶4:
    • 桶5:
    • 桶6:
    • 桶7:
    • 桶8:
    • 桶9: 10, 42, 50
  4. 合并桶的结果:按照桶的顺序,将每个非空的桶中的元素按顺序合并到一个新的数组中,得到最终的排序结果。

    最终得到的排序结果为:[10, 17, 22, 25, 29, 35, 42, 50]

1.2 可以和哪些排序算法使用

  1. 插入排序(Insertion Sort):
    • 桶排序将元素分散到不同的桶中,每个桶中的元素相对较少。可以对每个非空桶使用插入排序来进行排序。插入排序适用于小规模的数据集,其时间复杂度为O(n^2),但对于已经基本有序的桶来说,插入排序可以快速地完成排序。
  2. 快速排序(Quick Sort):
    • 在桶排序中,每个桶可以视为一个子序列。对每个非空桶应用快速排序算法进行排序,然后按照桶的顺序将它们合并起来。快速排序的平均时间复杂度为O(nlogn),在处理桶时可以有效地进行分区和排序。
  3. 归并排序(Merge Sort):
    • 桶排序后,每个桶内的元素已经是有序的。可以使用归并排序的思想将每个桶中的有序序列合并成一个有序序列。归并排序的时间复杂度为O(nlogn),适用于大规模数据集的排序。
  4. 基数排序(Radix Sort):
    • 基数排序是一种按照元素位数进行排序的算法。在桶排序中,可以将每个桶看作是基数排序中的一个位数,将元素按照每个位数的值分配到对应的桶中,然后按照桶的顺序进行合并。基数排序的时间复杂度为O(kn),其中k是元素的位数。

2-桶排序法的优点

  1. 高效的时间复杂度:在均匀分布的情况下,桶排序的平均时间复杂度接近线性,具有较高的排序效率。这是因为桶排序将元素分散到多个桶中,每个桶独立地进行排序,而不需要像比较排序算法那样逐个比较和交换元素。
  2. 适用于外部排序:桶排序适用于需要排序的数据量非常大,无法全部加载到内存中的情况。它可以通过将数据分配到磁盘上的多个桶中,对每个桶进行排序,然后按照桶的顺序合并结果,实现外部排序。
  3. 可以实现稳定排序:通过在每个桶中使用稳定的排序算法,如插入排序,可以实现桶排序的稳定性。稳定排序意味着具有相同值的元素在排序后的顺序仍然保持不变。
  4. 适用于分布均匀的数据:当待排序的数据在各个桶中分布相对均匀时,桶排序的效率最高。对于数据分布不均匀的情况,桶排序的性能可能会受到影响,需要在每个桶中使用其他排序算法。
  5. 可以灵活调节桶的数量:通过调节桶的数量,可以对桶排序的性能进行优化。较少的桶数量可以节省内存空间,但可能会导致桶中元素的数量较多,需要使用较复杂的排序算法。较多的桶数量可以使每个桶中的元素较少,简化排序过程,但可能会消耗更多的内存。

3-桶排序法的缺点

  1. 需要额外的存储空间:桶排序需要额外的存储空间来存储桶,桶的数量与待排序元素的数量相关。如果待排序元素数量非常大,可能需要分配大量的桶和相应的存储空间,占用更多的内存。
  2. 对数据分布要求较高:桶排序的性能受到待排序数据的分布情况影响较大。如果数据分布不均匀,导致部分桶中的元素数量过多,可能需要在每个桶中使用其他排序算法进行排序,从而降低了桶排序的效率。
  3. 不适用于数据范围过大或过小的情况:当待排序的数据范围非常大或非常小时,桶排序可能不是最佳选择。如果数据范围过大,需要创建大量的桶,消耗过多的内存;如果数据范围过小,桶之间的差距会变得很大,导致很多桶为空,浪费了存储空间。
  4. 不稳定性:桶排序本身并不保证稳定性,即相同值的元素在排序后的顺序可能会改变。要实现稳定的桶排序,需要在每个桶内使用稳定的排序算法,增加了额外的操作和复杂性。
  5. 不适用于动态数据集:桶排序对于动态数据集的排序效果不佳。如果待排序数据集经常发生变化,需要频繁地重新进行桶排序,而且可能需要重新分配桶和重新排序,导致性能下降。

4-桶排序法可以应用哪些场景

  1. 范围有限的整数排序:桶排序对于待排序的整数数据集,在数据范围较小且分布相对均匀的情况下,可以实现高效的排序。例如,对学生成绩(0-100范围)进行排序。
  2. 外部排序:当待排序的数据量过大,无法一次性全部加载到内存中时,桶排序可以进行外部排序。它可以将数据分散到磁盘上的多个桶中,对每个桶进行排序,然后按照桶的顺序合并结果,实现排序。
  3. 基于分布的排序:如果待排序数据集的分布相对均匀,桶排序可以充分利用数据的分布特点,将数据分散到多个桶中进行独立排序,从而提高排序效率。例如,对一段时间内的温度数据进行排序。
  4. 并行排序:由于桶排序将数据分散到多个桶中,每个桶可以独立进行排序,因此可以在多个处理单元或多个线程上并行执行排序操作,提高排序的速度。
  5. 分布式排序:桶排序可以应用于分布式系统中的排序问题。将待排序数据分散到不同的节点或机器上的桶中,各个节点独立进行桶内排序,最后再合并结果,实现分布式的排序。
  6. 部分有序数据排序:如果待排序数据集中存在一部分已经有序的子序列,桶排序可以有效地利用这些子序列的有序性。通过将子序列分散到不同的桶中,各个桶内的排序效率较高,最后再将各个桶的结果合并起来,提高整体的排序效率。

从上面来看,桶排序适用性受到数据的规模、分布、内存以及计算等一些因素的影响。具体使用什么方式需要根据实际 的情况进行。

5-举例

下面我们来举一个例子:

#include <stdio.h>
#include <stdlib.h>

// 定义桶的数量
#define BUCKET_COUNT 10

// 定义桶的结构
typedef struct
{
     int count;
     int *values;
} Bucket;

void bucketSort(int arr[], int n)
{
     // 创建桶数组
     Bucket buckets[BUCKET_COUNT];

     // 初始化桶
     for (int i = 0; i < BUCKET_COUNT; i++)
     {
          buckets[i].count = 0;
          buckets[i].values = NULL;
     }

     // 将元素放入桶中
     for (int i = 0; i < n; i++)
     {
          int bucketIndex = arr[i] / BUCKET_COUNT;
          Bucket *bucket = &buckets[bucketIndex];

          // 扩展桶的容量
          bucket->values = realloc(bucket->values, (bucket->count + 1) * sizeof(int));
          bucket->values[bucket->count] = arr[i];
          bucket->count++;
     }

     // 对每个桶中的元素进行排序
     for (int i = 0; i < BUCKET_COUNT; i++)
     {
          Bucket *bucket = &buckets[i];
          int bucketSize = bucket->count;

          // 使用简单的插入排序对桶中的元素进行排序
          for (int j = 1; j < bucketSize; j++)
          {
               int key = bucket->values[j];
               int k = j - 1;
               while (k >= 0 && bucket->values[k] > key)
               {
                    bucket->values[k + 1] = bucket->values[k];
                    k--;
               }
               bucket->values[k + 1] = key;
          }
     }

     // 合并桶中的元素到原始数组
     int index = 0;
     for (int i = 0; i < BUCKET_COUNT; i++)
     {
          Bucket *bucket = &buckets[i];
          int bucketSize = bucket->count;
          for (int j = 0; j < bucketSize; j++)
          {
               arr[index] = bucket->values[j];
               index++;
          }
          free(bucket->values);
     }
}

int main()
{
     int arr[] = {29, 25, 3, 49, 9, 37, 21, 43, 5};
     int n = sizeof(arr) / sizeof(arr[0]);

     printf("原始数组:\n");
     for (int i = 0; i < n; i++)
     {
          printf("%d ", arr[i]);
     }
     printf("\n");

     bucketSort(arr, n);

     printf("排序后的数组:\n");
     for (int i = 0; i < n; i++)
     {
          printf("%d ", arr[i]);
     }
     printf("\n");

     return 0;
}

请添加图片描述

#include <iostream>
#include <vector>
#include <algorithm>

void bucketSort(std::vector<int> &arr, int bucketSize)
{
     if (arr.empty())
     {
          return;
     }

     // 找到最大值和最小值
     int minValue = arr[0];
     int maxValue = arr[0];
     for (int i = 1; i < arr.size(); i++)
     {
          if (arr[i] < minValue)
          {
               minValue = arr[i];
          }
          else if (arr[i] > maxValue)
          {
               maxValue = arr[i];
          }
     }

     // 计算桶的数量
     int bucketCount = (maxValue - minValue) / bucketSize + 1;

     // 创建桶
     std::vector<std::vector<int>> buckets(bucketCount);

     // 将元素分配到桶中
     for (int i = 0; i < arr.size(); i++)
     {
          int bucketIndex = (arr[i] - minValue) / bucketSize;
          buckets[bucketIndex].push_back(arr[i]);
     }

     // 对每个桶中的元素进行排序
     for (int i = 0; i < buckets.size(); i++)
     {
          std::sort(buckets[i].begin(), buckets[i].end());
     }

     // 合并桶中的元素
     int index = 0;
     for (int i = 0; i < buckets.size(); i++)
     {
          for (int j = 0; j < buckets[i].size(); j++)
          {
               arr[index++] = buckets[i][j];
          }
     }
}

int main()
{
     std::vector<int> arr = {45, 12, 36, 78, 53, 21, 67};
     int bucketSize = 10;

     std::cout << "原始数据: ";
     for (int num : arr)
     {
          std::cout << num << " ";
     }

     bucketSort(arr, bucketSize);

     std::cout << "\n排列之后的数据: ";
     for (int num : arr)
     {
          std::cout << num << " ";
     }

     return 0;
}

请添加图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言算法--桶排序 的相关文章

随机推荐

  • 鸽巢算法

    该算法主要用来对给定数据集进行排序的 可以快速求出第N大的数字 时间为常数时间 缺点 数据的范围不能太大 步骤如下 1 给定一个待排序数组 相当于一群鸽子 创建一个备用数组 叫鸽巢数组 并初始化元素为0 备用数组的索引 鸽巢的编号 即是待排
  • 让人工智能机器人学会自我情绪管理

    人类微观管理的一个影响是显而易见的 它会减慢速度 人类也是如此 一个不得不向上级请求命令的人类士兵的反应会比一个有权采取主动的人反应更慢 这对人工智能来说是一个更大的刹车 机器人教育课件它的电子思维过程比人类的神经化学大脑循环要快得多 一个
  • 超详细的Node.js环境搭建

    在使用Node js前 我们需要先进行Node js安装和配置 1 下载 Node js官网地址 https node js org 从官网上可以看到有两个版本的安装包 14 15 1和15 3 0 15 3 0是当前最新版 14 15 1
  • a-table及相关组件的使用

    a table及相关组件的使用 基础的渲染
  • 第38.1节 osg加载大tif-编译vpb

    目录 本节内容 具体内容 本节内容 本节开始实现使用osg加载超大tif的功能 具体有多大的tif可以支持呢 就是要多大有多大 要不是网友们提这个功能 我基本上已经忘记vpb了 它是osg早期关于使用影像和高程大图来生成瓦片地形的这么一个工
  • DeepSpeed 部署中bug以及解决方法

    text generation 1 can t find Rust compiler 在Linux上安装Rust 您可以使用curl或者类似包管理器的工具来安装Rust 使用curl命令安装Rust和Cargo curl proto htt
  • java基础:内部类

    内部类 1 简介 大部分时候 类被定义为一个独立的程序单元 在某些情况下 也会把一个类放在另外一个类的内部定义 这种类就被称为内部类 嵌套类 包含内部类的类也被称为外部类 宿主类 2 作用 内部类提供了更好的封装 可以将内部类隐藏在外部类之
  • 图片上传怎么搞?!阿里云OSS对象存储教你快速实现!

    一 需求背景 小白 辉哥 我想在项目中实现图片上传 不知道有没有好用的第三方文件上传技术呢 辉哥 那多了去了 阿里 腾讯 百度 七牛云等都有文件上传技术 你从中随便挑一个 辉哥这就给你安排 小白 阿里也有文件上传 要不辉哥就给我安排阿里的实
  • Spring Boot 读取配置文件的五种方式

    第一种 Value 方式读取 前提条件 在application properties 文件中 写入相关属性值 customer address USA 读取工具类源码 Component public class CustonerConf
  • Java面试整理(二)《JavaSE》

    JavaSE 说明 我会根据我自己的经验 给每个内容标注重要程度 共有三个等级 低 中 高 仅个人参考意见 JVM是什么 中 JVM是Java Virtual Machine的缩写 是用于运行Java字节码的虚拟机 JVM是运行在操作系统之
  • 30功能之Base64编码原理

    一 Base64的由来 目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一 在做支付系统时 系统之间的报文交互都需要使用Base64对明文进行转码 然后再进行签名或加密 之后再进行 或再次Base64 传输 那么 Bas
  • 2022接口自动化测试工具Postman 使用教程

    一 Postman接口测试概述 1 1 接口测试 接口是指对协定进行定义的引用类型 通俗讲是就是软件系统不同组成部分衔接的约定 接口测试是测试系统组件间接口的一种测试 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点 测
  • react替换元素节点_一道 React 面试题:在浏览器、组件和元素中都渲染了些什么?...

    每日前端夜话 第403篇 正文共 1500 字 预计阅读时间 7 分钟 这道题的答案有点复杂 首先要搞清楚 element 和 component 是不是一回事 从技术上来说 ReactDOM 不会在 DOM 中渲染 React 组件或 R
  • qt程序在debug模式下正常release下崩溃

    报错为 0x00007FFBFA386DD3 Qt5Core dll 处 位于 MainWindow exe 中 引发的异常 0xC0000005 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突 原因为 在一个函数下定义有返
  • 安卓编译的时候依赖包解析不了,老是下载不下来。Faled to resolve: com.squareup.retrofit2:converter-gson:2.3.0

    安卓编译的时候依赖包解析不了 老是下载不下来 E ldeaProjectsNdrcApp app build gradle Error QpenFle ShowinProiectStructuredala Faled to resolve
  • 【已解决】华为P10禁止系统更新EMUI9

    EMUI9 0耗电快 发热 占用内存大 所以我还是回退8 0 但是总是提示更新 还偷偷用wifi下载3个多G的安装包 尽管已经禁止wifi下载安装了 忍无可忍只能用ADB禁止 系统更新 以下是大致流程 PC端手机助手官方降级到EMUI8 0
  • 读《洞穴奇案》——旧文人的愚民术

    忙了快一周多 一直没有抽出时间继续阅读本书 今天终于草草的阅读完了一章 这章讲的是法律与民意之间的关系 更深入地讲 法律是否真正的表达了民意 而再发问 这个民意到底指的是谁的意图 这种意图是发自内心的还是屁股决定脑袋 如果我们以最善意观点去
  • 中国蚁剑安装教程——antsword及webshell一句话木马

    中国蚁剑是一款开源的跨平台网站管理工具 它主要面向于合法授权的渗透测试安全人员以及进行常规操作的网站管理员 任何人不得将其用于非法用途以及盈利等目的 否则后果自行承担 一 首先下载加载器 下方是通过镜像github的地址 但是我依旧不能连接
  • 【区块链学习】

    区块链学习一 区块链概念 区块链结构 共识机制 智能合约 区块链应用于加密货币 区块链应用于实际场景 如何用最简单的方式解读区块链 通俗解释 区块链概念 区块链是一种分布式数据库 由许多区块组成 每个区块包含了一些交易信息和引用前一个区块的
  • C语言算法--桶排序

    1 什么是桶排序法 什么是桶排序法 其实说白了就是把需要排列的元素分到不同的桶中 然后我们对这些桶里的元素进行排序的一种方式 然后我们在根据桶的顺序进行元素的合并 不过前提是要确定桶的数量以及大小 按照稍微正式的说法是 桶排序法是一种基于计