原理:
桶排序是时间复杂度为线性的排序方法,它的实现主要基于指针数组构成的链表,算法把输入数组的元素按照值的大小等分为个区间第
个区间的元素范围是
到
所以如果我们要将值为的元素放入桶中首先要确定桶的序号,根据上面的描述,桶的序号为
,因为数组的编号从0开始,至n-1结束,因此C++实现的这部分代码为
float gap = float(scope)/ build_num;
int index = trunc((*(arr + i) - minimum - 1) / gap);
其中*(arr+i)是输入数组第i个元素解引用的值,minimum是整个数组的最小值,build_num是桶的数量,scope=maximum-minimum,表示整个待排序数组取值的范围大小,gap则是每个桶取值范围的大小。由于maximum必定在数组arr中,因此存在使得:
,但数组的编号是从0开始,因此计算index的表达式括号中减去了一个1,但此时也存在
使得
,因此最好对index进行向0取整,C++的头文件<math.h>定义了trunc成员,能够实现对浮点数向0取整。
接下来就要定义数据结构了,Node是链表的基本元素,它拥有一个指针域和指向下一个Node的指针:
struct Node
{
int data;
Node *next;
};
以及指针数组LinkList:
Node **LinkList;
注意这里是两个*代表指针数组,对于数组每个编号的第一个指针,我们可以置data域为NULL也可以利用这个节点记录这个链表的元素个数,在这里我选择后者,那么初始化就可以这样:
LinkList = (Node **)malloc(sizeof(Node *) * build_num);//分配内存空间
for (int i = 0; i < build_num;i++)
{
LinkList[i] = (Node *)malloc(sizeof(Node));
LinkList[i]->data = 0; //第一个节点该桶保存存储的数据数量
LinkList[i]->next = nullptr;
}
接下来要遍历传入的待排序数组,决定存入哪个桶的链表中,最好在存入时就保证数据从小到大有序,由于第一个指针存储的是元素的数量,每次将Node关联到第i个桶(链表)都在头节点的data域自增1,并且对链表进行插入操作时跳过该Node节点;
Node *p=(Node *)malloc(sizeof(Node));
p->next = nullptr;
p->data = *(arr + i);
Node *pre = LinkList[index];
pre->data++;
bool flag = false;
if(pre->next!=nullptr)//跳过第一个计数节点
{
flag = true;//非空链表置标记位为真
pre = pre->next;
}
flag标记位对后续程序的判断很有用,看下面的分析,
对于每个要存入链表的数,我们可以边遍历边比较元素,直到链表中的一个节点data域比该数要大,
while(pre->next != nullptr&&p->data>pre->data)
pre = pre->next;
执行上述代码后图示指针将指向这个位置:
由于没有前驱节点,我们所要做的就是将p插入到pre的后面,然后互置p和pre的data域:
但要特别注意另外两种情况,一种是遍历过后pre的后面是空指针并且p中存放的数据大于pre中存放的数据,那么
我们可以直接将p放在pre后面
但是如果整个链表只有一个Node节点,并且初始时刻,pre在第一个Node节点处(因为跳过了计数的指针)而p的data域小于pre的data域,但是链表是单向链表,我们也用上述过程处理:
但是如果链表为空,即非空链表标记位置为0,则我们直接将p置于头节点后
代码:
bucketSort.hpp
#include <iostream>
#include<math.h>
#include<sstream>
using namespace std;
class Bucket
{
public:
void buildLink(int *arr, int sizeofA,int build_num,int minimum,int scope)
{
this->size = build_num;
LinkList = (Node **)malloc(sizeof(Node *) * build_num);//创建数组指针
float gap = float(scope)/ build_num;
for (int i = 0; i < build_num;i++)
{
LinkList[i] = (Node *)malloc(sizeof(Node));
LinkList[i]->data = 0; //第一个节点保存存储的数据数量
LinkList[i]->next = nullptr;
}
for (int i = 0; i < sizeofA;i++)
{
int index = trunc((*(arr + i) - minimum - 1) / gap);
Node *p=(Node *)malloc(sizeof(Node));
p->next = nullptr;
p->data = *(arr + i);
Node *pre = LinkList[index];
pre->data++;
bool flag = false;
if(pre->next!=nullptr)//跳过第一个计数节点
{
flag = true;//非空链表标记置标记位为真
pre = pre->next;//初始pre在计数Node之后
}
while(pre->next != nullptr&&p->data>pre->data)
pre = pre->next;
if(pre->next==nullptr&&pre->data<p->data)
{
pre->next = p;
}
else
{
//前后两个链表元素的data域互置,因为没有前驱指针,而while循环后pre->data的值大于p->data
if(flag){
int smaller = p->data;
int larger = pre->data;
p->next = pre->next;
pre->next = p;
p->data = larger;
pre->data = smaller;
}
else
pre->next = p;
}
}
}
void outputSort()
{
ostringstream s;
s << "\nAfter Sorted:";
for(int i = 0; i < size;i++)
{
Node *pre=LinkList[i];
if(!(pre->next==nullptr))
pre = pre->next;
while(pre!=nullptr)
{
s << pre->data << " ";
pre = pre->next;
}
}
s << "\n";
cout << s.str() << endl;
}
void outputDistribution()
{
ostringstream s;
for (int i = 0; i < size;i++)
{
Node *pre=LinkList[i];
s << "第" << i << "链表共计有" << pre->data << "个元素,它们分别为";
if(!(pre->next==nullptr))
pre = pre->next;
while(pre!=nullptr)
{
s << pre->data << " ";
pre = pre->next;
}
s << "\n";
}
cout << s.str() << endl;
}
private:
struct Node
{
int data;
Node *next;
};
Node **LinkList;
int size;
};
bucketSort.cpp
#include<iostream>
#include<time.h>
#include<algorithm>
#include "bucketSort.hpp"
using namespace std;
#define max 50
#define min -50
int* getRandData(int n) {
int *arr=(int *)malloc(sizeof(int) * n);
for (int i = 0; i < n; i++)
*(arr+i)=((rand() %(max-min+1))+min);
return arr;
}
int main()
{
srand(time(NULL));
int size=50;
int *arr=getRandData(size);
Bucket bk;
for (int i=0;i<size;i++)
cout<<*(arr+i)<<" ";
cout<<endl;
int maximum = *max_element(arr, arr + size);
int minimum = *min_element(arr, arr + size);
int scope = maximum - minimum;
bk.buildLink(arr, size, 10, minimum, scope);
bk.outputSort();
bk.outputDistribution();
system("pause");
return 0;
}
运行效果如下: