《算法导论》练习与思考题第1-3章 (python版)

第一章 算法在计算中的作用

练习

1.1 算法

1.1-1

排序的例子:一个医生要接诊的病人会按照挂号时间排序。

1.1-2

热效率,衡量发动机等热能转换装置能量利用的效率

1.1-3

数据结构:链表
优势:顺序访问效率高
局限:随机访问效率低

1.1-4

相似之处:都是点与点之间的移动;都是求最短的移动路线
不同:最短路径问题有明确的解法,而旅行商问题是NP完全问题,没有有效算法。

1.1-5

必须是最佳解的例子:一个数学定理的证明
可以使用近似解的例子:手术方案的建立

1.2 作为一种技术的算法

1.2-1

应用层的例子:一个压缩软件,在应用层就会使用压缩算法。
算法的功能:应该将输入的数据无损地转换为更小的数据

1.2-2

当n的取值为2-6时插入排序优于归并排序

1.2-3

n最小为15时在同一台机器上 100 n 2 100n^2 100n2的算法优于 2 n 2^n 2n的算法

思考题

1-1 运行时间的比较

思路:先将给定时间转换为毫秒,然后用 f ( n ) f(n) f(n)的逆函数求出对应的n值

1秒钟(1000ms) 1分钟(60000ms) 1小时(3600000ms) 1天(86400000ms) 1月(2592000000ms) 1年(31536000000ms) 1世纪(3153600000000ms)
lg ⁡ n \lg{n} lgn 1 0 1000 10^{1000} 101000 1 0 60000 10^{60000} 1060000 1 0 3600000 10^{3600000} 103600000 1 0 86400000 10^{86400000} 1086400000 1 0 2592000000 10^{2592000000} 102592000000 1 0 31536000000 10^{31536000000} 1031536000000 1 0 3153600000000 10^{3153600000000} 103153600000000
n \sqrt{n} n 100 0 2 1000^{2} 10002 6000 0 2 60000^{2} 600002 360000 0 2 3600000^{2} 36000002 8640000 0 2 86400000^{2} 864000002 259200000 0 2 2592000000^{2} 25920000002 3153600000 0 2 31536000000^{2} 315360000002 315360000000 0 2 3153600000000^{2} 31536000000002
n n n 1000 1000 1000 60000 60000 60000 3600000 3600000 3600000 86400000 86400000 86400000 2592000000 2592000000 2592000000 31536000000 31536000000 31536000000 3153600000000 3153600000000 3153600000000
n 2 n^2 n2 31 31 31 244 244 244 1897 1897 1897 9295 9295 9295 50911 50911 50911 177583 177583 177583 1 , 775 , 837 1,775,837 1,775,837
n 3 n^3 n3 10 10 10 39 39 39 153 153 153 442 442 442 1373 1373 1373 3159 3159 3159 14 , 664 14,664 14,664
2 n 2^n 2n 9 9 9 15 15 15 21 21 21 26 26 26 31 31 31 34 34 34 41 41 41
n ! n! n! 6 6 6 8 8 8 9 9 9 11 11 11 12 12 12 13 13 13 15 15 15

第二章 算法基础

练习

2.1 插入排序

2.1-1

用画图绘制的插入排序过程

2.1-2
def insertionSortNonAscending(A):
	for j in range(1,len(A)):
		key = A[j]
		i = j - 1
		while i >=0 and A[i] < key:
			A[i+1] = A[i]
			i -= 1
		A[i+1] = key
2.1-3
def linearFind(A, v):
	for j in range(len(A)):
		if A[j] == v:
			return j
	return None

循环不变式:已经搜索的子数组A[0…j-1]中不存在v

  • 初始化:迭代开始之前(当0=1时)子数组A[0…j-1]中一个元素也没有,循环不变式成立
  • 保持:如果A[j]等于v,循环终止。反之A[0…j]中不存在v,循环不变式成立
  • 终止:当j等于A.length时循环终止,此时已经搜索的子数组A[0…j-1]即原数组A[0…n]。而如果在循环中A[j]等于v时循环终止,此时前一次迭代已经搜索的子数组A[0…j-1]中没有v,循环不变式成立
2.1-4

二进制加法问题:
     输入:n个数的两个序列, A = < a 1 , a 2 , ⋯   , a n > A=<a_1,a_2,\cdots,a_n> A=<a1,a2,,an> B = < b 1 , b 2 , ⋯   , b n > B=<b_1,b_2,\cdots,b_n> B=<b1,b2,,bn>,A和B表示两个二进制数每个数位的依次排列
     输出: n+1个数的序列 C = < c 1 , c 2 , ⋯   , c n + 1 > C=<c_1,c_2,\cdots,c_{n+1}> C=<c1,c2,,cn+1>,C为二进制数A与B的和

def binarySum(A, B):
	carry = 0
	C = []
	for i in range(len(A)-1,-1,-1):
		sum = A[i] + B[i] + carry
		C.insert(0, sum % 2)
		carry = sum // 2 
	C.insert(0, carry)
	return C

