算法导论-- 线性时间排序(计数排序、基数排序、桶排序)

线性时间排序

前面介绍的几种排序,都是能够在复杂度nlg(n)时间内排序n个数的算法,这些算法都是通过比较来决定它们的顺序,这类算法叫做比较排序 。下面介绍的几种算法用运算去排序,且它们的复杂度是线性时间。

——————————————————————————————————————

1.计数排序

计数排序采用的方法是:对每个元素x,去确定小于x的元素的个数,从而就可以知道元素x在输出数组中的哪个位置了。

计数排序的一个重要性质是它是稳定的,即对于相同的两个数,排序后,还会保持它们在输入数组中的前后顺序,这也是下面基数排序的基础。

虽然复杂度比之间的算法减小了,但在算法实现过程中,它要求输入数组数据位于一个区间[0,k]内,因为要遍历数组,计算[0,k]间的每个数在输入数组中的个数,这也算是计数排序的缺点吧!

下面是调试的程序,可直接运行,详细过程看下《算法导论》

#include<STDIO.H>
#define  K 10                   //数据在[0,K]之间
int A[]={2,7,3,5,3,2,9};
int B[20]={0};                  //输出数组
int C[20]={0};                  //计数数组
int Length=sizeof(A)/sizeof(A[0])-1;
void Count_Sort(int *A,int *B,int k)
{
   int j;
   for (j=0;j<=Length;j++)       //为每个数计个数
   {
	   C[A[j]]=C[A[j]]+1;
   }

   for (j=1;j<=K;j++)           //计算有多少元素小于等于j
   {
	   C[j]=C[j]+C[j-1];
   }

   for (j=Length;j>=0;j--)      //倒叙输出数组,保证了数据是稳定的
   {
       B[C[A[j]]]=A[j];
       C[A[j]]=C[A[j]]-1;     //A[j]输出,对应计数数组元素减1。
   }
}
int main()
{
	int i;
	Count_Sort(A,B,K);
	for (i=1;i<=Length+1;i++)
	{
		printf("%3d",B[i]);
	}
	printf("\n");
	return 0;
}

———————————————————————————————————

2.基数排序

基本思想:对N个d位的数据排序,与传统的想法不同,它是先从最低有效位开始排。

这里必须保证每次排序是稳定的,即对相同的数据,输出的顺序必须与输入的顺序相同。

实现例程:

#include <STDIO.H>
#include <string.h>
int A[]={329,457,657,839,436,720,355};    //要排序的数组
int Length=sizeof(A)/sizeof(A[0])-1;
void Count_Sort(int *A2,int *B)    //计数排序
{
	int j;
    int C[10]={0};                 //计数数组,数字在[0,9]之间
	for (j=0;j<=Length;j++)       //为每个数计个数
	{
		C[A2[j]]=C[A2[j]]+1;
	}

	for (j=1;j<=10;j++)           //计算有多少元素小于等于j
	{
		C[j]=C[j]+C[j-1];
	}

	for (j=Length;j>=0;j--)      //倒叙输出数组,保证了数据是稳定的
	{
		B[C[A2[j]]]=A[j];       //参照C[A2[j]]的大小,对数组A[j]输出
		C[A2[j]]=C[A2[j]]-1;
	}
}

void Radix_Sort(int *A,int d)
{
	int i,j,k,temp;
	int A2[10]={0};               //存放各个位
    int B[20]={0};                  //输出数组
   for(i=1;i<=d;i++)
   {

	   for (j=0;j<=Length;j++)
	   {
		   temp=A[j];
		   k=i;
		    while(k>1)
			{
                temp=temp/10;
			   	k--;
			}
			A2[j]=temp%10;	    //取指定的位存到A2[j],等待排序
	   }
	   Count_Sort(A2,B);
       memcpy(A,&B[1],(Length+1)*4);
   }
}
int main()
{
   int j;
   Radix_Sort(A,3);
   for (j=0;j<=Length;j++)
   {
	   printf("%5d\n",A[j]);
   }

}

———————————————————————————————————————————————————————————————————————————

3.桶排序

桶排序是假设输入数据服从均匀分布,平均情况下也是线性时间。假设输入数据是由一个随机过程产生,该过程将元素均匀,独立地分布在[0,1)区间上。

