算法导论 第二章算法基础(1)插入排序

本文深入解析了插入排序算法,包括其工作原理、伪代码实现及Python代码示例。同时,探讨了算法的循环不变式概念,如何确保算法正确性和效率。此外,文章还提供了查找特定值和二进制加法的额外算法示例。

插入排序

输入: n个数的一个序列 { a 1 , a 2 , ⋯   , a n } \{a_1, a_2, \cdots, a_n\} {a1,a2,,an}
输出: 输入序列的一个排序 { a 1 ′ , a 2 ′ , ⋯   , a n ′ } \{a^\prime_1, a^\prime_2, \cdots, a^\prime_n\} {a1,a2,,an}, 满足 a 1 ′ ≤ a 2 ′ ≤ ⋯ ≤ a n ′ a^\prime_1 \le a^\prime_2 \le \cdots \le a^\prime_n a1a2an

插入排序原址排序输入的数,算法在数组 A A A中重排这些数,在任何时候,最多只有其中的常数个数字存储在数组外面。

  • Insertion-Sort(A) 伪代码
for j = 2 to A.length
    key = A[j]
    i = j - 1
    while(i > 0 and A [i] > key)
        A[i + 1] = A[i]
        i = i - 1
    A[i + 1] = key
# -*- coding:utf-8 -*-
# 插入排序(降序)
# input: {a1, a2, a3, a4, ..., an}
# output: {a1 >= a2 >= a3 >= a4 >= ... >= an}
import random

def InsertionSort(array):
    for i in range(len(array) -2 , -1 , -1):
        key = array[i]
        j = i + 1
        while j < len(array) and array[j] > key:
            array[j - 1] = array[j]
            j = j + 1

        array[j - 1] = key
if __name__ == "__main__":
    array = random.sample([x for x in range(100)], 20)
    print("before: ", array)
    InsertionSort(array)
    print("end: ", array)
    

循环不变式定义

  • 初始化 : 循环的第一次迭代之前,它为真。
  • 保持 : 如果循环的某次迭代前它为真,那么下次迭代前它仍然为真。
  • 终止 :在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。

插入排序的循环不变式

  • 初始化 : 首先证明在第一次循环之前(当 j = 2 j=2 j=2时),循环不变式成立。所以子数组 A [ 1.. j − 1 ] A[1 .. j-1] A[1..j1]仅由单个元素 A [ i ] A[i] A[i]组成,实际上就是 A [ 1 ] A[1] A[1]中原来的元素。显然循环不变式在第一次迭代之前成立。
  • 保持 : 非形式化的,for循环体的第5~7行将 A [ j − 1 ] , A [ j − 2 ] , A [ j − 3 ] A[j-1],A[j-2],A[j-3] A[j1],A[j2],A[j3]等向右移动一个位置,直到找到 A [ j ] A[j] A[j]的适当位置,第7行将 A [ j ] A[j] A[j]的值插入指定的位置,此时子数组 A [ 1.. j ] A[1..j] A[1..j]由原来在 A [ 1.. j ] 的 元 素 组 成 , 但 是 已 经 排 序 , 那 么 对 f o r 循 环 的 下 一 次 迭 代 增 加 A[1..j]的元素组成,但是已经排序,那么对for循环的下一次迭代增加 A[1..j]forj$将保持循环不变式。
  • 终止 :导致for循环终止的条件是 j &gt; A . l e n g t h = n j&gt;A.length = n j>A.length=n。因为每循环迭代增加 j j j增加 1 1 1,那么必有 j = n + 1 j=n+1 j=n+1。在循环不变式中用 j j j代替 n + 1 n+1 n+1,我们有:子数组 A [ 1.. n ] A[1..n] A[1..n]由原来在A[1…n]中的元素组成,但已经排序。因此算法正确。

练习

2.1-1

# -*- coding:utf-8 -*-

def InsertionSort(array):
    for i in range(1, len(array)):
        key = array[i]
        j = i - 1
        while j >= 0 and array[j] > key:
            array[j+1] = array[j]
            j = j - 1
        array[j + 1] = key
        print(array)

if __name__ == "__main__":
    array = [31, 41, 59, 26, 41, 58]
    InsertionSort(array)

2.1-2

# -*- coding:utf-8 -*-
# 插入排序 (升序)
# input: {a1, a2, a3, a4, ..., an}
# output: {a1 <= a2 <= a3 <= a4 <= ... <= an}

import random

def InsertionSort(array):
    for i in range(1, len(array)):
        key = array[i]
        j = i - 1
        while j >= 0 and array[j] > key:
            array[j+1] = array[j]
            j = j - 1
        array[j + 1] = key

if __name__ == "__main__":
    array = random.sample([x for x in range(100)], 20)
    print("before: ", array)
    InsertionSort(array)
    print("end: ", array)

2.1-3

输入 n n n个数的一个序列 A = { a 1 , a 2 , ⋯ &ThinSpace; , a n } A=\{a_1,a_2,\cdots,a_n\} A={a1,a2,,an}和一个值 v v v
输出 :下标 i i i使得 v = A [ i ] v=A[i] v=A[i]或者当 v v v不在 A A A中出现时, v v v为特殊值 N I L NIL NIL

伪代码:FIND-KEY(A, v)

for i = 1 to A.length
    if A[i] == v
        return i
    return NIL

循环不变式 :对于每次循环迭代,子数组(已遍历的元素)中没有与 v v v相等的值。

初始化 :第一次循环之前,子数组没有元素,其中肯定没有与 v v v相等的值,循环不变式成立。

保持 :每次迭代都将比较 A [ i ] A[i] A[i] v v v的值,如果相等,循环终止;如果不等, A [ 1 ] … A [ i ] A[1] \dots A[i] A[1]A[i]中没有与 v v v相等的值,循环不变式保持。

终止 :当 i &gt; A . l e n g t h i &gt; A.length i>A.length 时或者 A [ i ] = = v A[i] == v A[i]==v 时循环终止。显然对于子数组即下标小于 i i i的所有元素均没有与 v v v相等的值,算法正确。

2.1-4

A,B各存放了一个二进制n位整数的各位数值,现在通过二进制的加法对其进行计算,结果以二进制的形式把各位上的数值存放在C数组中。

# -*- coding:utf-8 -*-

def binarySum(aArray, bArray):
    c = [0]* (len(aArray) + 1)

    for i in range(len(aArray) - 1, -1, -1):
        if((a[i] + b[i] + c[i + 1]) > 1):
            c[i] = 1
        c[i + 1] = c[i + 1] ^ a[i] ^ b[i]

    return c

if __name__ == "__main__":
    a = [1, 0, 1, 1, 0, 1, 0, 1, 1, 1]
    b = [0, 1, 1, 1, 0, 0, 1, 1, 0, 1]
    #100 1010 0100
    c = binarySum(a, b)
    print(c)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值