[算法入门]--十分钟弄懂桶排序(基数排序)

目录

一、先看一个例子

我们先来看这样一个问题

思路解析

 二、基数排序的思路

思路:

三、用到的函数讲解

maxBits

findBit

randint

randomCreateArray

基数序函数radixSort

四、参考图

五、所有代码--VsCode(C++实现版本)


一、先看一个例子

我们先来看这样一个问题

如何将一群学生的年龄进行排序?

要求:时间复杂度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");
}

posted @   IoOozZzz  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示