C语言算法--桶排序

news/2024/11/20 7:39:43/

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;
}

请添加图片描述


http://www.ppmy.cn/news/89108.html

相关文章

TLS 加速技术:Intel QuickAssist Technology(QAT)解决方案

作者&#xff1a;vivo 互联网服务器团队- Ye Feng 本文介绍了 Intel QAT 技术方案&#xff0c;通过Multi-Buffer技术和QAT硬件加速卡的两种方式实现对TLS的加速 一、背景 当前 TLS 已经成为了互联网安全的主要传输协议&#xff0c;TLS带来更高的安全性的同时&#xff0c;也带…

数字孪生智慧工厂可视化分析决策方案,打造智慧汽车工厂

智慧工厂是当前智能制造领域的热门话题之一&#xff0c;是一种集成数字技术、先进制造技术和现代管理技术的新型工厂模式。随着全球制造业的发展&#xff0c;智慧工厂逐渐成为未来工厂发展的一大趋势&#xff0c;越来越多的企业开始关注智慧工厂的建设。 该数字孪生智慧汽车工厂…

【分布族谱】正态分布和卡方分布的关系

文章目录 正态分布卡方分布卡方分布的极限 正态分布 正态分布&#xff0c;最早由棣莫弗在二项分布的渐近公式中得到&#xff0c;而真正奠定其地位的&#xff0c;应是高斯对测量误差的研究&#xff0c;故而又称Gauss分布。。测量是人类定量认识自然界的基础&#xff0c;测量误差…

使用cv2将图片转正

要点&#xff1a; 参考&#xff1a;如何用Python-OpenCV实现图片倾斜调整&#xff1f; 一 图片转正 比如一张纸的照片是倾斜的&#xff0c;用OpenCV如何实现自动检测出纸的轮廓并调整倾斜角度&#xff0c;让照片变“正”。 import cv2 import numpy as npdef click(event,x,y…

Seata AT模式源码解析二(Seata Client端启动流程)

文章目录 初始化TM和RM数据源代理 由于我们一般都是在springboot中使用的&#xff0c;而与springboot集成的我们一般就先看starter的spring.factories文件&#xff0c;看看它的自动装配 这里面主要关注SeataAutoConfiguration和SeataDataSourceAutoConfiguration。 SeataAutoCo…

2023 江西省大学生程序设计竞赛

写在前面的话&#xff1a;跟错榜了&#xff0c;死磕C题&#xff0c;又不懂sg然后就寄了。幸好队友带飞&#xff0c;最后把B题开出来了。最后六题rank18。&#xff08;这个故事告诉我们不要乱跟榜啊 比赛链接&#xff1a;https://codeforces.com/gym/104385 L 签到题&#xff…

CCIG:智能文档处理「新未来」

文章目录 ⭐️ CCIG大会简介⭐️ 领先世界的智能文档处理技术&#x1f31f; 智能图像处理&#xff1a;为文字识别 "增质提效" 筑基✨ 切边增强 - 提升文档图像质量✨ 弯曲矫正 - 解决图像畸变问题✨ 去摩尔纹 - 保证图像信息完整 &#x1f31f; 图像预处理整体效果展…

Go | zap

Go | zap 1. 简介 那些介绍、性能比较直接看参考中zap链接&#xff0c;这里只介绍该日志库用法&#xff0c;方便快速上手。 package mainimport ("go.uber.org/zap" )func main() {logger, _ : zap.NewProduction()defer logger.Sync()logger.Info("hello wo…