例如:将5 3 5 2 8 从大到小排序。
这个算法就好比有 11 个桶,编号从 0~10。每出现一个数,就在对应编号的桶中放一个小旗子,最后只要数数每个桶中有几个小旗子就 OK 了。例如 2 号桶中有 1 个小旗子,表示2 出现了一次;3 号桶中有 1 个小旗子,表示 3 出现了一次;5 号桶中有 2 个小旗子,表示 5出现了两次;8 号桶中有 1 个小旗子,表示 8 出现了一次。
#include
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、时间复杂度: O(M+N)。
2、缺点:非常浪费空间
冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换过来。
例如:将12 35 99 18 76 从大到小的排序。
既然是从大到小排序,也就是说越小的越靠后。
那我们每次都是比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的位置。一直比较下去直到最后两个数比较完毕后,最小的数就在最后一个了。就如同是一个气泡,一步一步往后“翻滚”,直到最后一位。这就将最小的一个数归位了,接下来按照相同方法进行比较剩下未归位的数。直到最后一个尚未归位的数。
#include
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
1、时间复杂度:O(NxN)。冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的时间复杂度是 O(NxN)。
2、缺点:时间复杂度太高,执行效率上却牺牲了很多。
例如:将 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 左边和右边的序列即可。
整个算法的处理过程:
#include
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
1、时间复杂度:O(NxN),平均时间复杂度为 O (NlogN)。
2、缺点:在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的,都是 O(NxN)。但是除此之外相比冒泡排序,每次交换是跳跃式的,速度自然就提高了。平均时间复杂度为 O (NlogN)。
参考:书籍《啊哈!算法》
(部分引用网络资源,侵权致歉,联系删除!)