首页 > 其他分享 >数据结构应用之桶排序

数据结构应用之桶排序

时间:2023-12-29 23:22:58浏览次数:42  
标签:sort 数据结构 int pBarrel bucket 之桶 test 排序

问: 有10G的订单数据,希望订单金额(假设都是正整数)进行排序,但我们内存有限,只有几百MB,如何进行排序?
答: 因内存有限,需要排序的数据量巨大,所以,此时需要外部排序,外部排序采用的是一种分治思想,外部排序最常用的是多路归并排序,即将大数据切成多份一次可以载入内存的小数据,对小数据进行内存排序,然后再对排序好的多个小数据进行归并排序。然而,本节将介绍它的一种变体--桶排序,它的主要是将待排序的数据分到若干个桶中,然后对每个桶内数据进行排序,最后取出桶内的数据组成有序列表。

PS:上代码:
桶内数据结构

struct barrel{
    int node[10]; //桶内最多支持10个数据
    int count; //节点数量
};

"桶里"排名 -- 桶内排序采用快速排序


int partition(int a[], int left, int right)
{
    int key = a[left];
    while(left < right)
    {
        while((left < right) && a[right] >= key)
        {
            right--;
        } 
        if(left < right)
        {
            a[left] = a[right];
        }
        while((left < right) && a[left] <= key)
        {
            left++;
        }
        if(left < right)
        {
            a[right] = a[left];
        }
    }
    a[left] = key;
    return left;
}

void quick_sort(int a[], int left, int right)
{
    int q = 0;
    // 终止条件
    if(left >= right)
    {
        return;
    }
    q = partition(a, left, right);
    quick_sort(a, left, (q-1));
    quick_sort(a, (q+1), right);
    return ;
}

桶排序,数据"分桶"

void bucket_sort(int data[], int size)
{
    int max, min, num, pos;
    int i, j, k;
    struct barrel *pBarrel;

    max = min = data[0];
    for(i = 1; i < size; ++i)
    {
        max = data[i] > max ? data[i] : max;
        min = data[i] < min ? data[i] : min;
    }
    num = (max - min + 1) / 10 + 1;  // 通过最大值最小值计算需要多少个桶,此处每个桶最多能存10个数据
    pBarrel = (struct barrel *)malloc(sizeof(struct barrel) * num);
    memset(pBarrel, 0, sizeof(struct barrel) * num);

    for(i = 0; i < size; ++i)
    {
        k = (data[i] - min + 1) / 10;   // 计算该数据属于第几个桶
        (pBarrel + k)->node[(pBarrel + k)->count] = data[i]; // 将该数据存入第k个桶中的第count的数据
        (pBarrel + k)->count++;  // 该桶内数据数据加1
    }

    // 桶内数据排序,快速排序
    pos = 0;
    for(i = 0; i < num; ++i)
    {
        if((pBarrel + i)->count != 0)
        {
            quick_sort((pBarrel + i)->node, 0, (pBarrel + i)->count-1);
            for(j = 0; j < (pBarrel + i)->count; ++j)
            {
                data[pos++] = (pBarrel+i)->node[j]; 
            }
        }
    }
    free(pBarrel);
}

模拟数据

void bucket_sort_test()
{
    int a[] = {78, 17, 39, 26, 72, 94, 21, 12, 23, 91};
    int size = sizeof(a) / sizeof(int);
    printf("\r\n bucket sort test ...\n");
    bucket_sort(a, size);
    dump(a, size);
}

int main()
{
    // 桶排序
    bucket_sort_test();
	
    return 0;
}

时间复杂度分析:
如果要排序的数据有 n 个,我们把它们均匀地划分到 m 个桶内,每个桶里就有$ k=n/m $个元素。每个桶内部使用快速排序,时间复杂度为 \(O(k * logk)\)。m 个桶排序的时间复杂度就是 \(O(m * k * logk)\),因为 k=n/m,所以整个桶排序的时间复杂度就是$ O(n*log(n/m))$。当桶的个数 m 接近数据个数 n 时,log(n/m) 就是一个非常小的常量,这个时候桶排序的时间复杂度接近 \(O(n)\)。

模板实现:

#ifndef TEMPLATE_SORT_BUCKET_HPP_
#define TEMPLATE_SORT_BUCKET_HPP_

#include <functional>
#include <iterator>
#include <algorithm>
#include <vector>

template <size_t BucketSize,
        typename IterT,
        typename T = typename std::iterator_traits<IterT>::value_type,
        typename Compare = std::less<T>>
void bucket_sort(IterT first, IterT last, Compare comp = Compare())
{
    const T min = *std::min_element(first, last), max = *std::max_element(first, last);
    const T range = max - min + 1;
    const size_t bucket_num = range / BucketSize + 1;

    std::vector<std::vector<T>> buckets(bucket_num);

    // 为桶内数据预留空间
    for(auto b : buckets)
    {
        b.reserve(2 * BucketSize);
    }

    for(IterT i = first; i != last; ++i)
    {
        size_t idx = (*i - min + 1) / BucketSize;
        buckets[idx].emplace_back(*i);
    }

    IterT dest = first;
    for(auto b: buckets)
    {
        std::sort(b.begin(), b.end(), comp);
        std::copy(b.begin(), b.end(), dest);
        dest += b.size();
    }
    return;
}

