排序算法-11-桶排序

桶排序

前面介绍的计数排序基数排序是两种线性时间(即时间复杂度为 O ( n ) O(n) O(n))的排序算法,这一篇介绍另外一种也是线性时间的排序方法:桶排序。

原理

桶排序的前提是要排序的元素呈均匀分布。其原理是先将要排序的元素分到有限数量的桶里,然后对每个桶里的元素分别排序,最后再按顺序输出。步骤如下:

  1. 设置一定数量的空桶。
  2. 把每个元素放到对应的桶去。因为是均匀分布,所以每个桶的元素数量都差不多。
  3. 对每个不是空的桶进行桶内排序。桶内排序可以采用别的排序算法,如插入排序、计数排序等,可以根据实际情况需要进行选择,也可以用递归的方式继续调用桶排序。
  4. 从不是空的桶里把元素再放回原来的序列中。因为每个桶内都是排好序的,桶与桶也是排好序的,所以最后输出就是排好序的。

举一个简单的例子更方便理解桶排序的原理。比如对一个班的学生成绩排序,成绩范围是0 ~ 100分,分10个桶,0 ~ 9分的放一个桶,10 ~ 19分的放一个桶,以此类推,90 ~ 100分的放一个桶。先在每个桶内进行排序,然后按桶的顺序输出,最终成绩就排好序了。(这里要注意一般情况下学生成绩都是正态分布,而不是均匀分布,所以桶排序的效率并不一定好。)

实现

按照以上原理我们来用代码实现。

下面就是用C语言实现的代码。

  • 要排序的数组a有n个元素。分两个函数来实现。
  • bucket_sort 中先准备好 bucket_num 个桶,然后找到a中元素的最大值和最小值,计算出桶与桶之间的间隔,再将每个元素都放入应该放入的桶中。接着对每个桶里的元素调用插入排序 insertion_sort 进行桶内排序,最后把桶中元素按顺序输出。
  • bucket_num 的值是可以进行调整的。
  • insertion_sort 进行链表版的插入排序,可参考数组版的插入排序对比。同样是分为有序区与无序区,每次从无序区拿出一个插入到有序区。值得注意的是这里在有序区头部增加了一个元素以方便后面的插入,因链表头*head有可能被改掉。
/* 元素结构体 */
typedef struct _elem {
	int value;
	struct _elem *next;
} elem_t;

void insertion_sort(elem_t **head)
{
	elem_t *q, *p, *p_next, *r;

	/* 表空或者只有一个节点,不需排序 */
	if (*head==NULL || (*head)->next==NULL) return;

	/* 分有序区和无序区,每次从无序区拿出一个插入到有序区 */
	/* 在有序区头部增加一个元素以方便后面的插入,因*head可能被改掉 */
	q = (elem_t*)malloc(sizeof(elem_t));
	if (q == NULL) return;
	/* q指向有序区的头,有序区开始只有一个元素 */
	q->next = *head;
	/* p指向无序区的头,无序区从第二个元素开始 */
	p = q->next->next;
	/* 将有序区与无序区分开 */
	q->next->next = NULL;

	while (p) { //无序区还存在有元素
		p_next = p->next; //记录无序区的下一个元素

		/* 在已排好序的链表q中找到p应该插入的位置 */
		r = q;
		while (r->next && r->next->value <= p->value) r=r->next;

		/* 将p插入到r前面 */
		p->next = r->next;
		r->next = p;

		p = p_next; //处理下一个无序区中的元素
	}

	*head = q->next;
	free(q);
}

