算法导论 第八章:线性时间排序

1.计数排序(Counting sort)

   其基本思想是:对于每一个元素x,确定小于x的元素的个数,然后直接将x放在输出数组的合适位置中。

其伪代码如下:

EG:

每个输入的元素的范围是0~k,k 不能太大,如果太大,分配给C的内存就会很大!当k=O(n)时,该算法的时间复杂度为:Θ(n)。此外计数排序是稳定排序,这一思想在接下来介绍的基数排序中很有用。

计数排序完整代码如下:

#include<iostream>
#include<climits>
using namespace std;

void Print(int *a)
{
	int n=a[0];
	for(int i=1;i<=n;i++)
		cout<<a[i]<<"   ";
	cout<<endl;
}
int *Transform(int *a,int n)
{
	int *A=new int[n+1];
	A[0]=n;
	for(int i=0;i<n;i++)
		A[i+1]=a[i];
	return A;
	}
int getMaxNum(int *a)
{
	int n=a[0];
	int max=INT_MIN;
	for(int i=1;i<=n;i++)
		if(a[i]>max)
			max=a[i];
	return max;
	}
int *CountingSort(int *A)
{
	int n=A[0];
	int *B=new int[n+1];
	int m=getMaxNum(A);
	int *C=new int[m+1];
	
	for(int i=0;i<=m;i++)
		C[i]=0;
	for(int i=1;i<=n;i++)
		C[A[i]]=C[A[i]]+1;
	for(int i=1;i<=m;i++)
		C[i]=C[i]+C[i-1];
	for(int i=n;i>=1;i--)
	{
		B[C[A[i]]]=A[i];
		C[A[i]]--;
		}
	B[0]=n;
	return B;
	}
int main()
{
	int a[]={2,5,3,3,24,12,5,8};
	int n=sizeof(a)/sizeof(int);
	int *A=new int[n+1];
	int *B=new int[n+1];   //store the sorted result
	A=Transform(a,n);  //a[0..n-1]->A[1..n];A[0]=a.length
	B=CountingSort(A);
	Print(B);
	return 0;
	}

运行结果:

2.基数排序(Radix sort)

对于每个数位采用计数排序,由于计数排序是稳定的,这将能保证基数排序的准确性。不过必须从最不重要的数位先开始排。

伪代码如下:


EG:


 注意:

1)给定n个d位数字(d-digit),每个数位可能取k中可能的值。基数排序算法总能以Θ(d(n+k))的时间正确对这些数进行排序。当d为常数,k=O(n)时,基数排序有线性运行时间。

2)给定n个b位数(b-bit)和任意的r≤b,基数排序能在Θ((b/r)(n+2^r))时间内正确对这些数进行排序。当b ≤ lgn 时,r=b; 当 b > lg n 时,r=Llgn」,这时的运行时间为Θ(bn/lgn).


基数排序完整代码如下:

#include<iostream>
#include<climits>
using namespace std;

void Print(int *a)
{
	int n=a[0];
	for(int i=1;i<=n;i++)
		cout<<a[i]<<"   ";
	cout<<endl;
}
int *Transform(int *a,int n)
{
	int *A=new int[n+1];
	A[0]=n;
	for(int i=0;i<n;i++)
		A[i+1]=a[i];
	return A;
	}
int getMaxNum(int *a)
{
	int n=a[0];
	int max=INT_MIN;
	for(int i=1;i<=n;i++)
		if(a[i]>max)
			max=a[i];
	return max;
	}
int *getDigit_d(int *a,int d)
{
	int n=a[0];
	int pow_d=1;
	int temp;
	int *dA=new int[n+1];

	for(int i=1;i<d;i++)
		pow_d*=10;

	for(int i=1;i<=n;i++)
	 {
		temp=a[i];
		temp=temp/pow_d;
		temp=temp%10;
		dA[i]=temp;
		} 
	dA[0]=n;
	}