它将[0,1)区间划分为n个相同大小的子区间,称为“桶”,然后遍历输入的数据,分别放入到指定的桶中(放入桶中的同时,有个链表插入排序的过程)。最后依次把各个桶中数据输出即可。

例程:

#include <STDIO.H>
#include <STDLIB.H>
int A[]={78,17,39,26,72,94,21,12,23,68};//假如输入数据平均分布在[0,99]区间内
int Length = sizeof(A)/sizeof(A[0])-1;
typedef struct Node      //链表单元结构
{
   int num;
   struct Node *next;
}Node;
Node *Bucket[10]={0};       //分成10个桶,即10个小区间
void Bucket_Sort(int *A)
{
   int i;
   int a;
   Node * temp=NULL,*Pre_temp=NULL;
   for (i=0;i<=Length;i++)               //遍历输入数组,放入到指定的桶中
   {
	   a = (int)(A[i]/10);
	   if(Bucket[a] == 0)
	   {
		  Bucket[a]=(Node *)malloc(sizeof(Node));
		  Bucket[a]->num=A[i];
		  Bucket[a]->next=NULL;
	   }
	   else                                 //对非空链表插入排序
	   {
		   temp=Pre_temp=Bucket[a];
		   while(A[i] > temp->num)
		   {
			  Pre_temp=temp;
              temp=temp->next;
			  if (temp==NULL)
			    break;
		   }

		   if (temp == NULL)            // 插入到最后一个位置
		   {
			   temp=(Node *)malloc(sizeof(Node));
			   temp->num=A[i];
			   temp->next=NULL;
			   Pre_temp->next=temp;
		   }
		   else if (temp == Bucket[a])  //插入到第一个位置
		   {
			   temp=(Node *)malloc(sizeof(Node));
			   temp->num=A[i];
			   temp->next=Bucket[a];
			   Bucket[a]=temp;
		   }
		   else                         //插入到中间位置
		   {
			   temp=(Node *)malloc(sizeof(Node));
			   temp->num=A[i];
			   temp->next=Pre_temp->next;
			   Pre_temp->next=temp;
		   }
	   }

   }

}
void Free_List(Node * head)      //释放链表结构内存
{
	Node *N=head;
     while (head!=NULL)
     {
		 N=head->next;
		 free(head);
		 head=N;
     }
}
int main()
{
	Node * temp=NULL;
	int i=1;
    Bucket_Sort(A);
    for(i=0;i<=9;i++)   //依次输出各个桶中的数据
	{
		temp=Bucket[i];
		while(temp!=NULL)
		{
			printf("%3d\n",temp->num);
			temp=temp->next;
		}
		Free_List(Bucket[i]);  //释放第i个桶的内存
	}
	return 0;
}
				
时间: 2024-10-11 15:51:27

算法导论-- 线性时间排序(计数排序、基数排序、桶排序)的相关文章

【经典算法】线性时间排序

在计算机科学中,排序是一门基础的算法技术,许多算法都要以此作为基础,不同的排序算法有着不同的时间开销和空间开销.排序算法有非常多种,如我们最常用的快速排序和堆排序等算法,这些算法需要对序列中的数据进行比较,因为被称为基于比较的排序. 基于比较的排序算法是不能突破O(NlogN)的.简单证明如下: N个数有N!个可能的排列情况,也就是说基于比较的排序算法的判定树有N!个叶子结点,比较次数至少为log(N!)=O(NlogN)(斯特林公式). 而非基于比较的排序,如计数排序,桶排序,和在此基础上的基

排序算法(4)-线性时间排序

在前面三节排序算法中,我们分别分析了不同策略,思想用于排序,而这些算法都是基于数据间的比较来确定顺序的.假设我不用比较,换一种思路,那么就可以达到时间复杂度为O(n)的排序算法,当然是以付出额外的空间为代价的. 一.基本思想 线性时间排序的算法思想: (1):在计数排序中,利用比x小或等的元素个数和的来确定x位置.比如2 5 4 9 1 6.9比其余5个数都大,那就说明9 在排序后的第6个位置,这样我们只要得到比某个数大的元素个数就能得到元素在排序后数组中的位置了. (2):在桶排序中,是通过映

