排序算法

本文详细介绍了几种基本的排序算法,包括冒泡排序、插入排序、归并排序、快速排序以及拓扑排序。冒泡排序和插入排序的时间复杂度为O(n^2),适合小规模数据;归并排序和快速排序采用分治策略,时间复杂度为O(nlogn),适用于大规模数据。此外,还探讨了各种排序算法在不同场景下的性能表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序

基本的排序算法

  • 冒泡排序
  • 插入排序
    常考的排序算法
  • 归并排序
  • 快速排序
  • 拓扑排序

其他排序算法

  • 堆排序
  • 桶排序

冒泡排序

每一轮,从杂乱无章的数组头部开始,每两个元素比较大小并进行交换;指导这一轮当中最大或者最小的元素被放置在数组的尾部;然后,不断地重复这个过程,直到所有元素都排好位置
例题:给定数组[2,1,7,9,5,8],要求按照从左到右,从小到大的顺序进行排序
python代码对冒泡排序的实现如下:

from typing import List


def bubble_sort(li: List[int]) -> List[int]:
    for i in range(len(li) - 1):
        for j in range(len(li) - 1 - i):
            if li[j] > li[j + 1]:
                li[j], li[j + 1] = li[j + 1], li[j]

    return li


if __name__ == '__main__':
    test_li = [2, 1, 7, 9, 5, 8]
    print(bubble_sort(test_li))

空间复杂度:O(1)
假设数组的元素个数是n,整个排序的过程中,直接在给定的数组进行元素的两两交换
时间复杂度:O(n^2)
情景一:给定的数组已经排好序,只需要进行n-1次的比较,两两交换次数为0,时间复杂度为O(n),这是最好的情况
情景二:给定的数组按照逆序排列,需要进行n(n-1)/2次比较,时间复杂度为O(n^2),这是最坏的情况
情景三:给定的数组杂乱无章,在这种情况下,平均时间复杂度是O(n^2)

插入排序

与冒泡排序的对比
在冒泡排序中,经过每一轮的排序处理后,数组后端的数是排好序的;
在插入排序中,经过每一轮的排序处理后,数组前端的数都是排好序的
插入排序的算法思想
不断地将尚未排好序的数插入到已经排好序的部分
python代码对插入排序的实现如下:

from typing import List


def insert_sort(li: List[int]) -> List[int]:
    for i in range(1, len(li)):
        for j in range(i, 0, -1):
            if li[j] < li[j-1]:
                li[j], li[j-1] = li[j-1], li[j]

    return li


if __name__ == '__main__':
    test_li = [2, 1, 7, 9, 5, 8]
    print(insert_sort(test_li))

空间复杂度:O(1)
假设数组的元素个数是n,整个排序的过程中,直接在给定的数组里进行元素的两两交换
时间复杂度:O(n^2)
情景一:给定的数组已经排好序,只需要进行n-1次的比较,两两交换次数为0,时间复杂度为O(n),这是最好的情况
情景二:给定的数组按照逆序排列,需要进行n(n-1)/2次比较,时间复杂度为O(n^2),这是最坏的情况
情景三:给定的数组杂乱无章,在这种情况下,平均时间复杂度是O(n^2)

归并排序

分治的思想:归并排序的核心思想是分治,把一个复杂问题拆分成若干个子问题来求解
归并排序的算法思想
把数组从中间分成两个子数组;
一直递归地把子数组划分成更小地子数组,直到子数组里面只有一个元素;
一次按照递归的返回顺序,不断地合并排好序地子数组,直到最后把整个数组地顺序排好
python代码对归并排序的实现如下:

from typing import List


def merge_sort(lst: List[int]) -> List[int]:
    if len(lst) <= 1:
        # 当列表元素只有一个的时候,直接返回
        return lst
    mid = len(lst) // 2
    left = lst[:mid]
    right = lst[mid:]

    left = merge_sort(left)
    right = merge_sort(right)
    # 递归的进行排序
    result = []
    while left and right:
        if left[0] <= right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
    if left:
        result += left
    if right:
        result += right
        
    return result


if __name__ == "__main__":
    test_li = [2, 1, 7, 9, 5, 8]
    print(merge_sort(test_li))

时间复杂度:T(n)
归并算法是一个不断递归的过程,假设数组的元素个数是n
时间复杂度是T(n)的函数:T(n)=2*T(n/2)+O(n)
怎么解这个公式呢?
对于规模为n的问题,一共要进行log(n)层的大小切分;
每一层的合并复杂度都是O(n)
所以整体的复杂度就是O(nlogn)
空间复杂度:O(n)
由于合并n个元素需要分配一个大小为n的额外数组,合并完成之后,这个数组的空间就会被释放

快速排序

基本思想
快速排序也采用了分治的思想;
把原始的数组筛选成较小和较大的两个子数组,然后递归地排序两个子数组;
在分成较小和较大地两个子数组过程中,如何选定一个基准值尤为关键
最有情况下地时间复杂度
T(n)=2*T(n/2)+O(n)
O(n)是怎么得出来得
把规模大小为n的问题分解为n/2的两个子问题;
和基准值进行n-1次比较,n-1次比较的复杂度就是O(n);
快速排序的复杂度也是O(nlogn)
最复杂的情况
每次在选择基准值的时候;
都不幸选择了子数组里面的最大或者最小值;
其中一个子数组长度为1;
另一个长度只比父数组少1
空间复杂度:O(logn)
和归并排序不同,快速排序在每次递归的过程中;
只需要开辟O(1)的存储空间来完成交换操作实现直接对数组的修改;
而递归次数为logn,所以它的整体空间复杂度完全取决于压堆栈的次数
python代码对快速排序的实现如下:

from typing import List


def quick_sort(li: List[int]) -> List[int]:
    if len(li) < 2:
        return li
    else:
        pivot = li[0]  # 递归条件,基准
        less = [i for i in li[1:] if i <= pivot]

        greater = [i for i in li[1:] if i > pivot]

        return quick_sort(less) + [pivot] + quick_sort(greater)


if __name__ == '__main__':
    test_li = [2, 1, 7, 9, 5, 8]
    print(quick_sort(test_li))

拓扑排序

应用场合
拓扑排序就是将图论里的顶点按照相连的性质进行排序
前提
必须是有向图
图里没有环
例:有一个学生想要修完5门课程的学分,这5们课程分别用1、2、3、4、5来表示,现在已知学习这些课程有如下的要求:

  • 课程2和4依赖于课程1
  • 课程3依赖于课程2和4
  • 课程4依赖于课程1和2
  • 课程5依赖于课程3和4
    那么,这个学生应该按照怎样的顺序来学习这5门课程呢?
    在这里插入图片描述
    时间复杂度:O(n)
    统计顶点的入度需要O(n)的时间;
    接下来每个顶点被遍历一次,同样需要O(n)的时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值