各种排序比较

集合num = {a1,a2,...an},将其按非递减排序。

1、插入排序

//increasing
void insert_sort(int num[],int n)
{
	for(int i = 1;i < n;i++)
	{
		int j = i,tmp = num[i];
		for(;j && num[j - 1] > tmp;j--)
			num[j] = num[j - 1];
		num[j] = tmp;
	}
}
最好情况:num[]排序前就是非递减,那么时间复杂度为O(n)

最坏情况:num[]排序前为非递增,那么时间复杂度为O(n^2)

所以插入排序时间复杂度为O(n^2),空间复杂度为O(1)


2、选择排序

void select_sort(int num[],int n)
{
	for(int i = 0;i < n;i++)
		for(int j = i + 1;j < n;j++)
			if(num[i] > num[j])
				swap(num[i],num[j]);
}
最好情况:num[]排序前就是非递减,那么时间复杂度为O(n)

最坏情况:num[]排序前为非递增,那么时间复杂度为O(n^2)

所以选择排序时间复杂度为O(n^2),空间复杂度为O(1)


3、冒泡排序

void bubble_sort(int num[],int n)
{
	for(int i = 0;i < n;i++)
		for(int j = n - 1;j >= i + 1;j--)
			if(num[j - 1] > num[j])
				swap(num[j],num[j - 1]);
}
最好情况:num[]排序前就是非递减,那么时间复杂度为O(n)

最坏情况:num[]排序前为非递增,那么时间复杂度为O(n^2)

所以冒泡排序时间复杂度为O(n^2),空间复杂度为O(1)


4、归并排序

/*
 * p <= q < r
 * num[p...q]及num[q + 1...r]已经有序
 */
void merge(int num[],int p,int q,int r)
{
	int tmp[N],n,i,j;
	n = 0;
	i = p,j = q + 1;
	while(i <= q && j <= r)
		if(num[i] < num[j])
			tmp[n++] = num[i++];
		else
			tmp[n++] = num[j++];
	while(i <= q)
		tmp[n++] = num[i++];
	while(j <= r)
		tmp[n++] = num[j++];
	for(i = 0;i < n;i++)
		num[i + p] = tmp[i];
}

/*
 * 将num[p...q]排序
 */
void merge_sort(int num[],int p,int r)
{
	if(p < r)
	{
		int q = (p + r) / 2;
		merge_sort(num,p,q);
		merge_sort(num,q + 1,r);
		merge(num,p,q,r);
	}
}
因为均为num[]已经有序与否,merge()函数一定是要执行的,它的时间复杂度为O(n),所以合并排序最好与最坏的时间复杂度均为为O(nlgn),合并排序的时间复杂度就为O(nlgn),它的空间复杂度为O(n)。


5、堆排序

int left(int x)
{
	return x << 1;
}

int right(int x)
{
	return (x << 1) + 1;
}

int parent(int x)
{
	return x >> 1;
}

/*
 *递归式:O(lgn)
 */
/*
void max_heapify(int num[],int i,int heapsize)
{
	int r,l,lmax;
	l = left(i),r = right(i);
	if(l <= heapsize && num[i] < num[l])
		lmax = l;
	else
		lmax  = i;
	if(r <= heapsize && num[lmax] < num[r])
		lmax = r;
	if(lmax != i)
	{
		swap(num[i],num[lmax]);
		max_heapify(num,lmax,heapsize);
	}
}
*/


void max_heapify(int num[],int i,int heapsize)
{
	int r,l,lmin,res;
	res = num[i];
	while(1)
	{
		l = left(i),r = right(i);
		if(l > heapsize)//无左结点,即此时i为叶子结点
			break;
		lmin = l;
		if(r <= heapsize && num[lmin] < num[r])
			lmin = r;
		if(res >= num[lmin])//res比两棵子树的根都要小,就不需要调整了
			break;
		num[i] = num[lmin];
		i = lmin;
	}
	num[i] = res;
}

/*
 * num[n / 2]为倒数第一个非叶子结点
 */
void build_max_heap(int num[],int n)
{
	for(int i = n / 2;i >= 1;i--)
		max_heapify(num,i,n);
}

void heap_sort(int num[],int n)
{
	int heapsize = n;
	build_max_heap(num,n);
	for(int i = n;i >= 2;i--)
	{
		swap(num[1],num[heapsize]);
		max_heapify(num,1,--heapsize);
	}
}
max_heapify()函数的时间复杂度为O(lgn),堆排序最好与最好坏情况的时间复杂度均为O(nlgn),因为是原地排序,所以空间复杂度为O(1)。


6、快速排序

int partition(int num[],int low,int high)
{
	int key = num[low];
	while(low < high)
	{
		while(low < high && num[high] > key)
			high--;
		num[low] = num[high];
		while(low < high && num[low] <= key)
			low++;
		num[high] = num[low];
	}
	num[low] = key;
	return low;
}

