各种排序算法概览

快速排序(交换范式)

// 一趟快排就是找到第一个元素的位置使得左边比它小右边比它大
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
原文地址:https://www.cnblogs.com/xytpai/p/12609662.html