• 各种排序算法概览


    快速排序(交换范式)

    // 一趟快排就是找到第一个元素的位置使得左边比它小右边比它大
    int _quicksort(int *a, int low, int high) {
        int mid_value = a[low];
        while(low < high) {
            while(low < high && a[high] >= mid_value) high--;
            a[low] = a[high];
            while(low < high && a[low] <= mid_value) low++;
            a[high] = a[low];
        }
        a[low] = mid_value;
        return low;
    }
    
    void quicksort(int *a, int low, int high) {
        if (low < high) {
            int mid = _quicksort(a, low, high);
            quicksort(a, low, mid-1);
            quicksort(a, mid+1, high);
        }
    }
    

    冒泡排序(交换范式)

    void bubblesort(int *a, int n) {
        for (int i = 0; i < n - 1; i++) {
              int flag = 0;
              for (int j = n - 1; j > i; j--) {
                    if (a[j - 1] > a[j]) {
                          swap(a[j], a[j - 1]);
                          flag = 1;
                    }
              }
              if (!flag) return;
        }
    }
    

    直接插入排序(插入范式)

    从数组a的第二个元素开始往后迭代,找到一个值后插入到前面使整个数组有序。

    void insertsort(int *a, int n) {
        for (int i = 1; i < n; i++) {
              int temp = a[i], j;
              for (j = i - 1; j >= 0; j--) {
                    if (temp < a[j]) a[j + 1] = a[j];          
                    else break;
              }
              a[j + 1] = temp;
        }
    }
    

    选择排序(选择范式)

    如果从小到大排,从索引0开始到结束,每一步选择后面最小的放在当前索引位置。

    void selectsort(int *a, int n) {
        for (int i = 0; i < n - 1; i++) {
              int min_idx = i;
              for (int j = i + 1; j < n; j++)
                    if (a[j] < a[min_idx]) min_idx = j;
              if (min_idx != i) swap(a[i], a[min_idx]);
        }
    }
    

    希尔排序(插入范式)

    举例一趟增量为3排序子过程:

    第0步 15 9 7 8 20 -1 4
    第1步 15     8       4  ->  4      8       5
    第2步    9     20       ->    9      20   
    第3步      7      -1    ->      -1       7
    (每一步都用直接插入排序获取)
    第一趟得到: 4 9 -1 8 20 7 5
    

    在此基础上,希尔排序遵循如下过程:
    从增量为n/2开始,逐趟增量减少一半进行上述操作,直到增量最终为1那一趟结束。

    void shellsort(int *a, int n) {
        const int reduce_factor = 2;
        int gap = n / reduce_factor;
        while (gap) {
              for (int i = gap; i < n; i++) {
                    int temp = a[i], j;
                    for (j = i - gap; j >= 0; j -= gap) {
                          if (temp < a[j]) a[j + gap] = a[j];
                          else break;
                    }
                    a[j + gap] = temp;
              }
              gap /= reduce_factor;
        }
    }
    

    堆排序(选择范式)

    推排序把数组看作一颗完全二叉树,大根堆意思是这颗二叉树的任何父节点永远大于子节点。
    完全二叉树就是数组的每个元素依次填入一个完整的二叉树。
    建大根堆过程:

    1. 从最后一个非叶结点开始,自底向上调整每一个非叶节点(从右向左从下到上)
    2. 调整操作:如果当前节点比两个儿子都大无需调整,否则找到最大的那个儿子把值与自己交换,再调整那个儿子直到叶结点。
      堆排序代码:
    void heap_adjust(int *a, int i, int size) {
        int lchild = 2 * i, rchild = 2 * i + 1;
        if (i > size / 2) return; // leaf
        int max_idx = i;
        if (lchild <= size && a[lchild - 1] > a[max_idx - 1]) max_idx = lchild;
        if (rchild <= size && a[rchild - 1] > a[max_idx - 1]) max_idx = rchild;
        if (max_idx != i) {
              swap(a[i - 1], a[max_idx - 1]);
              heap_adjust(a, max_idx, size);
        }
    }
    
    void heap_buildmax(int *a, int size) {
        for (int i = size / 2; i >= 1; i--) heap_adjust(a, i, size);
    }
    
    void heap_sort(int *a, int n) {
        heap_buildmax(a, n);
        for (int i = n; i >= 2; i--) {
              // 从后往前迭代,每次将堆顶输出到当前位置, 排序后为从小到大
              swap(a[1 - 1], a[i - 1]);
              heap_adjust(a, 1, i - 1);
        }
    }
    

    归并排序

    归并算法还是比较重要的,建议背一下,其功能是归并两个本来就有序的数组,使输出有序。

    void merge(int *a, int low, int mid, int high, int *buffer) {
        // a[low:mid] 与 a[mid+1:high] 各自有序, 现在要输出一个有序的a[low:high]
        int i, j, k;
        copy(a + low, a + high + 1, buffer + low);
        for (i=low, j=mid+1, k=low; i<=mid && j<=high; k++) {
            // i,j为两个有序数组的扫描指针, k为当前输出指针
            if (buffer[i] <= buffer[j]) a[k] = buffer[i++];
            else a[k] = buffer[j++];
        }
        while(i<=mid)  a[k++] = buffer[i++];
        while(j<=high) a[k++] = buffer[j++];
    }
    
    int *buffer = new int[n] // 需要开一个和输入一样大的数组
    void mergesort(int *a, int low, int high) {
        if (low<high) {
            int mid = (low+high)/2;
            mergesort(a, low, mid);
            mergesort(a, mid+1, high);
            merge(a, low, mid, high, buffer); //归并放最后,这个算法是自底向上的
        }
    }
    

    基数排序

    十进制基,优先低位基数排序过程:

    1. 建立10个桶
    2. 按照输入所有数最后一位(%10),放入这些桶(比如123就放入3号队列队尾)
    3. 从0~10将各个桶里的值输出
    4. 再按照倒数第二位放入桶(与2操作相似)
    5. 从0~10将各个桶里的值输出
    6. 一直继续直到最高位操作结束
    int maxbit(int *a, int n, int radix) {
        int res = -1;
        for (int i = 0; i < n; i++) {
              int p = a[i], c = 0;
              while (p) {
                    c++;
                    p /= radix;
              }
              res = max(res, c);
        }
        return res;
    }
    
    const int radix = 10;
    int radix_count[radix];
    int radix_buffer[radix];
    
    void radixsort(int *a, int n) {
        int max_bit = maxbit(a, n, radix);
        int r = 1;
        for (int i = 0; i < max_bit; i++) {
              memset(radix_count, 0, radix * sizeof(int));
              for (int j = 0; j < n; j++) 
                    radix_count[a[j] / r % radix]++; // 每个桶容纳数目
              for (int j = 1; j < radix; j++)  // 每个桶输出位置
                    radix_count[j] += radix_count[j - 1];
              for (int j = n - 1; j >= 0; j--) {
                    int v = a[j] / r % radix;
                    radix_buffer[radix_count[v] - 1] = a[j];
                    radix_count[v]--;
              }
              memcpy(a, radix_buffer, radix * sizeof(int));
              r *= radix;
        }
    }
    

    排序算法特性表

    算法 最好复杂度 平均复杂度 最坏复杂度 空间复杂度 稳定性
    直接插入排序 O(n) O(n^2) O(n^2) O(1) Y
    冒泡排序 O(n) O(n^2) O(n^2) O(1) Y
    选择排序 O(n^2) O(n^2) O(n^2) O(1)
    希尔排序 O(1)
    快速排序 O(nlogn) O(nlogn) O(n^2) O(logn)
    堆排序 O(nlogn) O(nlogn) O(nlogn) O(1)
    2路归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) Y
    基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r)) O(r) Y
  • 相关阅读:
    bzoj 2527: [Poi2011]Meteors 整体二分
    bzoj 2738 矩阵乘法
    bzoj 3110 K大数查询
    bzoj 3262 陌上花开
    cogs 577 蝗灾 CDQ分治
    bzoj 1101 zap
    bzoj 2005
    bzoj 3518 Dirichlet卷积
    bzoj 1257
    最优贸易 [NOIP 2009]
  • 原文地址:https://www.cnblogs.com/xytpai/p/12609662.html
Copyright © 2020-2023  润新知