第2章 算法基础

2.1 插入排序

2.1-1  

INSERTION-SORT在数组A=<31, 41, 59, 26, 41, 58>上的执行过程。

 

 

 

 

 

 

2.1-2

 按非升序排序(而不是非降序)的过程INSERTION-SORT:

INSERTION-SORT(A)
    for j = 2 to A.length
        key = A[j]
        // Insert A[j] into the sorted sequence A[1..j - 1].
        i = j - 1
        while i > 0 and A[i] < key
            A[i + 1] = A[i]
            i = i - 1
        A[i + 1] = key

2.1-3

 线性查找的伪代码:

LINEAR-SEARCH(A, v)
    for i = 1 to A.length
        if A[i] == v
            return i
    return NIL

循环不变式:

在第2~4行的for循环的每次迭代开始时,子数组A[1..i - 1]中的元素都不等于v。

2.1-4 

形式化描述:

输入: 代表n位二进制整数的两个n元数组A和B。

输出: (n+1)元数组C使得C代表的二进制整数等于A和B代表的两个二进制整数之和。

伪代码:

ADD-INTEGER(A, B)
    key = 0
    for i = 1 to n
        C[i] = (A[i] + B[i] + key) mod 2
        key = ⌊(A[i] + B[i] + key) / 2⌋
    C[n + 1] = key
    return C

2.2 分析算法

2.2-1

函数n^{3}/1000-100n^{2}-100n+3\Theta记号表示为\Theta (n^{3})

2.2-2 

选择算法伪代码:

SELECT-SORT(A)
    for i = 1 to A.length - 1
        smallest = i
        for j = i + 1 to A.length
            if A[j] < A[smallest]
                smallest  = i
        exchange A[i] with A[smallest]

 循环不变式:

在第2~5行的for循环的每次迭代开始时,A[i - 1]是数组中第i - 1小的元素。

因为当循环结束时,数组的前n - 1个数依次是数组中前n - 1小的元素,则第n个数是最大的数。所以只需要对前n - 1个元素,而不是对所有n个元素运行。 

 选择排序的最好情况与最坏情况运行时间都是\Theta (n^{2})

2.2-3 

假定要查找的元素等可能地为数组中的任意元素,线性查找平均需要检查输入序列的一半元素。最坏情况需要检查输入序列的所有元素。 

 线性查找的平均情况运行时间为\Theta (n/2),最坏情况运行时间为\Theta (n)

证明:

设线性查找需要检查输入序列的元素数为x。

  1. 在平均情况下,x取1~n中任一个数的概率都为1/n,则E(x)=\sum_{x=1}^n \frac{1}{n}\ast x=\frac{n(n+1)}{2} \frac{1}{n}=\frac{n+1}{2}。所以线性查找的平均情况运行时间为\Theta (n/2)
  2. 在最坏情况下,x一直取n,所以线性查找的最坏情况运行时间为\Theta (n)

2.2-4

修改算法,使它测试输入是否满足某些特殊情况,如果有的话,输出一个预先计算的答案。 

2.3 设计算法

2.3.1 分治法

2.3.2 分析分治算法

2.3-1 

归并排序在数组A=<3, 41, 52, 26, 38, 57, 9, 49>上的操作:

2.3-2

重写过程MERGE:

MERGE(A, p, q, r)
    n = 1
    Let L[1..q - p + 1] and R[1..r - q] be new arrays
    for i = p to q
        L[n] = A[i]
        n = n + 1
    n = 1
    for j = q + 1 to r
        R[n] = A[j]
        n = n + 1
    i = 1
    j = 1
    for k = p to r
        if i ≤ q - p + 1 and (j > r - q or L[i] ≤ R[j])
            A[k] = L[i]
            i = i + 1
        else
            A[k] = R[j]
            j = j + 1

2.3-3

证明:

  1. n=2时,T(n)=2=2\lg2成立。
  2. n=2^{k}时,T(2^{k})=2^{k}\lg{2^{k}}成立。 当n=2^{k+1}时,T(2^{k+1})=2T(2^{k})+2^{k+1}=2\ast 2^{k}\lg{2^{k}}+2^{k+1}=2^{k+1}(\lg{2^{k}} + 1)=2^{k+1}\lg{2^{k+1}}成立。 

所以递归式的解是T(n)=n\lg{n}。 

2.3-4 

插入排序的递归版本的最坏情况运行时间递归式:T(n)=T(n-1)+n

2.3-5 

迭代二分查找伪代码:

BINARY-SEARCH(A, v)
    i = 1
    j = A.length
    while i ≤ j
        mid = ⌊(i + j) / 2⌋
        if v == A[mid]
            return mid
        else if v < A[mid]
            j = mid - 1
        else
            i = mid + 1
    return NIL

证明:

假设T(n)是规模为n的一个问题的最坏情况运行时间,因为T(n)=T(n/2)+\Theta (1),则T(n)=\Theta (\lg{n})。所以二分查找的最坏情况运行时间为\Theta (\lg{n})

2.3-6 

 可以使用二分查找来把插入排序的最坏情况总运行时间改进到\Theta (n\lg{n})


思考题

2-1 

a.证明:

因为插入排序排序长度为k的单个子表的最坏情况运行时间为\Theta (k^{2}),则排序每个长度为kn/k个子表的最坏情况运行时间为\Theta (k^{2})\ast \frac{n}{k}=\Theta (nk)。所以插入排序最坏情况可以在\Theta (nk)时间内排序每个长度为kn/k个子表。

