重复造轮子系列--桶排序

理解了基数排序,也就理解了桶排序。

桶排序就是基数排序的一种优化,从MSD开始,即取最高位来排一次序,如果最高位没有重复(意味着没有冲突需要处理),是算法的最佳状态,O(n)。

如果有冲突,就将冲突的元素存放到对应的桶里(代码就是一个链表或者数组或者stl容器),然后对每个桶进行一次插入排序,平均情况的话冲突很小的,桶里的元素的数量就不多,速度很快,

如果冲突集中在几个桶甚至一个桶里,那么就出现了算法的最差情形,也就是O(n^2)的复杂度。

下面是例子代码实现:

 1 template<typename _InIt>
 2 void bucket_sort(_InIt first, _InIt last, short decimal_bits) {
 3     typedef typename iterator_traits<_InIt>::value_type _ValueType;
 4     typedef typename vector<_ValueType>::iterator _Iterator;
 5
 6     int n = last - first;
 7     if (n <= 0) return;
 8
 9     vector< vector<_ValueType> > v(n, vector<_ValueType>());
10     _InIt it = first;
11     for(; it!=last; ++it) {
12         int i = bucket_index(*it, decimal_bits);
13         v[i].insert(v[i].begin(), *it);
14     }
15
16     typename vector< vector<_ValueType> >::iterator ite = v.begin();
17     less_equal<_ValueType> cmp;
18     it = first;
19
20     for(; ite!=v.end(); ++ite) {
21         if (ite->empty()) continue;
22         insert_sort(ite->begin(), ite->end(), cmp);
23         for(_Iterator _it=ite->begin(); _it!=ite->end(); ++_it)
24             *it++ = *_it;
25     }
26 }

上面默认采用了递增排序,如果要递减,就反向迭代并使用greater_equal。

为图简便,这里把元素直接放到输入的容器了。

整个测试程序:

 1 #include <iostream>
 2 #include <vector>
 3 #include <cassert>
 4
 5 using namespace std;
 6
 7 template<typename _InIt, typename _Func>
 8 void __insert_sort(_InIt first, _InIt last, _Func& Func) {
 9     typedef typename iterator_traits<_InIt>::value_type _ValueType;
10
11     for (_InIt it = first+1; it != last+1; ++it) {
12         _ValueType key = *it;
13         _InIt ite = it - 1;
14         for (; (ite - first)>=0 && !Func(*ite, key); --ite)
15             *(ite + 1) = *ite;
16         *(ite + 1) = key;
17     }
18 }
19
20 template<typename _InIt, typename _Func>
21 void insert_sort(_InIt first, _InIt last, _Func& Func) {
22     __insert_sort(first, last-1, Func);
23 }
24
25 int bucket_index(int num, int p) {
26     assert(num >0 && p > 0);
27     int s = 1;
28     while (p-->0) s *= 10;
29     return ((num%s)*10)/s;
30 }
31
32 template<typename _InIt>
33 void bucket_sort(_InIt first, _InIt last, short decimal_bits) {
34     typedef typename iterator_traits<_InIt>::value_type _ValueType;
35     typedef typename vector<_ValueType>::iterator _Iterator;
36
37     int n = last - first;
38     if (n <= 0) return;
39
40     vector< vector<_ValueType> > v(n, vector<_ValueType>());
41     _InIt it = first;
42     for(; it!=last; ++it) {
43         int i = bucket_index(*it, decimal_bits);
44         v[i].insert(v[i].begin(), *it);
45     }
46
47     typename vector< vector<_ValueType> >::iterator ite = v.begin();
48     less_equal<_ValueType> cmp;
49     it = first;
50
51     for(; ite!=v.end(); ++ite) {
52         if (ite->empty()) continue;
53         insert_sort(ite->begin(), ite->end(), cmp);
54         for(_Iterator _it=ite->begin(); _it!=ite->end(); ++_it)
55             *it++ = *_it;
56     }
57 }
58
59 template<typename T>
60 void print(const vector<T>& v) {
61     for(vector<int>::const_iterator it=v.begin(); it != v.end(); it++)
62            cout << *it << " ";
63         cout << endl;
64 }
65
66 int main() {
67     int lst[] = {78,17,39,26,72,94,21,12,23,68};
68     vector<int> v(lst, lst+10);
69
70     bucket_sort(v.begin(), v.end(), 2);
71     print(v);
72
73     return 0;
74 }

