数据结构与算法之美专栏学习笔记-排序(上)

排序方法

冒泡排序、插入排序、选择排序、快速排序、归并排序、计数排序、基数排序、桶排序。

复杂度归类

冒泡排序、插入排序、选择排序 O(n^2)

快速排序、归并排序 O(nlogn)

计数排序、基数排序、桶排序 O(n)

 

算法的执行效率

1. 最好、最坏、平均情况时间复杂度。

2. 时间复杂度的系数、常数和低阶。

3. 比较次数,交换(或移动)次数。

排序算法的稳定性

稳定性概念

如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。

稳定性重要性

可针对对象的多种属性进行有优先级的排序。

排序算法的内存损耗

原地排序算法:特指空间复杂度是O(1)的排序算法。


冒泡排序

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求,如果不满足就让它俩互换。

稳定性

冒泡排序是稳定的排序算法。

空间复杂度

冒泡排序是原地排序算法。

时间复杂度

最好情况:O(n)。

最坏情况:O(n^2)。

平均情况:平均时间复杂度为O(n^2)。

C#代码演示

public void BubbleSort(int[] data,int n)
{
    if (n <= 1) return;//长度小于1的数组直接返回
    for(int j = 0; j < n; j++)//遍历数组
    {
        Boolean flag = false;//设标志位
        for (int i = 0; i < n-j-1; i++)//遍历数组未被排序的部分
        {
            if (data[i] > data[i + 1])//比较元素大小
            {
                int temp = data[i];//交换数据
                data[i] = data[i + 1];
                data[i + 1] = temp;
                flag = true;
            }
        }
        if (!flag) return;//如果一次冒泡无交换说明全部有序,返回
    }
}

插入排序

插入排序将数组数据分成已排序区间和未排序区间。初始已排序区间只有一个元素,即数组第一个元素。

在未排序区间取出一个元素插入到已排序区间的合适位置,直到未排序区间为空。

稳定性

插入排序是稳定的排序算法。

空间复杂度

插入排序是原地排序算法。

时间复杂度

1. 最好情况:O(n)。

2. 最坏情况:O(n^2)。

3. 平均情况:O(n^2)。

C#代码演示

public void InsertionSort(int[] data,int n)
{
    if (n <= 1) return;
    for(int i = 1; i < n; i++)//遍历数组
    {
        int value = data[i];//记录要进行排序的第i个数组成员
        int j = i - 1;//从第i个数组成员前的成员开始遍历到头
        for (; j >= 0; --j)
        {
            if (data[j] > value)//如果其比第i个数组成员大,将其前移
                data[j + 1] = data[j];
            else//否则就结束,因为前面的数据必然是有序的
                break;
        }
        data[j + 1] = value;//在前移结束后停下的位置前插入记录的数据
    }
}

选择排序

选择排序将数组分成已排序区间和未排序区间。初始已排序区间为空。

每次从未排序区间中选出最小的元素插入已排序区间的末尾,直到未排序区间为空。

稳定性

选择排序不是稳定的排序算法。

空间复杂度

选择排序是原地排序算法。

时间复杂度

都是O(n^2))

C#代码演示

public static void SelectionSort(int[] data,int n)
{
    if (n <= 1) return;
    for(int i = 0; i < n; i++)//遍历数组
    {
        int temp = data[i];//记录要排序的数组成员
        int pos = i;//记录最终的交换位置
        for(int j = i; j < n; j++)//遍历数组未交换的部分,找到最小的成员并记录其位置
        {
            if (data[j]<data[i])//如果未交换的部分比第i个成员小
            {
                data[i] = data[j];//将其提到前面
                pos = j;//记录交换的位置
            }
        }
        data[pos] = temp;//互换数据
    }
}

思考

冒泡排序和插入排序的时间复杂度相同都是O(n^2),为什么插入排序比冒泡排序更受欢迎?

因为冒泡排序的交换操作需要执行三次操作。

如果数据存储在链表中,三种排序方法的时间复杂会变成怎样?

假定只能改变节点位置

冒泡排序,比较次数不变,因为指针,交换数据更加复杂。

插入排序,比较次数不变,但可以直接插入数据,不需要一个个地后移数据。

选择排序,比较次数不变,因为指针,交换数据更加复杂。

猜你喜欢

转载自www.cnblogs.com/errornull/p/9891343.html