一文看懂三种经典排序算法:冒泡、快速、插入排序(含Python代码+解析)

排序是编程学习中的“必修课”,在各种数据处理场景中应用广泛。今天我就带大家用 Python 实现三种经典排序算法——冒泡排序、快速排序、插入排序,并配合详细注释和讲解,帮你真正理解它们的底层原理。


 1. 冒泡排序(Bubble Sort)

 原理说明:

冒泡排序的核心思想是:相邻元素两两比较,把大的“冒”到后面。每完成一轮,最大的元素就被“冒”到最右边。

思维过程:

  • 对整个列表进行多轮遍历

  • 每一轮遍历时,两两相邻元素比较,如果顺序不对就交换;

  • 每一轮结束,最大的一个数“沉底”到正确位置(这是外层循环的作用)。

  • 它的第二次循环最大范围取值的时间复杂度是 O(1) 。

为了方便大家理解代码运行的过程,在每一次循环结束之后展现本次排序结束等提示。可以复制代码到python中运行查看。

def B_sort(s):
    k=len(s)
    for i in range(k):
        j=i-1
        for j in range(0,k-i-1):# 把本次循环最大的一位数放在列表最后一个位置。并且下次的范围从后缩小一位
          if s[j] >s[j+1]:
              print("本次找到第",j+1,"位为[",s[j],"] 放到第",j+2,"位")
              print("排序后",s)
              s[j],s[j+1]=s[j+1],s[j]# 前后位置互换
              print("排序后",s)
              print()
        print("---------第",i+1,"次排序结束--------")

    return s
print(B_sort([5, 3, 8, 4, 2]))

下方是部分运行结果的呈现,将每一轮循环改变的数字位置都打印出来,观察数字的变化轨迹可以帮助更好的掌握对算法的理解。 

解析:

for i in range(0,k-i-1)其中的k-i-1 ,那我们就来详细解读一下它: 

  1. 我们要比较的是 arr[j]arr[j + 1],为了避免索引越界,j + 1 必须小于数组长度 k,也就是 j <= k - 2。 J的最大取值的时间复杂度是O(-(n+1))
  2. 但每一轮末尾第 i 个及后面的元素是排好序的,不需要再比较,因此有效比较范围应缩小为 j <= k - i - 2,即 range(0, k - i - 1)

Python实现:

def B_sort(input_list):
    k=len(input_list)
    for i in range(k):   #以列表的长度为内层循环的次数
         for j in range(0,k-i-1):   #主要是为了限制j的上限以及相邻的元素进行比较
            if input_list[j] > input_list[j+1]:
                input_list[j], input_list[j+1] =input_list[j+1],input_list[j]
    return input_list
