【经典阅读】《算法导论》Chapter1-2 Getting Started

1. 预习部分
  • 伪代码约定
    1. 缩进结构
    2. 循环计数器迭代增加用 t o to to,减小用 d o w n t o downto downto,变化量大于1时前面加个 b y by by
    3. 全为局部变量
    4. 过程按值传参,被调过程中对参数的操作不改变调用过程中的参数,但是对于对象成员的改变是会影响的。eg: 在被调过程中传参x并执行 x . f = 3 x.f=3 x.f=3,在调用程序中也会有改变
    5. 可以 r e t u r n return return多个值
  • 循环不变式的三条性质
    1. 初始(Initialization):第一次迭代前为真
    2. 保持(maintenance):循环某次的迭代之前为真,那么下一次迭代前也为真
    3. 终止(termination):循环终止时,看看最后的输出和相关状态是否正确,借此证明算法正确性
  • 插入排序(见习题2.1-2
  • 归并排序(归并步骤常见写法见习题2.3-2)
    //哨兵法
    MERGE(A, p, q, r)   //归并有序数组A[p..q] 与 A[q+1..r]
    	n1 = q-p+1
    	n2 = r-q
    	let L[1..n1+1] and R[1..n2+1] be new arrays
    	for i = 1 to n1
    		L[i] = A[i+p-1]
    	for j = 1 to n2
    		R[j] = A[q+j]
    	i = 1
    	j = 1
    	L[n1+1] = INF
    	R[n2+1] = INF
    	for k = p to r
    		if L[i] <= R[j]
    			A[k] = L[i]
    			i = i+1
    		else
    			A[k] = R[j]
    			j = j+1
    
    MERGE-SORT(A, p, r)
    	q = (p+r) / 2
    	MERGE-SORT(A, p, q)
    	MERGE-SORT(A, q+1, r)
    	MERGE(A, p, q, r)		
    
2. 课堂笔记(Lecture 1)

在这里插入图片描述

3. 部分练习与思考题解答
章节练习题

2.1-2
Rewrite the INSERTION-SORT procedure to sort into non-increasing instead of non-decreasing order.

solve:

INSERTION-SORT(A)
	for j = 2 to A.length
		key = A[j]
		i =  j-1
		while i>0 and A[i] < key  //非降序则是 A[i] > key,此外无区别
			A[i+1] = A[i]
			i = i-1
		A[i+1] = key



2.1-3
Consider the searching problem:
Input: A sequence of n n n numbers A = ⟨ a 1 , a 2 , . . . , a n ⟩ A= \langle a_1, a_2, ..., a_n \rangle A=a1,a2,...,anand a value v v v.
Output: An index i i i such that v = A [ i ] v = A[i] v=A[i] or the special value NIL if v v v does not appear in A.
Write pseudocode for linear search, which scans through the sequence, looking for v v v. Using a loop invariant, prove that your algorithm is correct. Make sure that your loop invariant fulfills the three necessary properties.

solve:

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

用循环不变式证明算法的正确性(Sample Answer):
在这里插入图片描述

2.1-4
Consider the problem of adding two n-bit binary integers, stored in two n-element
arrays A A A and B B B. The sum of the two integers should be stored in binary form in an (n+1)-element array C C C. State the problem formally and write pseudocode for adding the two integers.

solve:

ADD_TWO_NUMS(A, B, C, n)
	carry = 0 //进位记录
	for i = n downto 1
		C[i+1] = (A[i] + B[i] + carry) % 2 
		carry = (A[i] + B[i] + carry) / 2
	C[i+1] = carry		

Sample Answer:
在这里插入图片描述


2.2-2
在这里插入图片描述
solve:

SELECTION-SORT(A, n)
	for i = 1 to n-1
		min_i = i
		minval = A[i]
		for j = i+1 to n
			if minval > A[j]
				minval = A[j]
				min_i = j
		swap(A[i], A[min_i]) //交换值	

Loop Invariant: the subarray A [ 1... i − 1 ] A[1...i-1] A[1...i1] consists of the smallest i − 1 i-1 i1 elements with non-descending order.
Reason for n-1: when n-1 elements is the smallest, then the last element is the nth smallest.So the array is ordered.
best-case: Θ ( n 2 ) \Theta(n^2) Θ(n2)
worst-case: Θ ( n 2 ) \Theta(n^2) Θ(n2)


2.3-2
在这里插入图片描述
solve:

MERGE(A, p, q, r)   //归并有序数组A[p..q] 与 A[q+1..r]
	n1 = q-p+1
	n2 = r-q
	let L[1..n1] and R[1..n2] be new arrays
	for i = 1 to n1
		L[i] = A[i+p-1]
	for j = 1 to n2
		R[j] = A[q+j]
	i = 1
	j = 1
	k = 1
	while i<=n1 and j<=n2
		if L[i] <= R[j]
			A[k] = L[i]
			i = i+1
		else
			A[k] = R[j]
			j = j+1
		k = k+1
	while i<=n1
		A[k] = L[i]
		i = i+1
		k = k+1
	while j<=n2
		A[k] = R[j]
		j = j+1
		k = k+1	

2.3-5
在这里插入图片描述
iterative:

BINARY-SEARCH(A, l, r, v)  // find key v in A[l..r] (assume A[l..r] is non-descending)
	while l <= r
		mid = (l + r) / 2
		if v == A[mid]
			return mid
		else if v > A[mid]
			l = mid+1
		else
			r = mid-1		
 	return NIL // not found

recursive:

BINARY-SERACH(A, l, r, v)
	if l > r
		return NIL
	mid = (l + r) / 2
	if A[mid] == v
		return mid
	else if A[mid] > v
		return BINARY-SEARCH(A, l, mid-1, v)
	else
		return BINARY-SEARCH(A, mid+1, r, v)

2.3-6
在这里插入图片描述

2.3-7★
在这里插入图片描述

//排序 + 双指针,如果返回位置的话还需要散列表记录元素位置,这里假定只返回数字值
TWO-SUM(S, n, x)
	if n <= 0
		return NIL
	MERGE-SORT(S, 1, n)   // sort the S[1..n] with non-descending order  -> Θ(nlgn)
	i = 1
	j = n
	while i < j   //Θ(n)
		sum = S[i] + S[j] 
		if sum == x
			return {S[i], S[j]}
		else if sum < x
			i = i+1
		else
			j = j-1
	return NIL
章末思考题

几个重要的题目用红色星星标记了一下,供重点回顾(其实思考题都很重要),其中标记的伪代码最好有时间实现一下。

2-1 Insertion sort on small arrays in merge sort
在这里插入图片描述
solve:
a. n / k × Θ ( k 2 ) = Θ ( n k ) n/k \times \Theta(k^2) = \Theta(nk) n/k×Θ(k2)=Θ(nk)


b. n / k n/k n/k个序列两两归并,归并次数为 Θ ( l g ( n / k ) ) \Theta(lg(n/k)) Θ(lg(n/k)) ,每层归并都需要比较n次(有n个元素),故为 Θ ( n l g ( n / k ) ) \Theta(nlg(n/k)) Θ(nlg(n/k))


c. 标准归并算法: Θ ( n l g n ) \Theta(nlgn) Θ(nlgn),改进的归并算法: Θ ( n k + n l g ( n / k ) ) \Theta(nk +nlg(n/k)) Θ(nk+nlg(n/k))
要使运行时间相同,则有下面等式:
Θ ( n l g n ) = Θ ( n k + n l g ( n / k ) ) \Theta(nlgn) = \Theta(nk +nlg(n/k)) Θ(nlgn)=Θ(nk+nlg(n/k)),那么有
Θ ( n ( k − l g k ) ) ≤ Θ ( n l g n ) \Theta(n(k-lgk)) \le\Theta(nlgn) Θ(n(klgk))Θ(nlgn),即 Θ ( k − l g k ) ≤ Θ ( l g n ) \Theta(k-lgk) \le \Theta(lgn) Θ(klgk)Θ(lgn)
可见 k = Θ ( l g n ) k = \Theta(lgn) k=Θ(lgn)时满足不等式


d. 确保k长度下的数组排序时,插排比二路归并排序快就行了


2-2 Correctness of bubblesort
在这里插入图片描述
solve:
a. 还需要证明有序序列 A ′ A^\prime A的组成元素与 A A A相同,避免程序执行过程中有元素丢失

b. 直接贴答案了(对应第二层循环)
在这里插入图片描述

c. loop invariant: 序列 A [ 1.. i − 1 ] A[1..i-1] A[1..i1]为非降序序列,且序列 A [ i . . n ] A[i..n] A[i..n] A A A的剩余元素组成的序列
证明正确性:

  1. 初始: i = 1 i=1 i=1,此时序列为空,自然成立
  2. 保持: i > 1 i>1 i>1时, A [ 1.. i − 1 ] A[1..i-1] A[1..i1]非降序,第二层循环将从最末位至第i位的元素依次比较,并将最小者置换到第i位置, i = i + 1 i=i+1 i=i+1,由此下次循环前 A [ 1.. i ] A[1..i] A[1..i]为非降序
  3. 终止: i = A . l e n g t h i=A.length i=A.length,此时 A [ 1.. A . l e n g t h − 1 ] A[1..A.length-1] A[1..A.length1]已为非降序序列,那么 A [ A . l e n g t h ] A[A.length] A[A.length] A [ 1.. A . l e n g t h ] A[1..A.length] A[1..A.length]中的最大者,因此 A [ 1.. A . l e n g t h ] A[1..A.length] A[1..A.length]也是非降序序列,由此冒泡排序算法正确。

d. Θ ( n 2 ) \Theta(n^2) Θ(n2),与插排一致


2-3 Correctness of Horner’s rule

在这里插入图片描述
ANSWER

a. Θ ( n ) \Theta(n) Θ(n)

b. 一项项的求和就好,运行时间 Θ ( n 2 ) \Theta(n^2) Θ(n2),显然会比题示方法慢。

c,d直接上答案(循环不变式的证明还是不熟悉呀):
在这里插入图片描述


2-4 Inversions
在这里插入图片描述
ANSWER
a. (1,5), (2,5), (3,4), (3,5),(4,5)

b. 降序序列逆序对最多: C n 2 = n ( n − 1 ) / 2 C_n^2 = n(n-1)/2 Cn2=n(n1)/2

c.
不严格证明:
0个逆序对时插排时间为 Θ ( n ) \Theta(n) Θ(n),此时每趟只有一次比较,没有移动
1个逆序对时插排最好情况为 Θ ( n + 1 ) \Theta(n+1) Θ(n+1),有一次移动操作;最坏情况为 Θ ( n + n ) \Theta(n+n) Θ(n+n)
C n 2 C_n^2 Cn2个逆序对时插排对应最遭情况, Θ ( n 2 ) \Theta(n^2) Θ(n2)
对于 k k k个逆序对,那么运行时间为 Θ ( n + k ) \Theta(n+k) Θ(n+k)

官方证明:
在这里插入图片描述


d. 与答案有些许不同,可以找时间写个具体代码验证一下

CROSS-INVERSION-COUNTS(A, p, q, r)		 //题干中说明了元素各不相同
	if p>=r
		return 0
	n1 = q-p+1
	n2 = r-q
	let L[1..n1] and R[1..n2] be new arrays
	for i = 1 to n1
		L[i] = A[i+p-1]
	for j = 1 to n2
		R[j] = A[q+j]
	i = 1
	j = 1
	k = 1
	count = 0 //inversion counter
	while i<=n1 and j<=n2
		if L[i] <= R[j]
			A[k] = L[i]
			i = i+1
		else
			count = count + j
			A[k] = R[j]
			j = j+1			
		k = k+1
	while i<=n1
		count = count + n2
		A[k] = L[i]
		i = i+1
		k = k+1
	while j<=n2
		A[k] = R[j]
		j = j+1
		k = k+1		
	return count
	
CALC-INVERSION-COUNTS(A, p, r)
	q = floor((p+r) / 2)
	lcnt = CALC-INVERSION-COUNTS(A, p, q)
	rcnt = CALC-INVERSION-COUNTS(A, q+1, r)
	mcnt = CROSS-INVERSION-COUNTS(A, p, q, r)		
	return lcnt + mcnt + rcnt

//execute in main
CALC-INVERSION-COUNTS(A, 1, n)
一些有用的参考链接
  1. CLRS Solutions Site (习题答案基本都有)
  2. 麻省理工学院公开课:算法导论

猜你喜欢

转载自blog.csdn.net/weixin_42430021/article/details/108940168
今日推荐