排序算法(编辑中)

内容纲要

最近更新于 2023-02-07 20:03

大纲

\begin{cases}
插入排序
\begin{cases}
直接插入排序 \\
折半插入排序 \\
希尔排序
\end{cases}
\\
交换排序
\begin{cases}
冒泡排序 \\
快速排序
\end{cases}
\\
选择排序
\begin{cases}
简单选择排序 \\
堆排序
\end{cases}
\\
归并排序 \\
基数排序
\end{cases}

环境

平台工具集:Visual Studio 2022 v143
C 语言标准:C17
字符集:Unicode

默认升序

插入排序

直接插入排序

以 6 3 9 7 5 为例,将其分为有序数组和无序数组,初始有序数组成员为原数组第一个元素
6 | 3 9 7 5

第一轮:将无序数组第一个成员 3 提出来,并和有序数组最后一个成员 6 比较,3 小于 6,则有序数组最后一个成员往后移动一位
□ 6 | 9 7 5 (3)
有序数组已到头,则将提出来的 3 插入到空位
3 6 | 9 7 5

第二轮:将无序数组第一个成员 9 提出来,将其和有序数组最后一个元素 6 比较,9 大于 6 无序移动,则不移动
3 6 9 | 7 5

第三轮:将无序数组第一个元素 7 提出来,将其和有序数组最后一个元素 9 比较, 7 小于 9,将 7 往后移动一位
3 6 □ 9 | 5 (7)
再将 7 和有序数组倒数第二个元素 6 比较,7 大于 6,则不移动,并将提出来的 7 插入空位
3 6 7 9 | 5

第四轮:将无序数组第一个元素 5 提出来和有序数组最后一个元素 9 比较,5 小于 9,9 后移一位
3 6 7 □ 9 (5)
再将 5 和有序数组倒数第二个元素 7 比较,5 小于 7,7 后移一位
3 6 □ 7 9 (5)
再将 5 和有序数组倒数第三个元素 6 比较,5 小于 6,6 后移一位
3 □ 6 7 9 (5)
再将 5 和有序数组倒数第四个元素比较,5 大于 3,不移动,将提出来的 5 插入空位
3 5 6 7 9

直接插入法属于稳定的排序算法。时间复杂度为 O(n^2)。适用于待排序元素较少且元素基本有序的情况。

void sort(int array[], int n)
{
    for (int j, i = 1; i < n; ++i)
    {
        int tmp = array[i];
        for (j = i - 1; j >= 0 && tmp < array[j]; --j)
        {
                array[j + 1] = array[j];
        }
        array[j + 1] = tmp;
    }
}

file

折半插入排序

折半插入排序是直接插入排序的改进算法,大体类似,都是先分一个有序数组出来,再挨个把无序数组元素插入有序数组的适当位置。只是插入位置的查找方法不同,直接插入排序是挨个从有序数组的尾巴向开头比较,而折半插入排序使用折半查找算法,在查找部分的时间复杂度由 O(n) 降到 O(nlog_2n),但是移动元素的时间复杂度依然没有改变,总体时间复杂度还是 O(n^2)
折半插入排序也是稳定的排序算法。
适用于待排序元素较少且元素基本有序的情况。

void sort(int array[], int n)
{
    int low, high;
    for (int i = 1; i < n; ++i)
    {
        int tmp = array[i];
        for (low = 0, high = i - 1; high >= low;)
        {
            int mid = (low + high) / 2;
            if (tmp < array[mid])
            {
                high = mid - 1;
            }
            else
            {
                low = mid + 1;
            }
        }
        for (int j = i - 1; j >= low; --j)
        {
            array[j + 1] = array[j];
        }
        array[low] = tmp;
    }
}

希尔排序

希尔排序是基于直接插入改进的,将待排序元素按照一定增量分为多个子序列(一般首次增量取总数一半),例如待排序的元素为 9 8 7 6 5 ,共 5 个,增量取 5 / 2 = 2
则先比较下标为 2 和 2-1=0 的两个数,提出 7,和 9 比较,9 大于 7,则 9 移动到 7 的位置,到头了,将提出的 7 插入 9 的位置,得到 7 8 9 6 5
接下来比较 3 和 3 – 1 = 1 …….
……
上面其实就是对每个子序列分别使用直接插入排序
增量下一次取上一次增量的一半 2 / 2 = 1,增量为 1 后,跑完这一轮就代表排序完成。

