啊哈算法(一):排序

1 桶排序

1.1 算法

例如:将5 3 5 2 8 从大到小排序。

这个算法就好比有 11 个桶,编号从 0~10。每出现一个数,就在对应编号的桶中放一个小旗子,最后只要数数每个桶中有几个小旗子就 OK 了。例如 2 号桶中有 1 个小旗子,表示2 出现了一次;3 号桶中有 1 个小旗子,表示 3 出现了一次;5 号桶中有 2 个小旗子,表示 5出现了两次;8 号桶中有 1 个小旗子,表示 8 出现了一次。

1.2 代码

#include <stdio.h> 

int main() {
	int a[11],i,j,t;
	for(i=0;i<=10;i++)
		a[i]=0;        //初始化为0 

	for(i=1;i<=5;i++) //循环读入5个数
	{ 
		scanf("%d",&t); //把每一个数读到变量t中
		a[t]++; //进行计数
	} 
	
	for(i=10;i>=0;i--) //依次判断a[0]~a[10] 
		for(j=1;j<=a[i];j++) //出现了几次就打印几次
			printf("%d ",i); 
			
	getchar();getchar(); 
	//这里的getchar();用来暂停程序,以便查看程序输出的内容
	//也可以用system("pause");等来代替
	return 0; 
} 

输入:

5 3 5 2 8 

输出:

8 5 5 3 2

1.3 注意:

1、时间复杂度: O(M+N)。
2、缺点:非常浪费空间

2. 冒泡排序

2.1 算法

冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。
例如:将12 35 99 18 76 从大到小的排序。

既然是从大到小排序,也就是说越小的越靠后
那我们每次都是比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的位置。一直比较下去直到最后两个数比较完毕后,最小的数就在最后一个了。就如同是一个气泡,一步一步往后“翻滚”,直到最后一位。这就将最小的一个数归位了,接下来按照相同方法进行比较剩下未归位的数。直到最后一个尚未归位的数。

2.2 代码

#include <stdio.h> 

int main() { 
	int a[100],i,j,t,n; 
	scanf("%d",&n); //输入一个数n,表示接下来有n个数
	
	for(i=1;i<=n;i++) //循环读入n个数到数组a中
		scanf("%d",&a[i]); 

	//冒泡排序的核心部分
	for(i=1;i<=n-1;i++) //n个数排序,只用进行n-1趟
	{ 
		for(j=1;j<=n-i;j++) //从第1位开始比较直到最后一个尚未归位的数,想一想为什么到n-i就可以了。
		{ 
			if(a[j]<a[j+1]) //比较大小并交换
			{ t=a[j]; a[j]=a[j+1]; a[j+1]=t; } 
		} 
	} 
	
	for(i=1;i<=n;i++) //输出结果
		printf("%d ",a[i]); 
	
	getchar();getchar(); 
	return 0; 
}

输入:

5	
12 35 99 18 76

输出:

99 76 35 18 12

2.3 注意

1、时间复杂度:O(NxN)。冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的时间复杂度是 O(NxN)。
2、缺点:时间复杂度太高,执行效率上却牺牲了很多。

3. 快速排序

3.1 算法

例如:将 6 1 2 7 9 3 4 5 10 8 排序。
首先找一个基准数,为了方便,就让第一个数 6 作基准数。接下来,将序列中所有比基准数大的数放在 6 的右边,比基准数小的数放在 6 的左边。
方法就是分别从初始序列两端开始“探测”。先从右往左找一个小于 6 的数,再从左往右找一个大于 6 的数,然后交换它们。这用两个 i (哨兵 i)和 j(哨兵 j)分别指向最左边和最右边。
刚开始哨兵 i 指向 6,哨兵 j 指向 8。因基准数在最左边,所以需让哨兵 j 先出动,这一点非常重要。哨兵 j 一步一步向左挪动(即 j–),直到找到
一个小于 6 的数停下来。接下来哨兵 i 再一步一步向右挪动(即 i++),直到找到一个大于 6的数停下来。即哨兵 j 停在 5 面前,哨兵 i 停在 7 面前。现在交换哨兵 i 和哨兵 j 所指向的元素的值。过程如下:

接下来哨兵 j 继续向左挪动(再次友情提醒,每次必须是哨兵j 先出发)。他发现了 4比基准数 6 要小之后停了下来。哨兵 i 也继续向右挪动,他发现了 9比基准数 6 要大之后停了下来。此时再次进行交换。

哨兵 j 继续向左挪动,他发现了 3比 6 要小之后又停下来。哨兵 i 继续向右移动,此时两哨兵相遇了。说明此时“探测”结束。我们将基准数 6 和 3 进行交换。

到此第一轮“探测”真正结束。此时以基准数 6 为分界点,6 左边的数都小于等于 6,6右边的数都大于等于 6。此时我们以 6 为分界点拆分成了两个序列,,接下来只要模拟刚才的方法分别处理 6 左边和右边的序列即可。
整个算法的处理过程:

3.2 代码

#include <stdio.h> 

int a[101],n;//定义全局变量,这两个变量需要在子函数中使用 

void quicksort(int left,int right) 
{ 
	int i,j,t,temp; 
	if(left>right) 
		return; 

	temp=a[left]; //temp中存的就是基准数 
	i=left; 
	j=right; 
	while(i!=j) 
	{ 
		//顺序很重要,要先从右往左找 
		while(a[j]>=temp && i<j) 
			j--; 
		//再从左往右找 
		while(a[i]<=temp && i<j) 
			i++;
			 
		//交换两个数在数组中的位置 
		if(i<j)//当哨兵i和哨兵j没有相遇时
		{ 
			t=a[i]; 
			a[i]=a[j]; 
			a[j]=t; 
		} 
	} 
	//最终将基准数归位
	a[left]=a[i]; 
	a[i]=temp; 

	quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程 
	quicksort(i+1,right);//继续处理右边的,这里是一个递归的过程 
}
 
int main() 
{ 
	int i,j,t; 
	//读入数据 
	scanf("%d",&n); 
	for(i=1;i<=n;i++) 
		scanf("%d",&a[i]); 
		
	quicksort(1,n); //快速排序调用 

	//输出排序后的结果 
	for(i=1;i<=n;i++) 
		printf("%d ",a[i]); 
		
	getchar();getchar(); 
	return 0; 
}

输入:

10 
6 1 2 7 9 3 4 5 10 8 

输出:

1 2 3 4 5 6 7 8 9 10 

3.3 注意

1、时间复杂度:O(NxN),平均时间复杂度为 O (NlogN)。
2、缺点:在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的,都是 O(NxN)。但是除此之外相比冒泡排序,每次交换是跳跃式的,速度自然就提高了。平均时间复杂度为 O (NlogN)。

参考:书籍《啊哈!算法》
(部分引用网络资源,侵权致歉,联系删除!)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值