插入排序 python实现

问题描述

输入】n个数组成的序列 A n = [ a 1 , a 2 , ⋅ ⋅ ⋅ , a n ] A_n=[a_1,a_2,···,a_n] An=[a1,a2,⋅⋅⋅,an]

输出 A n A_n An的一个重新排列 A n ′ A_n' An,使得 a 1 ≤ a 2 ≤ ⋅ ⋅ ⋅ ≤ a n a_1\le a_2\le ···\le a_n a1a2⋅⋅⋅an

算法展示

def insertion_sort(An):
    """插入排序"""
    n = len(An)  # 获取An的长度

    for i in range(1, n):
        key = An[i]  # 当前需要插入排序的值
        j = i - 1  # 左一位的下标
        while j >= 0 and An[j] > key:
            # 向左比较并移动,直到找到自己的位置
            An[j + 1] = An[j]
            j -= 1
        An[j + 1] = key
    return An

算法基本分析

加入一系列print语句以辅助分析:

def insertion_sort(An):
    """插入排序"""
    n = len(An)  # 获取An的长度
    print(f"输入:{An}\n")
    for i in range(1, n):
        key = An[i]  # 当前需要插入排序的值

        print(f"-----第{i}次-----")
        print(f"origin:{An[0:i]} {An[i:]}")

        j = i - 1  # 左一位的下标
        while j >= 0 and An[j] > key:
            # 向左比较并移动,直到找到自己的位置
            An[j + 1] = An[j]
            j -= 1
        An[j + 1] = key

        print(f"result:{An[0:i+1]} {An[i+1:]}")
    print(f"\n输出:{An}")
    return An

[6,7,3,4,8,1,2,5]作为输入的 A n A_n An ,得到以下结果:

输入:[6, 7, 3, 4, 8, 1, 2, 5]

-----第1次-----
origin:[6] [7, 3, 4, 8, 1, 2, 5]
result:[6, 7] [3, 4, 8, 1, 2, 5]
-----第2次-----
origin:[6, 7] [3, 4, 8, 1, 2, 5]
result:[3, 6, 7] [4, 8, 1, 2, 5]
-----第3次-----
origin:[3, 6, 7] [4, 8, 1, 2, 5]
result:[3, 4, 6, 7] [8, 1, 2, 5]
-----第4次-----
origin:[3, 4, 6, 7] [8, 1, 2, 5]
result:[3, 4, 6, 7, 8] [1, 2, 5]
-----第5次-----
origin:[3, 4, 6, 7, 8] [1, 2, 5]
result:[1, 3, 4, 6, 7, 8] [2, 5]
-----第6次-----
origin:[1, 3, 4, 6, 7, 8] [2, 5]
result:[1, 2, 3, 4, 6, 7, 8] [5]
-----第7次-----
origin:[1, 2, 3, 4, 6, 7, 8] [5]
result:[1, 2, 3, 4, 5, 6, 7, 8] []

输出:[1, 2, 3, 4, 5, 6, 7, 8]
循环不变式(Loop Invariant)

循环不变式是在每一次循环中都不改变的性质。它有助于我们分析归纳类算法的正确性。

循环不变式的特点:

  • 初始化时为真 (初始条件)
  • 若单次循环开始时为真,则本次循环结束时也为真 (循环不变条件)
  • 整个循环结束后,依旧为真 (结论)

可以对比数学归纳法:

  • i=1时,结论成立 (初始条件)
  • 若i=n时,结论成立,则i=n+1时,结论也成立 (归纳条件)
  • 得证,对任意i,结论均成立 (结论)

对插入排序算法进行分析,

外层循环的循环不变式为
第 i 次循环开始时 , 左侧子序列 A n [ 0 : i ] 已排好序 , 即满足   a 1 < a 2 < ⋅ ⋅ ⋅ < a i 第i次循环开始时,\\ 左侧子序列A_n[0:i]已排好序,\\ 即满足\ \ a_1<a_2<···<a_i i次循环开始时,左侧子序列An[0:i]已排好序,即满足  a1<a2<⋅⋅⋅<ai

  • 初始化时, A n [ 0 : 1 ] A_n[0:1] An[0:1] 只有一个数,显然为真
  • 假设第i次循环时, A n [ 0 : i ] A_n[0:i] An[0:i]已排好序。此时key=An[i]。经过内层循环的比较与移动, A n [ 0 : i ] A_n[0:i] An[0:i]中大于key的值都在key的右侧,小于等于key的值都在key的左侧。即, A n [ 0 : i + 1 ] A_n[0:i+1] An[0:i+1]这i+1个数也已排好序。
  • 得证,经过n-1次循环后, A n [ 0 : n ] A_n[0:n] An[0:n]这n个数已排好序。即,得到了排好序的 A n ′ A_n' An .

算法复杂度分析

输入规模(Input Size)

输入规模是指算法所接受的输入单位数据的个数。

不同问题的输入单位数据可能是不同的。

插入排序的输入单位数据是列表中的单个数字。

因此,其输入规模n为输入数字列表的元素个数。

运行时间(Running Time)

假设代码的每一步执行需要常数时间 c i c_i ci

记所有代码运行的时间和记为算法的运行时间 T n T_n Tn

