[算法导论][练习题]2.3

2.3-1

使用图2-4作为模型,说明归并排序再数组 A = < 3 , 41 , 52 , 26 , 38 , 57 , 9 , 49 > A=<3, 41, 52, 26, 38, 57, 9, 49> A=<3,41,52,26,38,57,9,49>上的操作


答:
[ 3 9 26 38 41 49 52 57 ] [ 3 \quad 9 \quad 26 \quad 38 \quad 41 \quad 49 \quad 52 \quad 57 ] [39263841495257]
↑ \uparrow
[ 3 26 41 52 ∣ 9 38 49 57 ] [ 3 \quad 26 \quad 41 \quad 52 | 9 \quad 38 \quad 49 \quad 57 ] [32641529384957]
↑ \uparrow
[ 3 41 ∣ 26 52 ∣ 38 57 ∣ 9 49 ] [ 3 \quad 41 | 26 \quad 52 | 38 \quad 57 | 9 \quad 49 ] [34126523857949]
↑ \uparrow
[ 3 ∣ 41 ∣ 52 ∣ 26 ∣ 38 ∣ 57 ∣ 9 ∣ 49 ] [ 3 | 41 | 52 | 26 | 38 | 57 | 9 | 49 ] [34152263857949]

2.3-2

重写过程MERGE,使之不使用哨兵,而是一旦数组L或R的所有元素均被复制回A就立即停止,然后把另一个数组的剩余部分复制回A。


答:
伪代码1:

