常见排序算法 | 青训营笔记

75 阅读3分钟

常见排序算法

1. 冒泡排序

1.1 基本思想

  • 两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

1.2 算法描述

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

1.3 算法分析

  • 最佳情况:T(n) = O(n)
  • 最差情况:T(n) = O(n2)
  • 平均情况:T(n) = O(n2)

1.4 代码实现

void bubble_sort(int *arr, int len)
{
    int i, j, temp;
    for (i = 0; i < len - 1; i++) {
        for (j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

2. 选择排序

2.1 基本思想

  • 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  • 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  • 重复第二步,直到所有元素均排序完毕。

2.2 算法描述

  • 初始状态:无序区为 R[1..n],有序区为空;
  • 第 i 趟排序(i=1,2,3...n-1)开始时,当前有序区和无序区分别为 R[1..i-1]和 R(i..n)。该趟排序从当前无序区中- 选出关键字最小的记录 R[k],将它与无序区的第 1 个记录 R 交换,使 R[1..i]和 R[i+1..n)分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区;
  • n-1 趟结束,数组有序化了。

2.3 算法分析

  • 最佳情况:T(n) = O(n2)
  • 最差情况:T(n) = O(n2)
  • 平均情况:T(n) = O(n2)

2.4 代码实现

void select_sort(int *arr, int len)
{
    int i, j, min, temp;
    for (i = 0; i < len - 1; i++) {
        min = i;
        for (j = i + 1; j < len; j++) {
            if (arr[min] > arr[j]) {
                min = j;
            }
        }
        if (min != i) {
            temp = arr[min];
            arr[min] = arr[i];
            arr[i] = temp;
        }
    }
}

3. 插入排序

3.1 基本思想

  • 从第一个元素开始,该元素可以认为已经被排序
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描
  • 如果该元素(已排序)大于新元素,将该元素移到下一位置
  • 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置
  • 将新元素插入到该位置后
  • 重复步骤 2~5

3.2 代码

void insert_sort(int *arr, int len)
{
    int i, j, temp;
    for (i = 1; i < len; i++) {
        temp = arr[i];
        for (j = i; j > 0 && arr[j - 1] > temp; j--) {
            arr[j] = arr[j - 1];
        }
        arr[j] = temp;
    }
}

4. 希尔排序

4.1 基本思想

  • 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  • 按增量序列个数 k,对序列进行 k 趟排序;
  • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

4.2 代码

void shell_sort(int *arr, int len)
{
    int i, j, temp, gap;
    for (gap = len >> 1; gap > 0; gap >>= 1) {
        for (i = gap; i < len; i++) {
            temp = arr[i];
            for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
                arr[j + gap] = arr[j];
            }
            arr[j + gap] = temp;
        }
    }
}

5. 快速排序

5.1 基本思想

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割之后,该基准是它的最后位置。这个称为分割(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

5.2 代码

void quick_sort(int *arr, int left, int right)
{
    int i, j, temp;
    if (left > right) {
        return;
    }
    temp = arr[left];
    i = left;
    j = right;
    while (i != j) {
        while (arr[j] >= temp && i < j) {
            j--;
        }
        while (arr[i] <= temp && i < j) {
            i++;
        }
        if (i < j) {
            swap(&arr[i], &arr[j]);
        }
    }
    arr[left] = arr[i];
    arr[i] = temp;
    quick_sort(arr, left, i - 1);
    quick_sort(arr, i + 1, right);
}