int *CountingSort(int *A,int d)
{
	int n=A[0];
	int m;
	int *B,*C;
	int *digitA=new int[n+1];
	digitA=getDigit_d(A,d);

	m=getMaxNum(digitA);
	B=new int[n+1];
	C=new int[m+1];

	for(int i=0;i<=m;i++)
		C[i]=0;

	for(int i=1;i<=n;i++)
		C[digitA[i]]=C[digitA[i]]+1;

	for(int i=1;i<=m;i++)
		C[i]=C[i]+C[i-1];

	for(int i=n;i>=1;i--)
	{  
		B[C[digitA[i]]]=A[i];
		C[digitA[i]]--;
		}  
	B[0]=n;
	return B;
	}

int *RadixSort(int *a)
{
	int n=a[0];
	int t;
	int d=1;    //the digit of number
	int max=getMaxNum(a);
	t=max;
	while(1)
	{ 
		if(t>=10)
	 	{
			t=t/10;
			d++;
 			}
		else
			break;
 	 	}    //compute d

	for(int i=1;i<=d;i++) //sort a by d-digit using Counting Sort
		a=CountingSort(a,i);
	return a;
	}
int main()
{
	int a[]={2329,5457,8657,6839,4336,7520,2355};
	int n=sizeof(a)/sizeof(int);
	int *A=new int[n+1];
	int *B=new int[n+1];   //store the sorted result
	cout<<"Before sorting:"<<endl;
	A=Transform(a,n);  //a[0..n-1]->A[1..n];A[0]=a.length
	Print(A);
	cout<<"After sorting"<<endl;
	B=RadixSort(A);
	Print(B);

	return 0;
	}
运行结果:


3.桶排序(Bucket sort)

   当桶排序的输入符合均匀分布时,即可以线性时间运行。伪代码如下:

EG:

性能分析:

运行时间为:

所以有:

其中,

                

即使输入不均匀时,只要各桶的尺寸的平方和与总元素个数成线性关系,则仍可以以线性时间运行。

桶排序完整代码如下:

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<iomanip>
using namespace std;

struct Node{
	double   key;
	Node *next;
	};
void Print(double *a,int n)
{
	for(int i=0;i<n;i++)
	{ 
		cout<<setw(10)<<a[i];
		if((i+1)%10==0)
			cout<<endl;
		}
	cout<<endl;
	}
void OutBucketData(Node *b,double *a,int n)
{//rearrange the Bucket into array a
	Node *p;
	int j=0;
	for(int i=0;i<n;i++)
	{  
		p=b[i].next;
		if(p==NULL)
			continue;
		while(p !=NULL)
	  	{	
			a[j++]=p->key;
			p=p->next;
		  	}
		} 
}
void InsertDataToBucket(Node *bucket,int i,double k)
{//using linked list to insert k into bucket[i] in an ascending.
	Node *s,*q;

	s=new Node();   //store k into node s
	s->key=k;
	s->next=NULL;

	q=&bucket[i];
	while(q->next !=NULL )
	{
		if(q->key <= s->key  && s->key < q->next->key)   //insert node s
		{
			s->next=q->next;
			break;
			 }
		q=q->next;
		}   
		q->next=s;
	}
void BucketSort(double *a,int n)
{
	Node *Bucket =new Node[n];
	for(int i=0;i<n;i++)
	{    
		Bucket[i].key=(double)i/n;
		Bucket[i].next=NULL;
	 	}  
	for(int i=0;i<n;i++)
	{ 
		int bucket_i=a[i]*n;
		InsertDataToBucket(Bucket,bucket_i,a[i]);
	 	}    
		OutBucketData(Bucket,a,n); 
	}
void getRandNum(double *a,int n)
{
	srand(time(NULL));
	for(int i=0;i<n;i++)
		a[i]=(double)rand()/RAND_MAX;
	}
int main()
{
	//double a[]={0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.68};
	//int n=sizeof(a)/sizeof(double);
	int n;
	cout<<"Please input the number n:";
	cin>>n;
	double *a=new double[n];
	getRandNum(a,n);
	cout<<"Before sorting with Bucket sort:"<<endl;
	Print(a,n);
	BucketSort(a,n);
	cout<<"After sorting with Buket sort"<<endl;
	Print(a,n);
	
	return 0;
	}
运行结果:



【注:如若有错误,请指正~~~】




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值