【算法导论学习-014】计数排序(CountingSortTest)

参考:<算法导论>P194页 8.2节 Counting sort 1.Counting sort的条件 待排序数全部分布在0~k之间,且k是已知数:或者分布在min~max之间,等价于分布在0~max-min之间,max和min是已知数. 2.java 实现 /** * 创建时间:2014年8月17日 下午3:22:14 项目名称:Test * * @author Cao Yanfeng * @since JDK 1.6.0_21 类说明: 计数排序法,复杂度O(n), 条件:所有数分布在0

基数排序(桶排序) 不同方法

详细解释:算法导论/数据结构书 1.链式基数排序 //n个数,每个数有g个关键字//排序:从最后的关键字开始到最前的关键字//分配+收集//每个关键字分配+收集需要n+n时间,而共有g个关键字,时间复杂度为O(2ng),效率很高.//如果用数组,数据集中在一个区间,则区间的长度很长,另外的区间的内存浪费了.//用链表可以解决这个问题,且数据不需要先后顺序,所以无必要非要用数组 1 #include <stdio.h> 2 #include <stdlib.h> 3 //个数 4 #

C# 插入排序 冒泡排序 选择排序 高速排序 堆排序 归并排序 基数排序 希尔排序

以下列出了数据结构与算法的八种基本排序:插入排序 冒泡排序 选择排序 高速排序 堆排序 归并排序 基数排序 希尔排序,然后是測试的样例.代码位置:http://download.csdn.net/detail/luozuolincool/8040027 排序类: public class Sortings { //插入排序 public void insertSort(int[] array) { int temp = 0; int index = 0; for (int i = 0; i <

排序算法大集锦_线性时间_计数排序

这个之前在<编程珠玑>上面看到过,当时就感觉特别神奇! 速度突破了其他排序算法的下限 后来在<算法导论>上面又看到了,感触颇深!所以一定好好啃透<算法导论> 这一系列博客的特点就是--给出每趟排序的结果 本来想着好好写一下过程,弄个图片什么的,不过觉得网上的解析太多了,都比较好,所以这些博客就算是对自己的总结吧. #include <stdio.h> #include <string.h> int a[10]={2,8,6,7,3,3,1,9,6

算法导论 第6章 堆排序(简单选择排序、堆排序)

堆数据结构实际上是一种数组对象,是以数组的形式存储的,可是它能够被视为一颗全然二叉树,因此又叫二叉堆.堆分为下面两种类型: 大顶堆:父结点的值不小于其子结点的值,堆顶元素最大 小顶堆:父结点的值不大于其子结点的值,堆顶元素最小 堆排序的时间复杂度跟合并排序一样,都是O(nlgn),可是合并排序不是原地排序(原地排序:在排序过程中,仅仅有常数个元素是保存在数组以外的空间),合并排序的全部元素都被复制到另外的数组空间中去,而堆排序是一个原地排序算法. 1.在堆排序中,我们通常使用大顶堆来实现,因为堆

基数排序/桶排序-单链表实现

今天下午编程实现了基数排序(桶排序),只能说一千个人有一千个哈姆雷特,因此,一千个人可能有一千种基数排序的实现方式,无论是用数组,栈,队列,单链表(都是线性表哦, 好巧,哈哈).重要的是理解该排序算法的思路后,自己也就可以尝试着慢慢写出来了.时间关系,暂且只给出跟人代码(面试黄金月),以后有机会再补充实现思路.新手出道,代码可读性不要期望太高,多包涵,相信以后自己会进步的. typedef struct listnode // 定义节点 { int data; struct listnode *

从0开始学算法--排序(1.8桶排序)

算法理解: 桶排序是对计数排序的一种优化,在计数排序中x应该放在计数数组下表为x的位置上,这样如果重复数字较少,计数数组每个位置的利用率就非常小. 桶排序是将一系列大小近似的数字放在一个位置(每个桶维护一条有序的链表),这样提高每个位置的利用率,以提高效率. 以A[]={1,21,23,41,49}为例 假设每个桶里有10中元素,那么桶排序的结构如下图所示. #include <algorithm> #include <iostream> #include <cstring&