void quick_sort(int num[],int p,int r)
{
	if(p < r)
	{
		int q = partition(num,p,r);
		quick_sort(num,p,q - 1);
		quick_sort(num,q + 1,r);
	}
}
快速排序也应用了分治的思想,在partition()函数中每次都将第一个数作为关键字比较。快速排序的最坏情况是当num[]已经是非递减有序的时候,此时 partition()函数每次都要执行high - low次,所以此时时间复杂度退化为O(n^2),而快排一般的复杂度为O(nlgn),空间复杂度为O(lgn),即递归时栈所用。


7、快速排序的随机化版本

基本的快速排序当数据基本有序时,时间复杂度退化为O(n^2),其实这里我们可以进行一个随机优化。即partition()函数在取关键字key时,并不是取第一个,而是从num[low...high]中随机取一个作为关键字,这样就可以避免最坏情况。

int random_partition(int num[],int low,int high)
{
	if(low < high)
	{
		srand((unsigned)time(NULL));
		int tmp = rand() % (high - low)+ low;
		swap(num[low],num[tmp]);
	}
	return partition(num,low,high);
}

void random_quick_sort(int num[],int p,int r)
{
	if(p < r)
	{
		int q = random_partition(num,p,r);
		random_quick_sort(num,p,q - 1);
		random_quick_sort(num,q + 1,r);
	}
}


8、计数排序

计数排序假设n个输入元素中的每一个元素都是介于0到k之间的整数,k为某个整数。运用hash法,计算出每个元素在所有元素中的位置,最后直接放入到数组相应的位置上。

/*
 * 要排序的数在num[]中,都是在0~1000之间,排完序后存入num1
 */
void count_sort(int num[],int num1[],int n)
{
	int tmp[1001];
	memset(tmp,0,sizeof(tmp));
	for(int i = 0;i < n;i++)
		tmp[num[i]]++;
	for(int i = 1;i < 1001;i++)
		tmp[i] += tmp[i - 1];
	for(int i = 0;i < n;i++)
		num1[--tmp[num[i]]] = num[i];
}
计数排序不是比较排序算法,它的 时间复杂度为O(k + n),当k = O(n)时,它的复杂度即为O(n)。空间复杂度为O(k + n)。


9、桶排序

跟计数排序一样,桶排序也对输入亻了某种假设。桶排序假设输入由一个随机过程产生,该过程将元素均匀而独立地分页在区间[0,1)上。并把[0,1)区间分成n个大小相同的子区间,或称桶。然后将n个输入数据分页到各个桶当中去。然后对各个桶中的数据进行排序,最后按次序把各桶中的数据列出来即可。

void bucket_sort(double num[],int n)
{
	double tmp[100][100];
	memset(tmp,0,sizeof(tmp));
	for(int i = 0;i < n;i++)
	{
		int t = int(floor(num[i] * n));
		tmp[t][0] += 1;
		tmp[t][(int)tmp[t][0]] = num[i];
	}
	for(int i = 0;i < n;i++)
		if(tmp[i][0])
		{
			for(int j = 1;j <= (int)tmp[i][0];j++)
				for(int k = j + 1;k <= (int)tmp[i][0];k++)
					if(tmp[i][j] > tmp[i][k])
						swap(tmp[i][j],tmp[i][k]);
		}
	int t = 0;
	for(int i = 0;i < n;i++)
		for(int j = 1;j <= (int)tmp[i][0];j++)
			num[t++] = tmp[i][j];
	for(int i = 0;i < n;i++)
		printf("%.3lf ",num[i]);
	puts("");
}
桶排序关键时间关键就在第2个for循环,《算法导论》上讲,桶排序的期望时间是线性的。

各排序方法时间效率比较(时间单位ms)

1、20次每次随机生成10000组数据

数据插入排序选择排序冒泡排序归并排序堆排序快速排序计数排序
11966069374330
21966079223330
31706099493330
41786189443330
51726229643330
61766539774330
71776159563330
81776259544321
91736219554320
101766309534320
111756209553321
121746329524321
131736069483420
141686139583330
151806299553340
161786449543330
171736069514320
181716149414320
191736129533320
201766229463420
平均176.60620.20951.203.403.102.550.00


2、20次每次随机生成100000组数据

数据插入排序选择排序冒泡排序归并排序堆排序快速排序计数排序
11753450206959273941323
21738950172950394048293
31775449930949444042312
41738049762947533940312
51724049389958314041303
61792750816951113940292
71713249336950793940293
81716649378959253940333
91722449202971153940292
101788150673973343940293
111771552176969183941202
121739250451962503840292
131744849394959544042312
141715149347965853940292
151763850890952983940292
161757549348944303940292
171718849233941494044303
181718850872964104241293
191746250807969334041323
201761950601966713940292
平均17450.1550099.1595832.8039.441.0529.452.45

从表中可以看出,计数排序的平均时间是最好的,当然,计数排序需要有数据形式的限制。除了计数排序,快速排序的平均时间最好,而冒泡排序最差。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值