C++实现桶排序(基于链表)

原理:

桶排序是时间复杂度为线性的排序方法,它的实现主要基于指针数组构成的链表,算法把输入数组的元素按照值的大小等分为n个区间第i个区间的元素范围是\frac{i-1}{n}(maximum-minimum)+minimum\frac{i}{n}(maximum-minimum)+minimum

所以如果我们要将值为value的元素放入桶中首先要确定桶的序号,根据上面的描述,桶的序号为n*(value-minimum)/(maximum-minimum),因为数组的编号从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中,因此存在k使得:(*(arr+i)-minimum)/gap=(maximum-minimum)/gap=build\_num,但数组的编号是从0开始,因此计算index的表达式括号中减去了一个1,但此时也存在k使得(*(arr+i)-minimum-1)/gap=(minimum-minimum-1)/gap<0,因此最好对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;
}

运行效果如下:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值