基础排序算法

经典算法之基础排序算法


计数排序

计数排序算法是一种基于分类而不是比较的排序,不依赖排序对象之间的直接大小比较。

比如:我们在投票的时候,有 n (n <= 1000) 名候选人,m (m <= 200000) 张选票,现在我们需要统计每名候选人的票数。

使用计数排序的思路:只需要开一个大小不小于 n 的数组作为票箱,依次读入选票,然后将选票加到对应的票箱中,每次都将票数 +1,最后输出每个票箱的票数即可。在这个过程中,甚至不需要存储下每一张选票。

代码如下:

#include <iostream>
using namespace std;
const int N = 1010;

int n, m;
int a[N];

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++){
        int x;
        cin >> x;
        a[x]++;
    }
    
    for (int i = 1; i <= n; i++){
        cout << a[i] << " ";
    }
    cout << endl;
    
    return 0;
}

这种排序算法读入选票并统计的时间复杂度为 O(m),输出选票的时间复杂度为 O(n),空间复杂度为 O(n)。效率较低,所以计数排序只能用于排序编号 (值域) 范围不是很大的数字,此外,如果想要对浮点数或字符串进行排序,这种算法也实现不了。


下面三种就是针对元素大小进行排序的算法。

以数列排序为例:

输入 n (n < 1000) 个数字 ai (ai < 109) ,将其从小到大排序后输出。

1.选择排序

选择排序类似于摸牌时将纸牌排序。

思路:从第一张牌到最后一张牌中找到最小的一张,放在最前面的位置;然后从第二张牌到最后一张牌中继续找到最小的一张,放到第二位 … 如此反复,就可以得到从小到大的序列。如果遇到两个相等的数的时候,一般是不会去调换顺序的。

代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;

int a[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    //选择排序:多次遍历,每次找到最小的放在最前面
    for (int i = 1; i <= n - 1; i++)
        for (int j = i + 1; j <= n; j++)
            if (a[j] < a[i]){
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }

    for (int i = 1; i <= n; i++)
        cout << a[i] << " ";
    cout << endl;

    return 0;
}

时间复杂度为 O(n2),空间复杂度为 O(n).


2.冒泡排序

冒泡排序的核心就是:依次遍历将最大的数放在最后面。在每一轮比较中,最大的元素都会 “咕噜咕噜” 一样从左边移动到右边,像冒泡泡一样,所以得名冒泡排序。

思路:依次比较相邻的两个元素,如果前一项大于后一项就交换位置,再往后移动一位,依次两两比较。

比如,首先比较第1张牌和第2张牌,如果后面的牌比前面的牌小,那么就交换位置;然后比较第2张牌和第3张牌,同样保证第3张牌要大于第2张牌 … 依次类推,直到比较最后一张牌和倒数第二张牌,保证最后面一张牌是最大的牌。

继续和上面一样进行新的一轮比较再交换,需要注意的是,由于最后一张牌已经是最大的了,所以只需要比较到倒数第二张牌就可以了。经过 n - 1 轮排序后,就可以得到有序的序列了。

代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;

int a[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    //冒泡排序:依次比较相邻的两个元素,把最大的放在最后面
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n - i; j++)
            if (a[j] > a[j + 1]){
                //依次比较相邻两个元素
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }

    for (int i = 1; i <= n; i++)
        cout << a[i] << " ";
    cout << endl;

    return 0;
}

时间复杂度为 O(n2),空间复杂度为 O(n).


3.插入排序

边输入边排序:每次读取一个新的元素时,都将其插入到已排好序的序列当中,当读取完输入数据时,序列也就排好序了。

思路:把序列分为有序和无序的两部分。最开始有序的就只有一个元素,也就是第一个元素。要把接下来的无序区中一个元素插入到有序区中,就是从有序区的末尾开始往前比较,如果待插元素比正在比较的元素小,那么就把有序区的正在比较的元素往后放一格,然后继续往前面进行比较,直到待插元素遇到不大于自己的元素或者成为第一个为止。这时,待插元素就可以填入留出来的缺口中。接下来重复将无序区中的待插元素插入到有序区中去,直到所有的元素都在有序区中。

代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;

int a[N];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    //插入排序:分成有序和无序两部分,每次都将无序的一个元素插入到有序区中去
    for (int i = 1, j; i <= n; i++){
        int temp = a[i]; //记录一下待插元素,等下要插入到有序区中
        for (j = i - 1; j >= 0; j--){
            if (a[j] > temp)
                a[j + 1] = a[j];
            else break;
        }
        a[j + 1] = temp;
    }

    for (int i = 1; i <= n; i++)
        cout << a[i] << " ";
    cout << endl;

    return 0;
}

最坏情况下的时间复杂度为 O(n2),空间复杂度为 O(n),如果能保证序列基本有序,那么插入排序的效率就会高一些。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BraumAce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值