#endif

调用

template <size_t BucketSize,
          typename Container,
          typename T = typename Container::value_type,
          typename Compare = std::less<T>>
void test_bucket_sort(Container cont, Compare comp = Compare()) {
    bucket_sort<BucketSize>(cont.begin(), cont.end(), comp);
    std::transform(cont.begin(), cont.end(), std::ostream_iterator<T>(std::cout, " "),
            [](T i){ return i; });
    std::cout << std::endl;
}

int main() {
    std::vector<int> test{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9};

    test_bucket_sort<2>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9
    test_bucket_sort<3>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9
    test_bucket_sort<4>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9
    test_bucket_sort<5>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9
    test_bucket_sort<6>(test);  // 1 1 2 3 3 4 5 5 5 6 7 8 9 9 9

    return 0;
}

参考:https://time.geekbang.org/column/article/42038?cid=100017301

标签:sort,数据结构,int,pBarrel,bucket,之桶,test,排序
From: https://www.cnblogs.com/whiteBear/p/17935785.html

相关文章

  • 排序
    冒泡排序#include<iostream>usingnamespacestd;intmain(){intarr[6]={0};intlen=sizeof(arr)/sizeof(int);for(inti=0;i<len;i++){cin>>arr[i];}//writeyourcodehere......f......
  • 【数据结构】C语言实现双链表的基本操作
    双链表导言大家好,很高兴又和大家见面啦!!!经过前面几个篇章的内容分享,相信大家对顺序表和单链表的基本操作都已经熟练掌握了。今天咱们将继续分享线性表的链式存储的第二种形式——双链表。在今天的内容中,咱们将介绍双链表的创建以及一些基本操作,接下来跟我一起来看看吧!一、单链表与双......
  • 数据结构实验代码分享 - 4
    迷宫与栈问题(图的应用)【问题描述】以一个m*n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。输入:行列迷宫,0表示无障碍,1表示有障碍输出:一条Path或“NOPATH” 注:参考了《数据结......
  • 数据结构实验代码分享 - 3
    哈夫曼编码/译码系统(树应用)[问题描述]任意给定一个仅由26个大写英文字母组成的字符序列,根据哈夫曼编码算法,求得每个字符的哈夫曼编码。要求:1)输入一个由26个英文字母组成的字符串,请给出经过哈夫曼编码后的编码序列及其编码程度。(编码)2)采用上一问题的哈夫曼编码,给定一串编......
  • golang对map排序
    golang中map元素是随机无序的,所以在对maprange遍历的时候也是随机的,不像php中是按顺序。所以如果想按顺序取map中的值,可以采用以下方式:import("fmt""sort")funcmain(){m:=make(map[int]string)m[1]="a"m[2]="c"m[0]="b"......
  • 数据结构之<散列表>的介绍
    简介散列表也叫做哈希表,是根据键值对(key,value)进行存储的一种数据结构。散列表利用哈希函数将给定的键映射到一个特定的位置(通常是数组索引),这个位置通常被称为哈希值或哈希地址。这里可以举个微信好友列表的例子说明,存放好友首字母的表对应的就是散列表。1.哈希函数哈希函数是......
  • 数据结构&&集合总结
    总结数据结构数据结构:保存数据的一种方式常见的数据结构通过数组来保存,基于数组的数据结构(动态数组,长度可变的数组)基于数组的结构的优缺点​ 1.通过下标查询元素,效率高​ 2.通过下标修改元素,效率高​ **查改快**​ 在需要扩容的时候:添加慢,删除慢,插入元素慢......
  • 【数据结构】C语言实现单链表的基本操作
    单链表基本操作的实现导言大家好,很高兴又和大家见面啦!!!在上一篇中,我们详细介绍了单链表的两种创建方式——头插法与尾插法,相信大家现在对这两种方式都已经掌握了。今天咱们将继续介绍单链表的基本操作——查找、插入与删除。在开始今天的内容之前,我们先通过尾插法创建一个单链表,如......
  • 排序组件
    排序组件快速使用第一步:视图类需继承GenericAPIView或其子类#以图书类为例classBookView(ViewSetMixin,ListAPIView):queryset=Book.objects.all()serializer_class=BookSerializer 第二步:导入排序类相关组件#drf内置了排序类fromrest_framework.f......
  • 数据结构之<树>的介绍
    树的基本概念在数据结构中,树(Tree)是一种层次结构,由节点和边组成。树的基本概念包括根节点、子节点、父节点、兄弟节点等。节点拥有零个或多个子节点,除了根节点外,每个节点有且仅有一个父节点。树的层数称为树的高度。子节点以及它后续节点所形成的数称为子树。1.二叉树(BinaryTree)二......