2.2 分析算法

2.2-1

Θ ( n 3 ) \Theta(n^3) Θ(n3)

2.2-2
def selectionSort(A):
	for i in range(len(A)-1):
		min = i
		for j in range(i+1,len(A)):
			if A[min] > A[j]:
				min = j
		A[min],A[i]=A[i],A[min]

循环不变式:A[0…i-1]为已排序好的序列
考虑最后一次循环,此时i=n-1,A[1…n-2]为已排序的序列且都小于A[n-1]和A[n],当将A[n]和A[n-1]中最小的放到A[n-1]处时,A[n]处的数据也回到了正确的位置,所以只循环n-1次就足够。
最好情况和最坏情况都是 Θ ( n 2 ) \Theta(n^2) Θ(n2)的运行时间

2.2-3

平均情况和最坏情况运行时间均为 Θ ( n ) \Theta(n) Θ(n)
证明:
平均情况时,假设v有n+1种情况,v等可能的出现在A中任一位置或者不出现。
所以平均的运行时间为 1 + 2 + ⋯ + n n + 1 = 1 2 n ( n + 1 ) n + 1 = 1 2 n = Θ ( n ) \cfrac{1+2+\cdots+n}{n+1}=\cfrac{\frac{1}{2}n(n+1)}{n+1}=\frac{1}{2}n=\Theta(n) n+11+2++n=n+121n(n+1)=21n=Θ(n)
最坏情况时不出现,总共循环比较n次,运行时间为 Θ ( n ) \Theta(n) Θ(n)

2.2-4

???不太确定题的意思
大概是保证最好情况时循环能够直接结束程序给出答案吧。

2.3 设计算法

2.3-1

用画图绘制的归并排序过程

2.3-2
def merge(arr, p, q, r):
    n1 = q - p + 1
    n2 = r - q
    L = arr[p:q + 1]
    R = arr[q + 1:r + 1]
    i = j = 0
    k = p
    while i < n1 and j < n2:
        if L[i] <= R[j]:
            arr[k] = L[i]
            i += 1
        else:
            arr[k] = R[j]
            j += 1
        k += 1
    while i < n1:
        arr[k] = L[i]
        k += 1
        i += 1
    while j < n2:
        arr[k] = R[j]
        k += 1
        j += 1
2.3-3

首先:当k=1时 n = 2 k = 2 n=2^k=2 n=2k=2 T ( n ) = T ( 2 ) = 2 , n lg ⁡ n = 2 T(n)=T(2)=2,n\lg n=2 T(n)=T(2)=2,nlgn=2,所以 T ( n ) = n lg ⁡ n T(n)=n\lg n T(n)=nlgn
假设:k=m时等式成立, n = 2 m , T ( n ) = n lg ⁡ n = m 2 m n=2^m,T(n)=n\lg n=m2^m n=2m,T(n)=nlgn=m2m。因此k=m+1时将 n = 2 m + 1 n=2^{m+1} n=2m+1带入 T ( n ) = 2 T ( n 2 ) + n T(n)=2T(\cfrac{n}{2})+n T(n)=2T(2n)+n中,得 T ( n ) = 2 T ( 2 m ) + 2 m + 1 = m 2 m + 1 + 2 m + 1 = ( m + 1 ) 2 m + 1 T(n)=2T(2^m)+2^{m+1}=m2^{m+1}+2^{m+1}=(m+1)2^{m+1} T(n)=2T(2m)+2m+1=m2m+1+2m+1=(m+1)2m+1,而 n lg ⁡ n = 2 m + 1 lg ⁡ ( 2 m + 1 ) = ( m + 1 ) 2 m + 1 n\lg n= 2^{m+1}\lg(2^{m+1})=(m+1)2^{m+1} nlgn=2m+1lg(2m+1)=(m+1)2m+1,所以 T ( n ) = n lg ⁡ n T(n)=n\lg n T(n)=nlgn成立
综上所述,当n刚好是2的幂时,本题递归式的解为 T ( n ) = n lg ⁡ n T(n)=n\lg n T(n)=nlgn

2.3-4