/* 假设a中元素值均匀分布 */
void bucket_sort(int a[], int n)
{
	int i, j;
	int max, min, step;
	int bucket_num = 5;
	elem_t *p, *q;

	/* 申请bucket_num个桶 */
	elem_t** b = (elem_t**)malloc(bucket_num*sizeof(elem_t*));
	if (b==NULL) return;

	/* 桶置空 */
	for (i=0; i<bucket_num; i++) {
		b[i] = NULL;
	}

	/* 找出数组a中的最大值和最小值 */
    max = min = a[0];
    for (i=1; i<n; i++) {
        if (a[i] > max) max = a[i];
        if (a[i] < min) min = a[i];
	}

	/* 计算桶与桶的间隔 */
	step = (max - min) / bucket_num + 1;

	for (i=0; i<n; i++) {
		/* 计算a[i]属于哪个桶 */
		j = (a[i] - min)/step;
		/* 用a[i]的值构造元素p */
		p = (elem_t*)malloc(sizeof(elem_t));
		if (p==NULL) break;
		p->value = a[i];
		p->next = NULL;
		/* 将元素插入对应桶的最后 */
		if (b[j]==NULL) {
			b[j] = p;
		} else {
			q = b[j];
			while (q->next) q = q->next;
			q->next = p;
		}
	}

	/* 对每个桶中的元素进行排序 */
	for (i=0; i<bucket_num; i++) {
		insertion_sort(&b[i]);
	}

	/* 桶中元素按顺序输出到a中,并释放元素空间 */
	j = 0;
	for (i=0; i<bucket_num; i++) {
		q = b[i];
		while (q) {
			a[j++] = q->value;
			p = q;
			q = q->next;
			free(p);
		}
	}

	free(b);
}

为了验证此函数的效果,加上了如下辅助代码,对3个数组进行排序,运行结果在最后,可见排序成功。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE_ARRAY_1 5
#define SIZE_ARRAY_2 6
#define SIZE_ARRAY_3 20

void show_array(int a[], int n);
void bucket_sort(int a[], int n);

void main()
{
	int array1[SIZE_ARRAY_1]={11,224,32,9,0};
	int array2[SIZE_ARRAY_2]={1000,53,229,1,229,222};
	int array3[SIZE_ARRAY_3];

	for(int i=0; i<SIZE_ARRAY_3; i++) {
		array3[i] = (int)((10000.0*rand())/(RAND_MAX+1.0));
	}

	printf("Before sort, ");
	show_array(array1, SIZE_ARRAY_1);
	bucket_sort(array1, SIZE_ARRAY_1);
	printf("After sort, ");
	show_array(array1, SIZE_ARRAY_1);

	printf("Before sort, ");
	show_array(array2, SIZE_ARRAY_2);
	bucket_sort(array2, SIZE_ARRAY_2);
	printf("After sort, ");
	show_array(array2, SIZE_ARRAY_2);

	printf("Before sort, ");
	show_array(array3, SIZE_ARRAY_3);
	bucket_sort(array3, SIZE_ARRAY_3);
	printf("After sort, ");
	show_array(array3, SIZE_ARRAY_3);
}

void show_array(int a[], int n)
{
	if(n>0)
		printf("This array has %d items: ", n);
	else
		printf("Error: array size should bigger than zero.\n");

	for(int i=0; i<n; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}

运行结果:

Before sort, This array has 5 items: 11 224 32 9 0
After sort, This array has 5 items: 0 9 11 32 224
Before sort, This array has 6 items: 1000 53 229 1 229 222
After sort, This array has 6 items: 1 53 222 229 229 1000
Before sort, This array has 20 items: 8401 3943 7830 7984 9116 1975 3352 7682 2777 5539 4773 6288 3647 5134 9522 9161 6357 7172 1416 6069
After sort, This array has 20 items: 1416 1975 2777 3352 3647 3943 4773 5134 5539 6069 6288 6357 7172 7682 7830 7984 8401 9116 9161 9522

分析

时间复杂度

可以证明桶排序的时间复杂度为 O ( n ) O(n) O(n),因为其假设输入数据为均匀分布。

空间复杂度

因为桶排序需要另外的空间来存储桶和桶中元素,其量级为 n n n,所以桶排序的空间复杂度为 O ( n ) O(n) O(n)

稳定性

稳定。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值