【深基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(mlogm)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(mlogm)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
是首选,因为其性能和稳定性很好,适合大多数情况。- 冒泡排序和选择排序适合理解排序原理,但性能较差,适合小规模数据。
- 快速排序效率高且广泛使用,适合大数据量,但实现稍复杂。
- 计数排序对于范围已知、数据量大的整数排序尤其高效。