print(B_sort([5,4,3,2,1]))


     2. 快速排序(Quick Sort)

    原理说明:

    快速排序采用的是:选定一个“基准数”,将比它小的放左边,大的放右边,再分别对左右两边递归排序。

    思维过程:

    • 选一个基准数 pivot

    • 通过一次 while 划分数组:

      • 把小的放左边

      • 把大的放右边

    • 然后:

      • 对左边再进行“同样的排序” (也就是递归调用。在函数里面“调用自己”,避免写重复的while语句)

      • 对右边也进行“同样的排序”

    Python实现:

    def quick_sort(arr,start,end):
    
        if start >= end:
            return
        pivot=arr[start]   # 把最左边的第一个数当成基准数(拿出)
        low =start  #low是数组最小下标
        high=end    #high是数组arr的最大下标
        # 最左边的数被拿走了,那么就从右边找一个小于基准的数放上
        while low <high:     #外部循环是用来控制一次划分基准左右两类的总循环,只要high ==low就进行下一次
            while low <high and arr[high] >= pivot:  #只要 arr[high] 的值比 pivot 大或相等,我们就继续往左走(high -= 1),跳过它。
                high -=1
            arr[low]=arr[high]     #如果一次没有小于pivot的数就进行从右往左找,找到后,再从左往右找
    
            while low < high and arr[low] <= pivot:
                low +=1
            arr[high] =arr[low]
    
        arr[low]=pivot
        quick_sort(arr,0,high-1)   #左区间递归
        quick_sort(arr,low+1, end)     #右区间递归
    li=[8,2,46,7,3,4,9,1]
    quick_sort(li,0,len(li)-1)
    print(li)

     解析:

    while low < high and arr[high] >= pivot: 
            high -= 1 
    arr[low] = arr[high] 
    
    这部分的意思是:如果满足low < high and arr[high] >= pivot,那么high = high -1 进行执行while,不满足while条件时,就输出arr[low] = arr[high]。下面的while语句也是类似的意思。
    

    li = [8,2,46,7,3,4,9,1]
    quick_sort(li,0,len(li)-1)
    print(li)


    3.插入排序(Insertion Sort)

    原理说明:

    每次从未排序部分拿一个数插入到前面已排好序的部分中

    核心思想:

          把整个数组看作“已排序”和“未排序”两部分,每次从未排序区取出一个元素,向前比较并插入到合适的位置,使前半部分始终保持有序。

     Python实现

    # 插入排序:将数字不断插入左边有序的部分中
    def i_sort(nums):
        li = len(nums)
        for i in range(1, li):  # 第一个数字默认有序,所以从第二个数字开始(下标为1)
            word = nums[i]  # 当前要插入的值
            j = i - 1  # j是指针,从当前元素前一个位置开始向左寻找插入位置(反向循环)
            while j >= 0 and nums[j] > word:  # word 反向循环比较,如果前面的数比当前值大,就向后移动一位,为插入留出空位
                nums[j + 1] = nums[j]  # 将前一位的值赋值到后一位
                j -= 1  # j 反向循环,向前继续比较前面的数
            nums[j + 1] = word  # 放在比word小的数后
        return nums
    
    
    s = [23, 22, 44, 11, 66, 12, 13, 16]
    i_sort(s)
    print(s)

    解析:

    1.for i in range(1,li) 

     外层循环查询列表的范围下标是递增的,  但是内部while语句中数值的比较是反向循环依次比较的。 

    4.补充内容

    时间复杂度:衡量算法运行时间随输入规模增长的计算次数,反映程序处理大数据时的效率。

    时间复杂度含义举个例子
    O(1)常数时间,不随输入变化访问数组某个下标、判断奇偶等
    O(n)线性时间,输入越大时间越长遍历一个列表
    O(n log n)近线性时间,比 n 慢但比 快速排序
    O(n²)平方时间,数据多时急剧增长冒泡排序、选择排序

    空间复杂度:衡量算法在运行过程中额外占用的内存大小,不包括输入数据本身

    原地排序不使用额外数组

    稳定性:相同元素排序后相对位置是否改变。

            举例:

    [(小藕, 80), (李同, 80), (王谋, 90)]
    

    按照成绩排序:

    • 如果是稳定排序 ➜ [(小藕, 80), (李同, 80), (王谋, 90)]

    • 如果是不稳定排序 ➜ [(李同, 80), (小藕, 80), (王谋, 90)]

    不稳定排序中小藕和李同的相同分数先后顺序就发生了改变

    排序算法平均时间复杂度空间复杂度是否原地排序是否稳定排序
    冒泡排序O(n²)O(1) 是 是
    插入排序O(n²)O(1) 是 是
    快速排序O(n log n)O(log n) 是 否

      总览:


    小结

    冒泡排序逻辑简单但效率低,更多用于算法入门练习,插入排序在小规模或近乎有序的数据中表现稳定,可作为其他排序算法的优化补充,而快速排序凭借较高的平均效率,成为处理大规模无序数据的常用选择。根据不同场景灵活选择排序算法,才能在实际开发中兼顾性能与效果。


    如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ 比心
    有任何排序算法相关问题,欢迎留言交流!

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值