def insertion_sort(An):
    n = len(An)   #----- c1  1
    for i in range(1, n):  #----- c2  n
        key = An[i]   #----- c3  n-1
        j = i - 1	#----- c4  n-1
        while j >= 0 and An[j] > key:  #----- c5  sum_{i=1}^{n-1} t_i
            An[j + 1] = An[j] #-----c6 sum_{i=1}^{n-1} (t_i-1)
            j -= 1 #-----c7 sum_{i=1}^{n-1} (t_i-1)
        An[j + 1] = key #-----c8 n-1
    return An

如果为最佳情况,即输入的 A n A_n An已经排好了序,则 c 5 c_5 c5步骤只需要执行1次就能退出内层循环

∑ i = 1 n − 1 t i = ∑ i = 1 n − 1 1 = n − 1 \displaystyle \sum_{i=1}^{n-1}t_i=\displaystyle \sum_{i=1}^{n-1}1=n-1 i=1n1ti=i=1n11=n1 , ∑ i = 1 n − 1 ( t i − 1 ) = ∑ i = 1 n − 1 0 = 0 \displaystyle \sum_{i=1}^{n-1}(t_i-1)=\displaystyle \sum_{i=1}^{n-1}0=0 i=1n1(ti1)=i=1n10=0

故执行时间

T ( n ) = c 1 + c 2 n + c 3 ( n − 1 ) + c 4 ( n − 1 ) + c 5 ( n − 1 ) + c 8 ( n − 1 ) T(n)=c_1+c_2n+c_3(n-1)+c_4(n-1)+c_5(n-1)+c_8(n-1) T(n)=c1+c2n+c3(n1)+c4(n1)+c5(n1)+c8(n1)

= ( c 2 + c 3 + c 4 + c 5 + c 8 ) n − ( c 3 + c 4 + c 5 + c 8 − c 1 ) =(c_2+c_3+c_4+c_5+c_8)n-(c_3+c_4+c_5+c_8-c_1) =(c2+c3+c4+c5+c8)n(c3+c4+c5+c8c1)

= A n + B =An+B =An+B

如果为最坏情况,即输入的 A n A_n An为逆序,则 c 5 c_5 c5步骤只需要执行(i+1)次才能退出内层循环

∑ i = 1 n − 1 t i = ∑ i = 1 n − 1 ( i + 1 ) = ( 2 + n ) ( n − 1 ) 2 = n 2 + n − 2 2 \displaystyle \sum_{i=1}^{n-1}t_i=\displaystyle \sum_{i=1}^{n-1}(i+1)=\frac{(2+n)(n-1)}{2}=\frac{n^2+n-2}{2} i=1n1ti=i=1n1(i+1)=2(2+n)(n1)=2n2+n2 ,

∑ i = 1 n − 1 ( t i − 1 ) = ∑ i = 1 n − 1 i = n ( n − 1 ) 2 = n 2 − n 2 \displaystyle \sum_{i=1}^{n-1}(t_i-1)=\displaystyle \sum_{i=1}^{n-1}i=\frac{n(n-1)}{2}=\frac{n^2-n}{2} i=1n1(ti1)=i=1n1i=2n(n1)=2n2n

故执行时间

T ( n ) = c 1 + c 2 n + c 3 ( n − 1 ) + c 4 ( n − 1 ) + c 5 ( n 2 + n − 2 2 ) + c 6 ( n 2 − n 2 ) + c 7 ( n 2 − n 2 ) + c 8 ( n − 1 ) \displaystyle T(n)=c_1+c_2n+c_3(n-1)+c_4(n-1)+c_5(\frac{n^2+n-2}{2})+c_6(\frac{n^2-n}{2})+c_7(\frac{n^2-n}{2})+c_8(n-1) T(n)=c1+c2n+c3(n1)+c4(n1)+c5(2n2+n2)+c6(2n2n)+c7(2n2n)+c8(n1)

= c 5 + c 6 + c 7 2 n 2 + ( c 5 − c 6 − c 7 2 + c 2 + c 3 + c 4 + c 8 ) n + ( c 1 − c 3 − c 4 − c 5 − c 8 ) \displaystyle =\frac{c_5+c_6+c_7}{2}n^2+(\frac{c_5-c_6-c_7}{2}+c_2+c_3+c_4+c_8)n+(c_1-c_3-c_4-c_5-c_8) =2c5+c6+c7n2+(2c5c6c7+c2+c3+c4+c8)n+(c1c3c4c5c8)

= A n 2 + B n + C =An^2+Bn+C =An2+Bn+C

设算法的执行时间为 T ( n ) T(n) T(n),则大O表示法---- O (   f ( n )   ) O(\ f(n)\ ) O( f(n) )代表 T ( n ) T(n) T(n)的上界的量级,大 Ω \Omega Ω 表示法---- Ω (   f ( n )   ) \Omega(\ f(n)\ ) Ω( f(n) )代表 T ( n ) T(n) T(n)的下界的量级。

如表示插入排序的执行时间: O (   n 2   ) O(\ n^2\ ) O( n2 ) , Ω (   n   ) \Omega(\ n\ ) Ω( n )

常用大O表示法分析算法的时间复杂度。

由上述分析,

插入排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子希卡利

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值