简单排序(选择、冒泡、插入)

涉及的一些知识

  1. 逆序对

    A A A为一个有 n n n个数字的有序集( n > 1 n>1 n>1),其中所有数字各不相同。如果存在正整数 i i i j j j使得 1 ≤ i < j ≤ n 1 \leq i < j \leq n 1i<jn而且 A [ i ] > A [ j ] A[i]>A[j] A[i]>A[j],则 ( A [ i ] , A [ j ] ) (A[i],A[j]) (A[i],A[j])这个有序对称为A的一个逆序对。

  2. 定理

    任意 N N N个不同元素组成的序列平均具有 N ( N − 1 ) / 4 N(N-1)/4 N(N1)/4个逆序对。

  3. 稳定性

    能保证两个相等的数,经过排序之后,其在序列的前后位置顺序不变。( A 1 = A 2 A_1=A_2 A1=A2,排序前 A 1 A_1 A1 A 2 A_2 A2前面,排序后 A 1 A_1 A1还在 A 2 A_2 A2前面)

简单排序

简单排序有三种,选择排序(Selection Sort)、冒泡排序(Bubble Sort)和插入排序(Insertion Sort)。

选择排序

  1. 选择排序
    从待排序序列选择最小的元素与序列第1个元素交换;从剩下的元素中选择最小的元素与序列第2个元素交换;依此类推直到最后一个元素。

  2. 代码实现:

    /* 选择排序 */
    void SelectionSort(ListElementType *A, int Size)
    {
        int i = 0, j = 0, Min = 0;
    
        /* 从待排序序列选择最小的元素与序列第1个元素交换;从剩下的元素中选择最小的元素与序列第2个元素交换;依此类推直到最后一个元素。 */
        for (i = 0; i < Size; i++)
        {
            for (j = i, Min = j; j < Size; j++)
            {
                if (A[j] < A[Min])
                {
                    Min = j; /* Min存储最小元素下标 */
                }
            }
            Swap(&A[Min], &A[i]); /* 将未排序列中最小元素与A[i]交换位置 */
        }
    }
    
  3. 时间复杂度
    最坏情况时间复杂度: T w o r s t ( N ) = O ( N 2 ) T_{worst}(N)=O(N^2) Tworst(N)=O(N2)
    平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2)

  4. 稳定性
    选择排序是不稳定排序。

冒泡排序

  1. 冒泡排序

    1. 假设数组有 n n n个元素,从头到尾依次比较元素A[i]和A[i+1],如果顺序不对,将两个元素调换位置。当比较完元素A[n-2]和A[n-1]的时候,数组中最大的(假设从小到大排序)元素就会被放到数组最后一个位置。
    2. 对数组中前 n − 1 n-1 n1个元素重复第一步的操作,可将 n − 1 n-1 n1个元素中最大的元素放在 n − 1 n-1 n1个元素的最后位置。
    3. 重复执行第一步 n − 1 n-1 n1次,可将数组排序完成。
  2. 代码实现

    /* 冒泡排序 */
    void BubbleSort(ListElementType *A, int Size)
    {
        int i = 0, j = 0, flog = 0;
    
        for (i = 0; i < Size - 1; i++)
        {
            for (j = 0; j < Size - i - 1; j++)
            {
                if (A[j] > A[j + 1]) /* 比较两个元素的大小,将较大的放在较小的后面 */
                {
                    Swap(&A[j], &A[j + 1]);
                }
            }
        }
    }
    
  3. 优化
    当某次遍历的之后,如果序列已经排好序了,那么就没有必要继续遍历下去了,所以我们可以增加一个标记flag,用来监控某一次遍历过程中是否发生元素位置的交换,如果未发生交换,就可以直接停止循环了。

    /* 冒泡排序 */
    void BubbleSort(ListElementType *A, int Size)
    {
        int i = 0, j = 0, flag = 0;
    
        for (i = 0; i < Size - 1; i++)
        {
            flag = 0; /* 用来检测一趟冒泡之中是否发生交换,没发生交换证明序列已经排好序了 */
            for (j = 0; j < Size - i - 1; j++)
            {
                if (A[j] > A[j + 1])
                {
                    Swap(&A[j], &A[j + 1]);
                    flag = 1;
                }
            }
    
            /* 没发生交换,直接退出循环 */
            if (flag == 0)
            {
                break;
            }
        }
    }
    
  4. 时间复杂度

    • 优化前:
      最坏情况时间复杂度: T w o r s t ( N ) = O ( N 2 ) T_{worst}(N)=O(N^2) Tworst(N)=O(N2)
      平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2)
    • 优化后:
      时间复杂度: T ( N ) = O ( N + I ) T(N)=O(N+I) T(N)=O(N+I)(其中 I I I是数组中逆序对的个数)
      平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2)
  5. 稳定性
    冒泡排序是稳定排序。

插入排序

  1. 插入排序

    1. 认为数组第一个元素是有序的,第二个元素和第一个元素比较大小,如果第二个元素比较小,则两者交换位置。此时,第一第二个元素就是有序的了。
    2. 第三个元素和它前一个元素对比,如果第三个元素比较小,互换位置,然后该元素再与互换位置后的它前面的一个元素做同样的操作,直到它比它前一个元素值大或它变成了第一个元素。操作结束后前三个元素就是有序的了。
    3. 一直重复上面的操作,直到所有元素都变成有序的。
  2. 代码实现

    /* 插入排序 */
    void InsertionSort(ListElementType *A, int Size)
    {
        int i = 0, j = 0;
        ListElementType Temp;
    
        for (i = 1; i < Size; i++)
        {
            Temp = A[i];
            for (j = i; j > 0 && A[j - 1] > Temp; j--)
            {
                A[j] = A[j - 1]; /* 向后移动 */
            }
            A[j] = Temp;
        }
    }
    
  3. 时间复杂度
    时间复杂度: T ( N ) = O ( N + I ) T(N)=O(N+I) T(N)=O(N+I)(其中 I I I是数组中逆序对的个数)
    平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2)

  4. 稳定性
    插入排序是稳定排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值