写完这些例子,在准备刷难度高一点的算法之前,小小的总结了一下。

这些例子的适用范围都很小,仅供学习使用,比如上面的插入排序,迭代器仅仅对vector有效,如果有自己的数据结构容器,最好是为它实现一个随机访问迭代器。

时间: 2024-08-07 21:01:36

重复造轮子系列--桶排序的相关文章

重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印

重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端系统打印经常使用的场景. 1.一种是类似票务方面的系统需要打印固定格式的票据.比如景点门票.车票.电影票. 这种基本是根据模板调整位置套打. 2.还有一种是交易小票,比如商超POS小票,打印长度会随着内容的大小自动伸缩. 这种就不仅仅是固定格式的套打了,还得计算数据行以适应不同的打印长度. 打印方式

重复造轮子系列--计数,基数排序

计数,基数的中文读音都一样,这翻译的人还嫌我们计算机不够乱,真的想吐槽. 不管了,毕竟代码还是不一样的. 1.计数排序(counter sort): 通过一个上限来统计集合里的数值(或者其他非数值类型映射的数值),并累计比小于自己(包括)的数值的统计的个数,从而形成排序的索引(也就是前面有多少个小于我的,我的位置就确定了). 普通计数排序代码:(仍需优化,valuetype默认是整数类型) 1 template<typename _InIt> 2 void counter_sort(_InIt

重复造轮子系列--插入排序和归并排序

囧,道理很简单,实践起来却不容易. 因为编程语言跟算法描述数据结构并不能完全一致,所以理论到实践还是有些出入的. 下面的例子是没有哨兵位置的实现: 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <cassert> 5 6 using namespace std; 7 8 template<typename _InIt, typename _Func

重复造轮子系列--内存池(C语言)

mem_pool.h 1 #ifndef MEM_POOL_H_ 2 #define MEM_POOL_H_ 3 4 typedef struct MemBlock { 5 struct MemBlock* next; 6 int size; 7 void *ptr; 8 } MemBlock; 9 10 typedef unsigned char byte; 11 12 // 8 16 32 64 128 256 512 1024 2048 4096 13 // 1 2 4 8 16 32 6

重复造轮子系列--dijkstra算法

spf.h 1 #ifndef SPF_H_ 2 #define SPF_H_ 3 4 5 typedef struct { 6 int length; 7 char src; 8 char dst; 9 char prev_hop; 10 } dijkstra; 11 12 #define MAX 1024 13 #define NODE_NUM 5 14 #define TRUE 1 15 #define FALSE 0 16 17 #endif spf.c 1 #include <stdi

重复造轮子系列--字符串常用操作(C语言)

xstring.h 1 #ifndef XSTRING 2 #define XSTRING 3 4 typedef struct xstring { 5 char *str; 6 struct xstring *next; 7 } xstring; 8 9 10 ////////////////////////////////////////////////////////////////////////// 11 void* allocate(size_t size); 12 13 #ifde

GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便地使用所有版本的Android动作栏的设计模式. 对于Android 4.0及更高版本,ActionBarSherlock可以自动使用本地ActionBar实现,而对于之前没有ActionBar功能的版本,基于Ice Cream Sandwich的自定义动作栏实现将自动围绕布局.能够让开发者轻松开发

避免重复造轮子的UI自动化测试框架开发

一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览器的基本上底层都是selenium,驱动无线app和浏览器基本是appium.monkey之类的,底层都是基于官方支持的自动化测试框架开发而来,然后上层又做了各种封装 首先在开始计划开发自动化时,第一步是了解目前已有的自动化开发技术,上面说了,最底层的就那几种,根据实际要去测试的业务需求选择合适的自

第27篇 重复造轮子---模拟IIS服务器

在写程序的时候,重复造轮子是程序员的一个大忌,很多人对重复造轮子持有反对的态度,但是我觉得这个造轮子的过程,是对于现有的知识的一个深入的探索的过程,虽然我们不可能把轮子造的那么的完善,对于现在有的东西是一个更好的理解和使用.   当你打开一个网页时,你会看到一个html页面的呈现,当然这是一个完整的Http的请求和响应的过程,无非是对HTML+CSS+JS一个综合的产物,在这个过程中浏览器请求数据的过程中会发出一个有一个格式的字符串(基于http协议生成的http请求报文),服务器在接收这样的一