排序算法(快排、归并、计数、基数排序)

排序

排序概览

排序方法 时间复杂度(平均) 时间复杂度(最坏) 稳定性
快速排序 n log n n方 不稳定
归并排序 n log n n log n 稳定
计数排序 n+k n+k 稳定
基数排序 n k n k 稳定
堆排序 n log n n log n 不稳定
选择排序 n方 n方 不稳定
冒泡排序 n方 n方 稳定
插入排序 n方 n方 稳定

一.快速排序

  1. 排序思想

    • 排序区间为 l, r
      • 如果区间长度小于等于 1 则直接退出, 否则选一个区间中随机的数字 xl 位元素交换作为比较元素
      • 将大于 x 的数字放在左边, 小于的放在右边, 等于的也要换边!!
      • 此时 x 的位置已经固定, 对两边区域的分别递归
    • 一开始的区间为 1, n
    • 两个指针分别从 lr 开始向中间扫描, 直到相遇结束一次扫描
  2. 代码实现

    复制代码
    void quicksort(int l,int r){
     if(l >= r)
         return;
     swap(a[l], a[l + rand() % (r - l + 1)]);
     int x = a[l];
     int i = l, j = r;
     while(i < j){
         while(i < j && a[j] > x)
             j--;
         if(i < j)
             a[i++] = a[j];
         while(i < j && a[i] < x)
             i++;
         if(i < j)
             a[j--] = a[i];
     }
     a[i] = x;
     quicksort(l, i - 1);
     quicksort(i + 1, r);
    }
  3. 补充

    • 实际打比赛可用 sort() 函数, 可以直接快排

    • 对于多关键字排序可以重构比较符号

      struct Node{
      int x, y;
      bool operator < (const Node &A) const{
      if(x != A.x)
      return x < A.x;
      return y < A.y;
      }
      } a[N + 1];

    • 找第 k 小的数用快排, 每一轮只要比较 ik, 然后排一半即可


二.归并排序

  1. 排序思想

    • 排序区间为l, r
      • 如果区间长度为 1 则直接退出, 否则将区间分为 l, mm+1, r 俩部分, 其中 m = ( l + r ) / 2
      • 递归两个子区间进行排序
      • 将两个已经排好的子区间合并
    • 一开始只要对区间 1, n 排序即可
  2. 代码实现

    复制代码
    void mergesort(int l,int r){
     if(l == r) return;
     int m = (l + r) / 2;
     mergesort(l, m);
     mergesott(m + 1, r);
     int p1 = l, p2 = m + 1, tot = 0;
     while(p1 <= m && p2 <= r){
         if(a[p1] <= a[p2])
             c[++tot] = a[p1++];
         else
             c[++tot] = a[p2++];
     }
     while(p1 <= m)
         c[++tot] = a[p1++];
     while(p2 <= r)
         c[++tot] = a[p2++];
     for(int i = 1; i<= tot; i++)
         a[i + l - 1] = c[i];
    }

三.计数排序

  1. 排序思想

    • 统计每个数据出现了几次
    • 统计完每个元素后, 求一遍前缀和, 就知道每个数字在排序完后的序列中出现的位置
    • 把数字填入对应的位置即可
  2. 代码实现

    复制代码
    int n, m, a[N + 1], c[M + 1], r[N + 1];
    inline void countingsort(){
         memset(c, 0, sizeof(c));
         for(int i = 1; i <= n; i++)
             ++c[a[i]];
    
         for(int i = 1; i <= m; i++){
             for(int j = 1; j <= c[i]; j++)
                 printf("%d", r[i]);
         }
         printf("\n");
    
         for(int i = 2; i <= m; i++)
             c[i] += c[i-1];
         for(int i = n; i; --i)
             r[i] = c[a[i]]--;
         for(int i = 1; i<= n; i++)
             printf("%d", r[i]);
         printf("\n");
     }
  3. 补充

    • 适用于值域范围较小的数字排列

四.基数排序

  1. 排序思想

    • 拆分成 m 个关键字, 从后往前对这些关键字排序, 每次排序会使用上一次的排序结果
    • 每一次是用计数排序来实现
    • 假设已经排完了第 i 个及以后的关键字, 现在要排第 i - 1 个关键字,这里是一个双关键字排序, 第一关键字是第 i - 1 个关键字, 第二关键字是第 i 个及以后的关键字的 rank
    • 我们只需要把数字按照第 i 个及以后的关键字从小到大排序放在数组里, 再进行一次计数排序即可 ( 因为计数排序是稳定的 )
  2. 代码实现

    复制代码
    int n, m, a[N + 1], sa[N + 1], v[N + 1], r[N + 1], c[M + 1];
    
    inline void countingsort(){
    memset(c, 0, sizeof(c));
    for(int i = 1; i <= n; i++)
        ++c[a[i]];
    for(int i = 2; i <= m; i++)
        c[i] += c[i-1];
    for(int i = n; i; --i)
        r[sa[i]] = c[v[sa[i]]]--;
    for(int i = 1; i<= n; i++)
        sa[r[i]] = i;
    }
    
    inline void radisort(){
    for(int i = 1; i <= n; i++)
        sa[i] = i;
    int x = 1;
    for(int i = 1; i <= m; i++, x*=10){
        for(int j = 1; j <=n; j++)
            v[j] = a[j] / x % 10;
        countingsort();
        }
    }
  3. 补充

    • 基数排序经常被用于字符串的排序, 比如说后缀数组的核心就是基数排序
相关推荐
倒头就睡的小比特1 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼1 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
m0_547486661 天前
《数据结构教程》全套 PPT课件2026
数据结构
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark1 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
码流子1 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven1 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark1 天前
从算法设计模式看编程思维的抽象能力4
算法