b.因为合并2个长度为k的子表的最坏情况运行时间为\Theta (2k),在递归树的每一层有n/k个长度为k的子表,合并一层的所有子表的最坏情况运行时间为\Theta (2k)\ast \frac{n}{2k}=\Theta (n),递归树总共有\lg{n/k}层,则合并所有层的所有子表的最坏情况运行时间为\Theta (n)\ast \lg{n/k}=\Theta (n\lg{(n/k)})

c.要使修改后的算法与标准的归并排序具有相同的运行时间,即\Theta (nk+n\lg{(n/k)})=\Theta (n\lg{n}),则\Theta (k+\lg{(n/k)})=\Theta (\lg{n})\Rightarrow \Theta (k)=\Theta (\lg{n})。所以k的最大值是\lg{n}

d.在实践中,应该选择插入排序比归并排序快的k值。

2-2

a.还需要证明A'[1..n]可以构成原数组。

b.第2~4行的for循环的循环不变式:

在开始第2~4行for循环的每次迭代时,A[j]是子数组A[j..A.length]中最小的元素。

证明:

初始化:在循环的第一次迭代之前,有j=A.length,子数组A[j..A.length]中只有一个元素,所以A[j]是子数组A[j..A.length]中最小的元素。

保持:首先假设A[j]< A[j-1],因为A[j]是子数组A[j..A.length]中最小的元素,所以在第4行将A[j]A[j-1]交换之后,A[j-1]是子数组A[j-1..A.length]中最小的元素。反之,若A[j]\geq A[j-1]A[j-1]已是子数组A[j-1..A.length]中最小的元素,则不交换A[j]A[j-1]

终止:终止时j=i,根据循环不变式,A[i]是子数组A[i..A.length]中最小的元素。

c. 第1~4行的for循环的循环不变式:

在开始第1~4行for循环的每次迭代时,子数组A[1..i-1]已按从小到大的顺序包含数组A中的i-1个最小元素。

证明:

初始化:在循环的第一次迭代之前, 有i=1,子数组A[1..i-1]为空。这个空的子数组包含数组Ai-1=0个最小元素。

保持:因为子数组A[1..i-1]已按从小到大的顺序包含数组A中的i-1个最小元素,第2~4行的for循环终止时,根据(b)部分证明的循环不变式,A[i]是子数组A[i..A.length]中最小的元素,所以子数组A[1..i]已按从小到大的顺序包含数组A中的i个最小元素。

终止:终止时i=A.length,根据循环不变式,子数组A[1..A.length-1]已按从小到大的顺序包含数组A中的A.length-1个最小元素,则A[A.length]是数组A中的最大元素,所以数组A'[1..n]按从小到大的顺序包含数组A中的n个元素。

d. 冒泡排序的最坏情况运行时间是\Theta (n^{2}),其性能与插入排序相当。

2-3

a.实现霍纳规则的以上代码片段的运行时间是\Theta (n)

b.朴素的多项式求值算法:

POLYNOMIAL-EVALUATION(a, x)
    y = 0
    for i = 0 to n
        z = 1
        for j = 1 to i
            z = z * x
        y = y + a[i] * z

该算法的运行时间是\Theta (n^{2}),其性能比霍纳规则差。

c.证明:

初始化:在循环的的第一次迭代之前,有i=ny=\sum _{k=0}^{n-(i+1)}a_{k+i+1}x^{k}=0

保持:因为y=\sum _{k=0}^{n-(i+1)}a_{k+i+1}x^{k},所以在第3行将a_{i-1}+x\cdot y赋值给y后,y=a_{i-1}+x\cdot \sum _{k=0}^{n-(i+1)}{a_{k+i+1}x^{k}}=a_{i-1}+\sum _{k=0}^{n-(i+1)}{a_{k+i+1}x^{k+1}}=\sum _{k=0}^{n-i}{a_{k+i}x^{k}}

终止:终止时i=-1,根据循环不变式,y=\sum _{k=0}^{n}{a_{k}x^{k}}

d.证明:

因为当算法终止时有y=\sum _{k=0}^{n}{a_{k}x^{k}},所以上面给出的代码片段将正确地求由系数a_0, a_1,\cdots ,a_n刻画的多项式的值。

2-4

a.数组<2, 3, 8, 6, 1>的5个逆序对:(1, 5)、(2, 5)、(3, 4)、(3, 5)、(4, 5)。

b.由集合{1, 2, ..., n}中的元素构成的已反向排序的数组具有最多的逆序对,有\frac{(n-1)n}{2}逆序对。

c.设插入排序的最佳情况运行时间为t(n),输入数组中逆序对的数量为m,则插入排序的运行时间T(n)=t(n)+km,其中k为交换数组中两个元素需要的时间。

d.确定在n个元素的任何排列中逆序对数量的算法,最坏情况需要\Theta (n\lg{n})时间: 

REVERSED-ORDER(A, p, r)
    if p < r
        q = ⌊(p + r) / 2⌋
        reverse = REVERSED-ORDER(A, p, q) + REVERSED-ORDER(A, q + 1, r) +
            REVERSED(A, p, q, r)
    else
        reverse = 0
    return reverse

REVERSED(A, p, q, r)
    reverse = 0
    n = 1
    Let L[1..q - p + 1] and R[1..r - q] be new arrays
    for i = p to q
        L[n] = A[i]
        n = n + 1
    n = 1
    for j = q + 1 to r
        R[n] = A[j]
        n = n + 1
    i = 1
    j = 1
    for k = p to r
        if i ≤ q - p + 1 and (j > r - q or L[i] ≤ R[j])
            A[k] = L[i]
            i = i + 1
        else
            A[k] = R[j]
            j = j + 1
            reverse = reverse + (q - p - i + 2)
    return reverse

 

  • 12
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值