# 计数排序

计数排序是一种线性时间复杂度的排序算法,适用于一定范围内的整数排序。它的基本思想是通过统计每个元素出现的次数,然后根据这些统计信息将元素排列起来。

# 题目:

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

# 分析

将选手的选票数保存在以选手号为下标的数组里,对应的值就是选票

注意:题目是按照票数字数由小到大排序

# 代码

// 计数排序
#include <iostream>
using namespace std;
int a[1000] = {0}; // 声明一个长度为 n 的数组 a,用于记录每个候选人获得的选票数量
int main()
{
    int n, m; // 候选人数量和选票数量
    cin >> n >> m;
    int candidate; // 声明一个变量用于记录每张选票上的候选人编号
    for (int i = 0; i < m; i++)
    {
        cin >> candidate;
        a[candidate]++; // 将对应位置的数组 a 的值加 1
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < a[i]; j++)
        {
            cout << i << " "; // 输出相应次数的候选人编号
        }
    }
    return 0;
}

# 选择、冒泡、插入排序

这三种排序时间复杂度并不佳,数据规模较大时可能耗时久,比赛一般不用。

用扑克牌举例

# 选择排序(Selection Sort):

  • 选择排序过程是这样的:每次从未排序的牌堆中选择最小的牌,然后放到已排序牌的末尾。
  • 重复这个过程,直到所有牌都被放置在正确的位置上。
  • 选择排序是不稳定的排序算法,其时间复杂度为 O (n^2)。

选择排序的代码示例(C++):

void selectionSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        int minIndex = i;
        for (int j = i+1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        swap(arr[i], arr[minIndex]);
    }
}

# 冒泡排序(Bubble Sort):

  • 冒泡排序过程是这样的:相邻的两张牌进行比较,如果它们的顺序错误,则交换它们的位置。最大 / 小的牌到最后面了。
  • 重复这个过程,直到所有牌都按照正确的顺序排列。
  • 冒泡排序是稳定的排序算法,其时间复杂度为 O (n^2)。

冒泡排序的代码示例(C++):

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

# 插入排序(Insertion Sort):

  • 插入排序过程是这样的:将一张新牌插入到已排序的牌堆中的正确位置。
  • 重复这个过程,直到所有牌都按照正确的顺序排列。
  • 插入排序是稳定的排序算法,其时间复杂度为 O (n^2)。

插入排序的代码示例(C++):

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1] = key;
    }
}

# 快速排序

比赛常用排序

快速排序(Quick Sort)是一种常用且高效的排序算法,其基本思想是通过分治的策略将一个大问题拆分为小问题进行解决。

具体步骤如下:

  1. 选择一个基准元素。通常情况下,可以选择数组中的第一个元素作为基准。
  2. 将数组划分为两个子数组,使得左边的子数组中的元素都小于等于基准元素,右边的子数组中的元素都大于基准元素。这个过程称为分区(Partition)。可以使用双指针法或者挖坑法来实现分区。
  3. 对左右子数组分别进行递归调用快速排序。即将左子数组和右子数组作为新的数组执行步骤 1 和步骤 2,直到子数组的长度为 0 或 1,此时说明子数组已经有序。
  4. 合并结果。将左子数组、基准元素和右子数组按顺序合并起来。

快速排序的时间复杂度为平均情况下的 O (nlogn),最坏情况下的时间复杂度为 O (n^2)(当选取的基准元素恰好为最大或最小值时),但是最坏情况出现的概率较低。快速排序是一种原地排序算法,不需要额外的空间开销。

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pivotIndex = partition(arr, low, high); // 将数组进行分区,并获取基准元素的索引
        quickSort(arr, low, pivotIndex - 1); // 对左侧子数组进行快速排序
        quickSort(arr, pivotIndex + 1, high); // 对右侧子数组进行快速排序
    }
}
int partition(int arr[], int low, int high) {
    int pivot = arr[low]; // 选择第一个元素作为基准
    int i = low, j = high;
    
    while (i < j) {
        while (i < j && arr[j] >= pivot) { // 在右侧找到小于基准的元素的索引
            j--;
        }
        arr[i] = arr[j];
        
        while (i < j && arr[i] <= pivot) { // 在左侧找到大于基准的元素的索引
            i++;
        }
        arr[j] = arr[i];
    }
    
    arr[i] = pivot; // 将基准元素放置在正确的位置
    return i; // 返回分区后基准元素的索引
}

# sort 排序

,STL(标准模板库)中提供了 sortunique 算法来进行排序和去重操作。

  1. sort 算法用于对指定范围内的元素进行排序,默认按照升序进行排序。它的函数原型如下:
template <class RandomAccessIterator>
void sort(RandomAccessIterator first, RandomAccessIterator last);

其中, firstlast 表示要排序的元素范围,通常是使用迭代器指定的数组或容器的起始和结束位置。

例如,对一个整数数组进行排序可以这样使用 sort 算法:

int arr[] = {5, 2, 8, 3, 1};
int n = sizeof(arr) / sizeof(arr[0]);
sort(arr, arr + n);
  1. unique 算法用于移除指定范围内的连续重复元素,使得每个元素只出现一次,并返回新的范围的尾后迭代器。它的函数原型如下:
template <class ForwardIterator>
ForwardIterator unique(ForwardIterator first, ForwardIterator last);

其中, firstlast 表示要去重的元素范围,通常是使用迭代器指定的数组或容器的起始和结束位置。

例如,对一个整数数组进行去重可以这样使用 unique 算法:

int arr[] = {1, 2, 2, 3, 3, 4, 5, 5};
int n = sizeof(arr) / sizeof(arr[0]);
sort(arr, arr + n); // 先排序,再去重
auto last = unique(arr, arr + n);

在上面的例子中, unique 函数将数组中的连续重复元素移除,并返回新的范围的尾后迭代器 last

需要注意的是,使用 unique 算法之前通常需要先对元素进行排序,以确保重复的元素相邻。因此,在调用 unique 之前,往往会配合使用 sort 算法来先对元素进行排序。

更新于