《啊哈算法》——排序
今天开始了对《啊哈算法》这本书的学习。概括来讲,这本书算是算法界的小白书,语言很通俗,介绍的算法也比较简单,现在回来看简单的东西会不会显得浪费时间呢?不然,笔者最近感觉竞赛并不是终极之道,学习的方向也改为以书为对象(《具体数学》一栏的设立便可以看出),开始慢慢完善专业知识的体系,并慢慢做一些实用性的东西。
对于排序问题,是算法需要解决的基本问题,而排序也有很多种,其不同的方法也决定了其不同的效率,下面我们来看几种常见的排序方法。
桶排序:
给出一组样例:将5、8、3、1、7从小到大的排序。
桶排序基于一维数组,给出这样排序方法:初始化数组a[10] = 0,然后a[5]++、a[8]++、a[3]++、a[1]++、a[7]++。随后我们控制循环语句,访问a[i],并将i输出a[i]次,最终我们便会得到由小到大的排列。而这个过程,也像其名字所描述的,将数据i扔到了下标为i的数组元素a[i]这个桶里。
桶排序巧妙的把数i当做一维数组的下标,而用a[i]记录i出现的次数,利用下标自小到大的排列顺序完成了对数据的排列。其优点是简易明了,但是缺点也很明显,我们所开的数组长度是受内存空间限制的,因此随着数据的扩大,该排序便显得捉襟见肘了。
下面我们来简单的实现一下这个桶排序。
#include<cstdio> int main() { int a[11] , i , j , t; for(i = 0;i <= 10;i++) a[i] = 0; for(i = 1;i <= 5;i++) { scanf("%d",&t); a[t]++; } for(i = 0;i <= 10;i++) for(j = 1;j <= a[i];j++) printf("%d ",i); return 0; }
冒泡排序:
对于一个需要由小到大排列的序列a[],我们通过比较相邻的两位a[i]、a[i+1],如果a[i+1] < a[i],则将a[i]与a[i+1]交换位置,此时排在序列末尾中又较小的数字就像气泡一样一点点向上冒,但问题在于每次这种交换较小的数字只向上冒了一格,而如果我们遍历了区间上所有相邻的情况,则在这一次遍历中,这个最小的数字将会升到序列的最前面(或者最大的数字沉到序列的最后面,这取决于从序列的头部还是尾部开始)。那么很显然,如果进行n - 1次遍历,我们便可以完成对n个数字的排序。
由上面的算法分析我们不难看出,冒泡排序的时间复杂度是O(n^2),它虽然避免了桶排序浪费内存的弊端,但是其算法效率显得不是很客观。
简单的代码实现如下。
#include<cstdio> using namespace std; int main() { int a[100] , i , j , t , n; scanf("%d",&n); for(i = 1;i <= n;i++) scanf("%d",&a[i]); for(i = 1;i <= n-1;i++) for(j = 1; j <= n - 1;j++) { 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]); return 0; }
快速排序:
首先我们限定,排序方式是从小到大。
快速排序基于一个精妙的二分思想,使得其排序效率非常快。假设给定一个序列a[1]到a[n],我们从下标为[1,n]开始,以a[1]为一个参考量,分别从左边和右边扫下标为[1,n]这个区间段,我们设置i记录从左边开始扫的下标,j表示从右边开始扫的下标,一旦发现a[i] > a[1]并且a[j] < a[1],则交换a[i]和a[j],直到两边的指针汇集到了一处,即i = j , 此时将a[i]与a[1]交换,则此时的序列顺序满足第i个元素的左边都是小于a[i]的,第i个元素的右边都是大于a[j]的。但是a[i]左边区间和右边区间的顺序仍然不符合要求,因此我们利用相同的方法,对左区间和右区间进行排序。显然,这不断地将区间一分为二然后分别排序,这呈现出一种类似于dfs回溯的方式,能够遍历到整个大区间的小区间,即完成对所有小区间的排序,也就完成了对整个区间上数字的排序。
基于对快排的简单介绍,我们便可以进行编程实现了。
#include<cstdio> int a[101] , n; void quicksort(int left , int right) { int i , j , t , temp; if(left > right) return; temp = a[left]; i = left; j = right; while(i != j) { while(a[j] >= temp && i < j) j--; while(a[i] <= temp && i < j) i++; if(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]); return 0; }