MERGE(A, p q, 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[p + i - 1]
	for j = 1 to n2
		R[j] = A[q + j]
	i = 1
	j = 1
	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		
		if i > n1 or j > n2
				break
	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:

MERGE(A, p q, 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[p + i - 1]
	for j = 1 to n2
		R[j] = A[q + j]
	i = 1
	j = 1
	for k = p to r
		if i > n1
			A[k] = R[j]
			j = j + 1
		else if j > n2
			A[k] = L[i]
			i = i + 1
		else if L[i] <= R[j]
			A[k] = L[i]
			i = i + 1
		else
			A[k] = R[j]
			j = j + 1

2.3-3

使用数学归纳法证明:当 n n n刚好是2的幂时,以下递归式的解是 T ( n ) n l o g 2 n T(n)nlog_2n T(n)nlog2n
T ( n ) = { 2 若 n = 2 2 T ( n 2 ) 若 n = 2 k , k > 1 T(n) = \begin{cases} 2 & \text{若}n=2 \\ 2T(\frac{n}{2}) & \text{若}n=2^k,k>1 \end{cases} T(n)={22T(2n)n=2n=2k,k>1


证明:
(1)当 n = 2 1 n=2^1 n=21时, T ( n ) = 2 = 2 l o g 2 2 T(n)=2=2log_22 T(n)=2=2log22。即当 n = 2 1 n=2^1 n=21时, T ( n ) = n l o g 2 n T(n)=nlog_2n T(n)=nlog2n成立。
(2)假设当 n = 2 k n=2^k n=2k时, T ( n ) = n l o g 2 n T(n)=nlog_2n T(n)=nlog2n成立。即 T ( n ) = T ( 2 k ) = 2 k l o g 2 k = 2 k k T(n)=T(2^k)=2^klog_2k=2^kk T(n)=T(2k)=2klog2k=2kk
(3)当 n = 2 k + 1 n=2^k+1 n=2k+1时,
T ( n ) = T ( 2 k + 1 ) T(n)=T(2^k+1) T(n)=T(2k+1)
= 2 T ( 2 k + 1 2 ) + 2 k + 1 =2T(\frac{2^k+1}{2})+2^{k+1} =2T(22k+1)+2k+1
= 2 T ( 2 k ) + 2 k + 1 =2T(2^k)+2^{k+1} =2T(2k)+2k+1
= 2 × 2 k k + 2 k + 1 =2\times2^kk+2^{k+1} =2×2kk+2k+1
= 2 k + 1 k + 2 k + 1 =2^{k+1}k+2^{k+1} =2k+1k+2k+1
= 2 k + 1 ( k + 1 ) =2^{k+1}(k+1) =2k+1(k+1)
= 2 k + 1 l o g 2 2 k + 1 =2^{k+1}log_22^{k+1} =2k+1log22k+1
= n l o g 2 n =nlog_2n =nlog2n
即,当$n=2^{k+1}时,
T ( n ) = n l o g 2 n T(n)=nlog_2n T(n)=nlog2n成立。

综上所述,当 n n n刚好是2的幂时,以下递归式的解是 T ( n ) = n l o g 2 n T(n)=nlog_2n T(n)=nlog2n
T ( n ) = { 2 若 n = 2 2 T ( n 2 ) 若 n = 2 k , k > 1 T(n) = \begin{cases} 2 & \text{若}n=2 \\ 2T(\frac{n}{2}) & \text{若}n=2^k,k>1 \end{cases} T(n)={22T(2n)n=2n=2k,k>1

2.3-4

我们可以把插入排序表示为如下的一个递归过程。为了排序 A [ 1.. n ] A[1 .. n] A[1..n],我们递归地排序 A [ 1.. n − 1 ] A[1 .. n-1] A[1..n1],然后把 A [ n ] A[n] A[n]插入已排序地数组 A [ 1.. n − 1 ] A[1 .. n-1] A[1..n1]。为插入排序的这个递归版本的最坏情况运行时间写一个递归式。


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

2.3-5

回顾查找问题(参见联系2.1-3),注意到,如果序列 A A A已排好序,就可以将该序列的重点与 v v v进行比较。根据比较的结果,原序列中有一半就可以不用再做进一步的考虑了。二分查找算法重复这个过程,每次将序列剩余部分的规模减半。为二分查找写成迭代或递归的伪代码。证明:二分查找的最坏情况运行时间为 Θ ( l o g 2 n ) \Theta(log_2n) Θ(log2n)


答:
迭代伪代码:

BINARY-SEARCH-ITERATION(A, low, high, v)
	while low ≤ high
		middle = (low + high) / 2
		if v < A[middle]
			high= middle - 1
		else if v > A[middle]
			low = middle + 1
		else
			return middle
	return NIL

迭代C#代码:

private static int BinarySearchIteration(int[] array, int low, int high, int v)
{
	while (low <= high)
	{
		int middle = (low + high) / 2;
		if (v < array[middle])
		{
			high = middle - 1;
		}
		else if (v > array[middle])
		{
			low = middle + 1;
		}
		else
		{
			return middle;
		}
	}

	return -1;
}

递归伪代码:

BINARY-SEARCH-RECURSION(A, low, high, v)
	if low > high
		return NIL

	middle = (low+ high) / 2
	if(v < A[middle])
		return BINARY-SEARCH-RECURSION(A, low, middle - 1, v)
	else if(v > A[middle])
		return BINARY-SEARCH-RECURSION(A, middle + 1, high, v)
	else
		return middle

递归C#代码:

private static int BinarySearchRecursin(int[] array, int low, int high, int v)
{
	if(low > high)
	{
		return -1;
	}

	int middle = (low + high) / 2;
	if(v < array[middle])
	{
		return BinarySearchRecursin(array, low, middle - 1, v);
	}
	else if(v > array[middle])
	{
		return BinarySearchRecursin(array, middle + 1, high, v);
	}
	else
	{
		return middle;
	}
}

证明二分查找的最坏情况运行时间为 Θ ( l o g 2 n ) \Theta(log_2n) Θ(log2n)
每次我们将 v v v与中间元素进行比较时,搜索范围都将继续进行。此时,搜索范围减半。故运行时间的递归式为:
T ( n ) = { Θ ( 1 ) 若 n = 1 T ( n ) = T ( n 2 ) + Θ ( 1 ) 若 n > 1 T(n) = \begin{cases} \Theta(1) & \text{若}n=1 \\ T(n) = T(\frac{n}{2}) + \Theta(1) & \text{若}n>1 \end{cases} T(n)={Θ(1)T(n)=T(2n)+Θ(1)n=1n>1
此递归式的解为 Θ ( l o g 2 n ) \Theta(log_2n) Θ(log2n)

2.3-6

注意到2.1节中的过程INSERTION-SORT的第5~7行的while循环采用一种线性查找来(反向)扫描已排好序的子数组 A [ 1.. j − 1 ] A[1 .. j-1] A[1..j1]。我们可以是哟个二分查找(参见联系2.3-5)来把插入排序的最坏情况总运行时间改进到 Θ ( n l o g 2 n ) \Theta(nlog_2n) Θ(nlog2n)吗?


答:
在最坏情况中,待排序的元素是按与要求相反的顺序排布的。插入排序算法INSERTION-SORT,在插入第j个元素的迭代中需要做两个步骤:首先,通过线性查找来(反向)扫描已排好序的子数组 A [ 1.. j − 1 ] A[1 .. j-1] A[1..j1];然后将 A [ 1.. j − 1 ] A[1 .. j-1] A[1..j1]中的元素逐个向后移动一个位置。如果使用二分查找,仅仅是改变了查找过程的运行时间,但未改变移动过程的运行时间。
而移动过程的总运行时间依然是 Θ ( n 2 ) \Theta(n^2) Θ(n2),因此,我们无法使用二分查找来把插入排序的最坏情况总运行时间改进到 Θ ( n l o g 2 n ) \Theta(nlog_2n) Θ(nlog2n)

我们也可以通过计算得出类似的结论。

为了简化问题,我们假设将目标数字与数组中某个下标的元素进行对比的时间为1,而移动一个元素的时间也为1。但是,我们不考虑类似操作下标这种不太重要的时间,也不考虑将目标数组放置到数组的指定位置的时间。

在原来的INSERTION-SORT算法中,对于要插入的第 k k k个元素,在该次迭代中,比较所需要的运行时间为 k − 1 k-1 k1,移动所需要的运行时间也为 k − 1 k-1 k1,所需要的运行时间为 2 ( k − 1 ) 2(k-1) 2(k1)
所以,总运行时间为
∑ k = 1 n ( k − 1 ) + ( k − 1 ) \sum_{k=1}^{n}(k-1)+(k-1) k=1n(k1)+(k1)
= ∑ k = 1 n 2 ( k − 1 ) =\sum_{k=1}^{n}2(k-1) =k=1n2(k1)
= n ( n − 1 ) =n(n-1) =n(n1)

在改进后的INSERTION-SORT算法中,对于要插入的第 k k k个元素,在该次迭代中,比较所需要的运行时间为 l o g 2 k log_2k log2k,移动所需要的运行时间仍然为 k − 1 k-1 k1
所以,总运行时间为
∑ k = 1 n l o g 2 k + ( k − 1 ) \sum_{k=1}^{n}log_2k+(k-1) k=1nlog2k+(k1)
= n ( n − 1 ) 2 + l o g 2 ( n ! ) =\frac{n(n-1)}{2} + log_2(n!) =2n(n1)+log2(n!)

因为 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)的增长速度大于 n l o g 2 n nlog_2n nlog2n,我们无法使用二分查找来把插入排序的最坏情况总运行时间改进到 Θ ( n l o g 2 n ) \Theta(nlog_2n) Θ(nlog2n)

通过下图,我们也可以直观地看出这一情况:
在这里插入图片描述

*2.3-7

描述一个运行时间为 Θ ( n l o g 2 n ) \Theta(nlog_2n) Θ(nlog2n)的算法,给定 n n n个整数的集合 S S S和另一个整数 x x x,该算法能确定 S S S中是否存在两个其和刚好为 x x x的元素。


答:
首先,使用归并排序算法对集合 S S S进行排序,运行时间为 Θ ( n l o g 2 n ) \Theta(nlog_2n) Θ(nlog2n)
其次,对于排序后的集合 S S S中的元素中的每一个元素 s i s_i si i = 1.. n i = 1 .. n i=1..n),使用二分查找算法在 S [ i + 1.. n ] S[i+1 .. n] S[i+1..n]中查找是否有元素 s i ′ s_i^{'} si符合 s i ′ = x − s i s_i^{'}=x-s_i si=xsi,这个过程的运行时间是 Θ ( l o g 2 n ) \Theta(log_2n) Θ(log2n)。如果找到了,就返回该元素的位置,如果没有找到,则继续下一次迭代。

所以,总的运行时间为:
T ( n ) = Θ ( n l o g 2 n ) + n ⋅ Θ ( l o g 2 n ) = Θ ( n l o g 2 n ) T(n) = \Theta(nlog_2n)+n\cdot\Theta(log_2n)=\Theta(nlog_2n) T(n)=Θ(nlog2n)+nΘ(log2n)=Θ(nlog2n)

因此,该算法能确定 S S S中是否存在两个其和刚好为 x x x的元素。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值