算法

排序算法

有童yt_hy · 6月20日 · 2025年 · · · · · · · · 本文共2258个字 · 预计阅读8分钟 1056次已读

冒泡排序 (Bubble Sort)

从头到尾依次比较相邻元素,若顺序错误就交换位置,将较大(或较小)元素“冒泡”到序列末端

最佳时间复杂度:O(n)每一个数一对比就对了

平均时间复杂度:O(n^2)每一个数都要和其余数对比

最差时间复杂度:O(n^2)

空间复杂度:O(1),原地排序

void bubble_sort(int arr[], int n) {
  /* 外层循环控制趟数 (共 n-1 趟) */
  for (int i = 0; i < n - 1; ++i) {
      int swapped = 0;           /* 标记本趟是否发生交换,用于提前结束 */
      /* 内层循环:将本趟最大的元素“冒泡”到末尾 */
      for (int j = 0; j < n - 1 - i; ++j) {
          if (arr[j] > arr[j + 1]) {
              /* 交换相邻元素 */
              int tmp;
              tmp = arr[j];
              arr[j] = arr[j + 1];
              arr[j + 1] = tmp;
              swapped = 1;
          }
      }
      /* 若本趟没有发生交换,说明数组已有序,可提前结束 */
      if (!swapped) break;
  }
}

插入排序 (Insertion Sort)

先把序列的第一个元素视为已排序区,其余元素视为未排序区.从未排序部分取出一个数,放到已排序部分的正确位置

最佳时间复杂度:O(n)每一个数一对比就对了

平均时间复杂度:O(n^2)每一个数都要和其余数对比

最差时间复杂度:O(n^2)

void insertion_sort(int arr[], int n) {
  for (int i = 1; i < n; ++i) {     // 从第二个元素开始逐个插入
      int key = arr[i];             // 待插入的元素
      int j   = i - 1;

      /* 将比 key 大的元素向后移动,为 key 腾出位置 */
      while (j >= 0 && arr[j] > key) {
          arr[j + 1] = arr[j];
          --j;
      }
      arr[j + 1] = key;             // 把 key 插入到正确的位置
  }
}

选择排序 (Selection Sort)

找到最小或最大的数,放到开头或结尾

最佳时间复杂度:O(n^2)每一个数都要和其余数对比

平均时间复杂度:O(n^2)

最差时间复杂度:O(n^2)

void selection_sort(int a[], int n)
{
  for (int i = 0; i < n - 1; ++i)
  {
      /* 假设 a[i] 就是当前最小值的位置 */
      int min_idx = i;

      /* 在 [i+1, n-1] 区间中找真正的最小值索引 */
      for (int j = i + 1; j < n; ++j)
          if (a[j] < a[min_idx])
              min_idx = j;

      /* 若找到更小值,则把它换到“已排序区”末尾 */
      if (min_idx != i)
      {
          int tmp   = a[i];
          a[i]     = a[min_idx];
          a[min_idx] = tmp;
      }
  }
}

快速排序 (Quick Sort)

随便找一个数,将比它小的挪到左边,大的挪到右边,然后对两边递归使用快速排序直到没有可以排的为止.

最佳时间复杂度:O(n logn)每一个数调用递归且规模显著减小

平均时间复杂度:O(n logn)

最差时间复杂度:O(n^2)

void quicksort(int a[], int left, int right)
{
  int i = left, j = right;
  int pivot = a[(left + right) / 2];   /* 选取中间元素作为基准值,当然这里可以随便选 */

  /* 一趟划分:把 <=pivot 的元素放左边,>=pivot 的元素放右边 */
  while (i <= j)
  {
      while (a[i] < pivot) i++;       /* 找到左侧第一个 ≥ pivot 的位置 */
      while (a[j] > pivot) j--;       /* 找到右侧第一个 ≤ pivot 的位置 */

      if (i <= j)                     /* 若左右哨兵未交叉,则交换 */
      {
          int tmp = a[i];
          a[i] = a[j];
          a[j] = tmp;
          i++;                         /* 继续向内收缩 */
          j--;
      }
  }

  /* 递归处理左右子区间 */
  if (left < j) quicksort(a, left, j);
  if (i < right) quicksort(a, i, right);
}

或者直接使用qsort()

void qsort(void *base, size_t nmemb,
          size_t size,
          int (*compar)(const void *, const void *));

归并排序 (Merge Sort)

分解:将现有元素递归一分为二直到为单个元素(默认有序)

合并:递归合并所有元素

时间复杂度:O(n logn)每一个数调用递归且规模显著减小

三路快速排序 (Turnary Quick Sort)

选择枢纽(pivot),然后递归小于和大于部分,适用于有大量重复值

平均时间复杂度:O(n log n)

最坏时间复杂度:O(n²)

堆排序 (Heap Sort)

建堆(大顶堆(升序所用)或小顶堆(降序所用)),将堆顶(当前最大或最小值)与末尾元素交换,堆区缩小 1,随后对新堆顶执行下沉恢复堆性质,当堆大小减到 1 时,数组已按升序排列

时间复杂度:O(n log n)