T ( n ) = { Θ ( 1 ) , n = 1 T ( n − 1 ) + Θ ( n ) , n > 1 T(n)= \begin{cases} \Theta(1) ,&n=1\\ T(n-1) +\Theta(n),&n>1 \end{cases} T(n)={ Θ(1),T(n1)+Θ(n),n=1n>1

2.3-5
def binaryFind(A, v, p, q):
    if p <= q:
        r = (p + q) // 2
        if A[r] == v:
            return r
        elif A[r] < v:
            return binaryFind(A, v, r + 1, q)
        else:
            return binaryFind(A, v, p, r - 1)
    else:
        return None

最坏情况下,有如下递归式:
T ( n ) = { Θ ( 1 ) , n = 1 T ( n 2 ) + Θ ( 1 ) , n > 1 T(n)= \begin{cases} \Theta(1) ,&n=1\\ T(\cfrac{n}{2}) +\Theta(1),&n>1 \end{cases} T(n)=Θ(1),T(2n)+Θ(1),n=1n>1
显然有:
lg ⁡ n { T ( n ) − T ( n 2 ) = Θ ( 1 ) T ( n 2 ) − T ( n 2 2 ) = Θ ( 1 ) T ( n 2 2 ) − T ( n 2 3 ) = Θ ( 1 ) ⋯ ⋯ T ( 2 ) − T ( 1 ) = Θ ( 1 ) \lg n \begin{cases} T(n)-T(\cfrac{n}{2})=\Theta(1)\\ T(\cfrac{n}{2})-T(\cfrac{n}{2^2})=\Theta(1)\\ T(\cfrac{n}{2^2})-T(\cfrac{n}{2^3})=\Theta(1)\\ \cdots\cdots\\ T(2)-T(1)=\Theta(1) \end{cases} lgnT(n)T(2n)=Θ(1)T(2n)T(22n)=Θ(1)T(22n)T(23n)=Θ(1)T(2)T(1)=Θ(1)
所以最坏情况的运行时间为 Θ ( lg ⁡ n ) \Theta(\lg n) Θ(lgn)

2.3-6

不能,2.1节中INSERTION-SORT的while循环同时起到两个作用:找到已排序的n个数中要插入的位置和将该位置之后的元素向后移动一位,运行时间是 Θ ( n ) \Theta(n) Θ(n)。最坏情况时,找到位置的需求可以使用二分查找来降低运行时间为 Θ ( lg ⁡ n ) \Theta(\lg n) Θ(lgn),但移动元素的运行时间还是 Θ ( n ) \Theta(n) Θ(n),所以while循环的运行时间无法通过使用二分查找降低至 Θ ( lg ⁡ n ) \Theta(\lg n) Θ(lgn),所以最坏情况运行时间也无法使用二分查找改进到 Θ ( n lg ⁡ n ) \Theta(n\lg n) Θ(nlgn)的运行时间。

2.3-7

使用归并排序将集合的元素排序,时间复杂度为 Θ ( n lg ⁡ n ) \Theta(n\lg n) Θ(nlgn),假设排好序的元素组成序列 A = < a 1 , a 2 , ⋯   , a n > A=<a_1,a_2,\cdots,a_n> A=<a1,a2,,an>,其中 a 1 ⩽ a 2 ⩽ ⋯ ⩽ a n a_1\leqslant a_2\leqslant\cdots\leqslant a_n a1a2an。从数列两端遍历,让left= a 1 a_1 a1,right= a n a_n an,考虑left+right和x的关系。假设此时left= a l a_l al,right= a r a_r ar
如果left+right==x,说明正好找到了题目要求的两个数。
如果left+right>x,那么right右边的所有数 a r ′ a_{r'} ar都有left + a r ′ > +a_{r'}> +ar>left + a r > +a_{r}> +ar>x。此时将right左移一位,重新判断。
如果left+right<x,那么left左边的所有数 a l ′ a_{l'} al都有 a l ′ + a_{l'}+ al+right > a l >a_{l} >al+right > > >x。此时将left右移一位,重新判断。
当left和right重叠时说明已经找遍了可能产生x的两个数的组合。
可以看出,最好情况一次找到时间复杂度为 Θ ( 1 ) \Theta(1) Θ(1),最坏情况会遍历一遍排序完的序列,时间复杂度为 Θ ( 1 ) × n = Θ ( n ) \Theta(1)\times n=\Theta(n) Θ(1)×n=Θ(n),但无论什么情况显然远远小于 Θ ( n lg ⁡ n ) \Theta(n\lg n) Θ(nlgn)的排序时间,所以该算法的时间复杂度为 Θ ( n lg ⁡ n ) \Theta(n\lg n) Θ(nlgn)

思考题

2-1

a.插入排序n个元素的最坏情况时间复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2)。所以 n k \cfrac{n}{k} kn个子表插入排序的时间复杂度为 Θ ( k 2 ) × n k = Θ ( n k ) \Theta(k^2)\times\cfrac{n}{k}=\Theta(nk) Θ(k2)×kn=Θ(nk)

b.归并两个长度为k的子表时间复杂度为 Θ ( k ) \Theta(k) Θ(k),归并 n k \cfrac{n}{k} kn个子表时间复杂度为 Θ ( k ) × Θ ( n k ) = Θ ( n ) \Theta(k)\times\Theta(\cfrac{n}{k})=\Theta(n) Θ(k)×Θ(kn)=Θ(n),可知每轮归并时间复杂度为 Θ ( n ) \Theta(n) Θ(n) n k \cfrac{n}{k} kn个子表需要归并 Θ ( lg ⁡ ( n k ) ) \Theta(\lg(\cfrac{n}{k})) Θ(lg(kn))轮,所以合并子表最坏情况时间复杂度为

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

八云黧

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

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

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

打赏作者

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

抵扣说明:

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

余额充值