[算法入门]--十分钟弄懂桶排序(基数排序)
目录
一、先看一个例子
我们先来看这样一个问题
如何将一群学生的年龄进行排序?
要求:时间复杂度O(n)
Tips:注意挖掘题目中的隐含条件!
思路解析
隐藏条件:学生的年龄在0-100之间,我们利用这一点建立一个学生年龄作为索引,存放值为对应年龄的学生人数的数组bucket(桶);将数据存入桶后倒入help临时数组,最后help数组覆盖需要处理的数据即可。
#include <iostream> #include <vector> using namespace std; int main(){ vector<int> ages; //学生年龄 vector<int> bucket(100, 0); //准备一个桶 ages.push_back(12); ages.push_back(13); ages.push_back(14); ages.push_back(15); ages.push_back(11); ages.push_back(10); ages.push_back(9); vector<int> help; //辅助空间 //动态数组压入学生年龄数据 vector<int>::iterator it = ages.begin(); //初始化迭代器 for (; it < ages.end(); it++) { bucket.at(*it)++; //bucket[年龄] = 人数 } it = bucket.begin(); //进入桶中迭代 for (; it < bucket.end(); it++) //迭代器遍历桶 { int n = *it; //对应年龄的人数 for (int j = 0; j < n; j++) { help.push_back(it - bucket.begin()); } } ages = help; cout << "The ages:"; for (int i = 0; i < ages.size(); i++) { cout << ages.at(i) << ' '; }cout << endl << endl; }
二、基数排序的思路
思路:
给定一个数组,
这个数组中最大的数是43,也就是说数组中所有的数的位数不会超过2,桶排序就是利用了这一个隐藏信息。
我们将数组排序两次,第一次按照所有数字的最后一位排序,那就是这样
按照从右往左排序对应的位,数组会按照最左边优先级最高来排序,因为最左边是最后被覆盖进数组的。
就是因为每一次按照位来排序,所以桶排序也叫基数排序。
第二次再按照所有的数字的倒数第二位排序(也就是第一位),数组达到一个有序的状态。
所以,桶排序的原理其实很简单:
1.找出数组中最大数有几位
2.有几位我们就入桶出桶几次
3.每次准备一个(十进制的情况)大小为10的桶----[0, 9]
4.正序遍历数组,按照顺序取出对应的位
5.将位作为索引,值是该位出现的数量,进行统计
6.处理bucket中的数据,使它的值从原来的【索引位出现数量】--> 小于等于索引位的数量
换言之就是这个数出现的序号[我们说的序号从1开始,索引从0开始]
7.从右往左倒序遍历数组,取出每个数的对应位,查找桶中这个位对应的位置(或者说小于等于这个位的数量)将这个数放入对应位置的help辅助数组当中去
8.按照最大位的数量重复上述过程对应的次数即可(例如最大的数是2311,最大位就是四位,那么重复入桶出桶四次,每次排序的依据是倒数第一位->倒数第二位->倒数第三位->倒数第四位)
三、用到的函数讲解
设计一个返回数组中最大数位长度的函数,这个很简单就不解释了:
maxBits
int maxBits(vector<int> arr) { int max = *arr.begin(); for (auto i : arr) { if (i > max) max = i; } int count = 0; while (max > 0) { max /= 10; count++; } return count; } // 返回数组中最大数有几位
遍历数组找到最大值后再统计最大值的位数即可 。
findBit
int findBit(int n, int c) { int bit; while (c--) { bit = n % 10; n /= 10; } return bit; } // 返回从右往左数指定位
再设计一个数找到每次遍历的对应位
randint
int randint(int a, int b) { return rand() % (b - a + 1) + a; }
返回一个随机数用于随机创建数组,配合主函数的随机种子使用效果更佳!
randomCreateArray
vector<int> randomCreateArray(int size, int a, int b) { cout << "creating array: "; vector<int> arr; for (int i = 0; i < size; i++) { arr.push_back(randint(a, b)); cout << arr[i] << ' '; } cout << endl; return arr; }
随机创建数组便于测试
基数序函数radixSort
void radixSort(vector<int> &arr, int radix) // 基数radix代表具体的进制 { int maxbits = maxBits(arr); // 寻找数组中最大数有几位 for (int i = 1; i <= maxbits; i++) // 进行多轮处理 { // 例如:12345: 5 -> 4 -> 3 -> 2 -> 1 vector<int> help(arr.size(), 0); //定义临时数组空间 vector<int> bucket(radix, 0); // 初始化一个桶来统计位的出现次数 for (auto k : arr) // 遍历数组提取出大循环所对应的位 { int bit = findBit(k, i); //找到对应位 bucket[bit]++; //对应位加一 } for (int j = 1; j < bucket.size(); j++) { bucket[j] += bucket[j - 1]; //bucket中的数代表<=它的数字的个数(也索引位的数的位置) } for (int k = arr.size() - 1; k > -1; k--) { //倒着遍历数组 int bit = findBit(arr[k], i); //找到数组所遍历的数对应的位 int index = bucket[bit]; //存放小于等于bit位的数量 help[index - 1] = arr[k]; //将该数存入临时数组 bucket[bit]--; //用完少一个 } arr = help; } }
四、参考图
最后附上我用来理解桶排序所做的图解:
五、所有代码--VsCode(C++实现版本)
#include <iostream> #include <vector> #include <ctime> using namespace std; int randint(int a, int b) { return rand() % (b - a + 1) + a; } vector<int> randomCreateArray(int size, int a, int b) { cout << "creating array: "; vector<int> arr; for (int i = 0; i < size; i++) { arr.push_back(randint(a, b)); cout << arr[i] << ' '; } cout << endl; return arr; } int findBit(int n, int c) { int bit; while (c--) { bit = n % 10; n /= 10; } return bit; } // 返回从右往左数指定位 int maxBits(vector<int> arr) { int max = *arr.begin(); for (auto i : arr) { if (i > max) max = i; } int count = 0; while (max > 0) { max /= 10; count++; } return count; } // 返回数组中最大数有几位 void radixSort(vector<int> &arr, int radix) // 基数radix代表具体的进制 { int maxbits = maxBits(arr); // 寻找数组中最大数有几位 for (int i = 1; i <= maxbits; i++) // 进行多轮处理 { // 例如:12345: 5 -> 4 -> 3 -> 2 -> 1 vector<int> help(arr.size(), 0); //定义临时数组空间 vector<int> bucket(radix, 0); // 初始化一个桶来统计位的出现次数 for (auto k : arr) // 遍历数组提取出大循环所对应的位 { int bit = findBit(k, i); //找到对应位 bucket[bit]++; //对应位加一 } for (int j = 1; j < bucket.size(); j++) { bucket[j] += bucket[j - 1]; //bucket中的数代表<=它的数字的个数(也索引位的数的位置) } for (int k = arr.size() - 1; k > -1; k--) { //倒着遍历数组 int bit = findBit(arr[k], i); //找到数组所遍历的数对应的位 int index = bucket[bit]; //存放小于等于bit位的数量 help[index - 1] = arr[k]; //将该数存入临时数组 bucket[bit]--; //用完少一个 } arr = help; } } int main() { srand(time(0)); const int radix = 10; cout << findBit(1, 2); vector<int> arr = randomCreateArray(10, 1, 100); for (auto i : arr) { cout << i << ' '; }//打印 radixSort(arr, radix); //按照十进制的规则排序数组 cout << endl; cout << "The sorted array:"; for (auto i : arr) { cout << i << ' '; }//打印排序之后的数组 system("pause"); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架