简易“桶排序”
- “简易桶排序”并不是真正的桶排序,真正的桶排序复杂得多。
- 就像5个人得到五个分数,分数范围0-10,那么我们可以设置从0分到10分一共10个桶,都初始化为0,用户输入分数,对应一个分数桶里的数+1,最后再对11个桶进行数值输出,为0的不输出,数值为1就输出1次,为2 就输出2次,便得到排序后的数据啦。
- 这种排序理解起来很简单,但是非常占空间,取值范围内的每一个数都需要初始化数组,并且这种简易的算法对于float小数、复杂数据(比如带有附加信息的)无能为力。
- 简易“桶排序”时间复杂度为O(M+N)。
【例】对五个人的分数进行升序排列,满分10分
int main() //升序排列
{
int a[11], i, j, t;
for (i = 0; i <= 10; i++)//初始化10个桶
{
a[i] = 0;
}
for (i = 1; i <= 5; i++) // 5为排序个数,也可以用户输入
{
cin >> t;//对每个桶计数
a[t]++;
}
for (i = 0; i <= 10; i++)
{
for (j = 1; j <= a[i]; j++)//这个循环输出次数代表桶里的个数
{
cout << i << "\t";
}
}
return 0;
}
冒泡排序
冒泡排序可以说是每本书都必讲的,基本思想:比较相邻的两个数据,如果他们的排列不符合期望便把他们交换过来,冒泡排序的算法核心是双重嵌套循环部分。时间复杂度位O(N2),和上面的简易桶排序对比,虽然不占空间,但会浪费很多时间。
第一趟,从第一位开始和第二位进行对比,(若不符合排序规则便交换),依次往后,第二位和第三位对比,第三位和第四位对比,第四位和第五位对比。做完第一趟我们发现最小的那个数已经到达了他应该所处的位置。
第二趟,从第一位开始和第二位进行对比,依次往后,注意这里第四和第五位不用再对比。做完第二趟我们发现第二小的那个数也已经到达了他应该所处的位置。
第三趟,从第一位开始和第二位进行对比,依次往后,注意这里第三和第四位不用再对比。做完第三趟我们发现第三小的那个数也已经到达了他应该所处的位置。
第四趟…做完第四趟我们发现第四小的那个数也已经到达了他应该所处的位置。
第五趟…做完第二趟我们发现最大的那个数也已经到达了他应该所处的位置。
int main()
{
int a[6]={3,6,76,4,3,8};
int i, j, k, l;
for (l = 0; l < 5; l++)//循环5趟
{
for (i = 0; i < 5; i++)
{
j = i + 1; //比较相邻位,变换位置
if (a[i] < a[j])
{
int temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
for (k = 0; k <= 5; k++)//做完每一趟输出
{
cout << a[k] << " ";
}
cout << endl;
}
return 0;
}
输出如下:
当然可以把数组变成结构体便可输出更多信息,比如姓名…
快速排序
据说这是最常用的排序算法,和上面的两种算法相比,它既节约空间又节约时间。
快速排序算法首先需要有一个基准数,把小于基准数的放到基准数的左边,大于基准数的放到右边,再对当前基准数的左右两边的数据在进行同样的操作,直到所有的基准数归位。
【例】对数组a的数据3,1,9,7,2,5,4,6,8,10
进行排序
首先设置基准数为最左边的数3,设置两个哨兵 i 和 j 分别指向 a[0] 和 a[9] 。
- 哨兵j先开始向左寻找一个比基准数3要小的数,接下来哨兵i开始向右寻找大于基准数3 的数停下来。这时j指向了2,i指向了9,交换i和j指向的数的位置,数组变成
3,1,2,7,9,5,4,6,8,10
- 再一次j先开始继续向左寻找小于3的数,当j指向2的时候,我们理解为i和j相遇了并且都指向2,此时需要交换基准数3和两个哨兵共同指向的数据2,那么至此小于基准数的值都到了左边,大于基准数的值都到了右边,数组变成
2,1,3,7,9,5,4,6,8,10
- 接下来在左右两边分别取基准数再做这样一次操作,继续做…直到不可拆分出新的子序列为止,至此排序完成。
注意注意思考这里每次都是哨兵j先开始向左,为什么?假设从i先开始出动我们发现当两个哨兵相遇时,按理应该交换基准数和哨兵所指的值的位置,但是交换之后有可能基准数左边的数不一定小于基准数 ,这不符合我们对快速排序的要求,提供一组简单数据4,3,9,8,2
可以尝试先从i开始向右试试你就会发现。
#include <iostream>
#include <stdio.h>
using namespace std;
int a[10] = { 8,43,6,3,10,15,92,7,25,5 }; //全局变量数组
void quicksort(int left, int right) //传入两个哨兵
{
int sign,i,j,t; //定义基准数sign
if (left > right) //当传入哨兵错误会结束当前函数
return;
sign = a[left];
i = left;
j = right;
while (i != j) //哨兵未相遇才会执行循环
{
while (a[j] >= sign && i < j)
j--; //条件成立代表哨兵j还未找到小于基准数的值,故j--
while (a[i] <= sign && i < j)
i++; //条件成立代表哨兵i还未找到大于基准数的值,故i++
if (i < j) //哨兵定位后交换数值
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
a[left] = a[i]; //当哨兵相遇时,将基准数和哨兵的值交换
a[i] = sign;
quicksort(left, i - 1);
quicksort(i + 1, right);
return;
}
int main()
{
cout << "原数据" << endl;
for (int i = 0; i <= 9; i++)
cout << a[i] << " ";
cout << endl;
quicksort(0,9);
cout <<endl<< "排序后" << endl;
for (int i = 0; i <= 9; i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}
下面三张图,第一张图时 i = j = 4,此时两个哨兵相遇了,应该交换基准值和当前哨兵指向的值