【深基9.例1】选举学生会——常见的五种排序方法

题目描述

学校正在选举学生会成员,有 \(n\)\(n\le 999\))名候选人,每名候选人编号分别从 \(1\)\(n\),现在收集到了 \(m\)\(m \le 2000000\))张选票,每张选票都写了一个候选人编号。现在想把这些堆积如山的选票按照投票数字从小到大排序。

输入格式

输入 \(n\)\(m\) 以及 \(m\) 个选票上的数字。

输出格式

求出排序后的选票编号。

样例 #1

样例输入 #1

5 10
2 5 2 2 5 2 2 2 1 2

样例输出 #1

1 2 2 2 2 2 2 2 5 5

一题多解

1. 使用 std::sort (内置排序算法)

std::sort 是 C++ 标准库提供的高效排序算法,基于快排和插入排序的混合算法(通常为 IntroSort),时间复杂度为 O(mlog⁡m)O(m \log m)O(mlogm)。

cpp复制代码#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    vector<int> a(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i];
    }
    
    sort(a.begin(), a.end());
    
    for (int i = 0; i < m; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
    
    return 0;
}

2. 冒泡排序(Bubble Sort)

冒泡排序是一种简单的排序算法,通过反复交换相邻元素的位置,使得较大的元素逐渐“冒泡”到列表的末端。它的时间复杂度为 O(m2)O(m^2)O(m2),适合数据量较小的情况。

cpp复制代码#include <bits/stdc++.h>
using namespace std;

void bubbleSort(vector<int>& a) {
    int n = a.size();
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (a[j] > a[j + 1]) {
                swap(a[j], a[j + 1]);
            }
        }
    }
}

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

3. 选择排序(Selection Sort)

选择排序每次找到剩余元素中最小的元素,将其放到已排序部分的末尾。它的时间复杂度为 O(m2)O(m^2)O(m2)。

cpp复制代码#include <bits/stdc++.h>
using namespace std;

void selectionSort(vector<int>& a) {
    int n = a.size();
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (a[j] < a[minIndex]) {
                minIndex = j;
            }
        }
        swap(a[i], a[minIndex]);
    }
}

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

4. 快速排序(Quick Sort)

快速排序是一种分治法的排序算法,平均时间复杂度为 O(mlog⁡m)O(m \log m)O(mlogm),但最坏情况为 O(m2)O(m^2)O(m2)(当数据基本有序时)。实现较为复杂,但在大数据量下性能很好。

cpp复制代码#include <bits/stdc++.h>
using namespace std;

void quickSort(vector<int>& a, int left, int right) {
    if (left >= right) return;
    int pivot = a[left], i = left, j = right;
    while (i < j) {
        while (i < j && a[j] >= pivot) j--;
        if (i < j) a[i++] = a[j];
        while (i < j && a[i] <= pivot) i++;
        if (i < j) a[j--] = a[i];
    }
    a[i] = pivot;
    quickSort(a, left, i - 1);
    quickSort(a, i + 1, right);
}

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

5. 计数排序(Counting Sort)

计数排序适合处理范围有限的整数数据。在本题中,候选人编号的范围是 111 到 nnn,因此计数排序是一个合适的选择。计数排序的时间复杂度是 O(m+n)O(m + n)O(m+n),但它需要额外的空间来记录计数。

cpp复制代码#include <bits/stdc++.h>
using namespace std;

void countingSort(vector<int>& a, int maxValue) {
    vector<int> count(maxValue + 1, 0);
    for (int num : a) {
        count[num]++;
    }
    int index = 0;
    for (int i = 1; i <= maxValue; i++) {
        while (count[i]-- > 0) {
            a[index++] = i;
        }
    }
}

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

总结

  • std::sort 是首选,因为其性能和稳定性很好,适合大多数情况。
  • 冒泡排序和选择排序适合理解排序原理,但性能较差,适合小规模数据。
  • 快速排序效率高且广泛使用,适合大数据量,但实现稍复杂。
  • 计数排序对于范围已知、数据量大的整数排序尤其高效。
posted @ 2024-10-29 17:35  xiins  阅读(24)  评论(0编辑  收藏  举报