希尔排序是一种不稳定的排序算法。由于增量的选择是随机的,因此时间复杂度的计算就十分复杂,只是在一定的范围内,但是希尔排序也是第一个时间复杂度突破 O(n^2) 的排序算法
希尔排序主要用在数据量在 5000 以内,且对速度要求不高的场合。

void insert_sort(int array[], int n, int delta)
{
    for (int i = delta; i < n; ++i)
    {
        if (array[i] < array[i - delta])
        {
            int k, tmp = array[i];
            for (k = i - delta; k >= 0 && tmp < array[k]; k  -= delta)
            {
                array[k + delta] = array[k];
            }
            array[k + delta] = tmp;
            printArray(array, 9);
        }
    }
}

void sort(int array[], int n)
{
    for (int delta = n / 2; delta >= 1; delta /= 2)
    {
        insert_sort(array, n, delta);
    }
}

以 9 8 7 6 5 4 3 2 1 为例,共 n = 9,初始 delta = n / 2 = 4

[0] [1] [2] [3] [4] [5] [6] [7] [8] delta i k
9 8 7 6 5 4 3 2 1 4 4 0
5 8 7 6 9 4 3 2 1 5 1
5 4 7 6 9 8 3 2 1 6 2
5 4 3 6 9 8 7 2 1 7 3
5 4 3 2 9 8 7 6 1 8 4
5 4 3 2 8 7 6 9 0
1 4 3 2 5 8 7 6 9 2 2 0
3 1
1 2 3 4 5 8 7 6 9 4 2
0
5 3
1
6 4
6 2
6 0
7 5
1 2 3 4 5 6 7 8 9

交换排序

冒泡排序

冒泡排序思路非常简单,第一轮将第一个和第二个比较,前者大,两个就交换位置,然后比较第二个和第三个,第三个和第四个……倒数第二个和最后一个,一轮完毕最大的数就在最后一个
第二轮,还是一样,第一个和第二个比较….倒数第三个和倒数第二个比较,此轮完毕,倒数第二大的数就在倒数第二个位置
第三轮,……倒数第四个和倒数第三个比较,此轮完毕,倒数第三大的数就在倒数第三个位置
……

冒泡排序是一种稳定的排序方法。时间复杂度为 O(n^2),适用于待排序元素较少,且对时间要求不高的场合。

void sort(int array[], int n)
{
    for (int i = 1; i < n; ++i)
    {
        for (int j = 0; j < n - i; ++j)
        {
            if (array[j] > array[j + 1])
            {
                int tmp = array[j + 1];
                array[j + 1] = array[j];
                array[j] = tmp;
            }
        }
    }
}

快速排序 –

选择排序

简单选择排序

简单选择排序的思想感觉和冒泡排序比较相似。
简单选择排序,每一轮找出未排序部分的最小值的下标,然后将这个最小值和当前轮次对应下标的数交换。比如第一轮找出的最小值和下标 0 交换,第二轮从下标 1 开始找最小值记录其下标,然后将最小值和下标 1 的数交换,第三轮从下标 2 开始找最小值,接着和下标 2 的数交换……
相比于冒泡排序,并不需要每次都去交换,只需要记录其下标,直到本轮找到最小值,最后才交换,性能会好一点
时间复杂度为 O(n^2),适用于数据量小对事件要求不高的场合。

void sort(int array[], int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        int minIdx = i;
        for (int j = i + 1; j < n; ++j)
        {
            if (array[minIdx] > array[j])
            {
                minIdx = j;
            }
        }
        if (minIdx != i)
        {
            int tmp = array[i];
            array[i] = array[minIdx];
            array[minIdx] = tmp;
        }
    }
}

堆排序 –

归并排序 –

基数排序 –