算法 Hw2

Ⅰ Analysis


1.

(a)

length[A] 记为 n n n

  1. 对于内循环的正确性:
  • 循环不变式:内循环每次迭代开始,子数组 A[j ... n] 中元素 A[j] 是最小的,并且其元素与迭代前保持一致。

  • 初始化: 第一次循环迭代前(即 j = n j = n j=n),那么循环不变式中只有一个元素,同时也为是最小元素,故循环不变式成立。

  • 保持: 假设迭代之前,有循环不变式 A[j ... n] 为真。下一次进入循环体会对 A[j]A[j−1] 的大小进行比较,并决定是否交换使 j−1 位置为 min{ A[j], A[j−1] } 。因为 A[j]A[j ... n] 中的最小元素,所以可以得出 A[j−1]A[j−1 ... n] 中最小的元素。此时子数组 A[j−1 ... n] 中的元素并未发生改变。所以,此次循环迭代增加元素 A[j−1] 保持了循环不变式。

  • 终止: 循环终止时,子数组 A[i ... n] 由原来元素组成,且 A[i] 为其中最小元素。

  1. 对于外循环的正确性:
  • 循环不变式:外循环每次迭代开始,子数组 A[1 ... i−1] 构成升序数组,且剩余子数组 A[i ... n] 中的元素均大于等于 A[i−1]

  • 初始化: 第一次循环迭代前(即 n = 1 n = 1 n=1),此时数组 A[1 ... i-1] 为空数组,故循环不变式成立。

  • 保持: 假设迭代之前,有循环不变式为真。下一次进入循环体,由上面已证明的内循环正确性可知,A[i ... n] 的元素构成并未改变,且 A[i] 为最小元素。所以 A[1 ... i] 符合升序排列,且 子数组 A[i+1 ... n] 中的元素均大于等于 A[i]

  • 终止: 循环终止时,子数组 A[1 ... n−1] 构成升序数组,且 A[n] 大于 A[1 ... n−1] 中的任意元素,所以 A[1 ... n] 按照升序排列,且元素与循环前保持一致。

综上所述,可证明冒泡排序终止后后,元素保持一致且有序,即证明算法的正确性。


(b)

冒泡排序插入排序
最好情况 Θ ( n 2 ) \Theta(n^2) Θ(n2) Θ ( n ) \Theta(n) Θ(n)
最坏情况 Θ ( n 2 ) \Theta(n^2) Θ(n2) Θ ( n 2 ) \Theta(n^2) Θ(n2)
  • 对于冒泡排序,无论何种情况,每步都会进行比较,将循环进行到底。任何情况下,总循环次数不变,比较次数未改变,所以时间开销均为 Θ ( n 2 ) \Theta(n^2) Θ(n2)
  • 对于插入排序,最坏情况下(即逆序排列),与冒泡排序一致,为 Θ ( n 2 ) \Theta(n^2) Θ(n2)。而最好情况下,插入排序只需进行 n − 1 n-1 n1 次比较,所以时间开销为 Θ ( n ) \Theta(n) Θ(n),优于冒泡排序。

2.

首先,将数组填充至最近的 2k - 1,记作 n' ,即:若 n = 13 n = 13 n=13, 则将数字 14 14 14 15 15 15填充入数组。这样做的原因是,对于任意同一位的bit,从 1 1 1 一直加到 n',其和必定为2k-1

例如:当 n = 6 n = 6 n=6 时,将数字 7 7 7 填充入数组,此时 n ′ = 13 n' = 13 n=13 k = 3 k = 3 k=3, 每一位的和为 2 k − 1 = 4 2^{k-1} = 4 2k1=4

第3位第2位第1位
1001
2010
3011
4100
5101
6110
7111
求和444
  • 只有当 n = 1 n = 1 n=1 时, k = 1 k = 1 k=1 2 k − 1 2^{k-1} 2k1 为奇数,此时数组中有 0 0 0 ,则缺少 1 1 1;有 1 1 1 ,则缺少 0 0 0
  • 而其余情况, 2 k − 1 2^{k-1} 2k1 均为偶数,所以寻找 缺失数字k 的方法就是,将同一位的bit值相加,其和若为偶数,则 k 的该位为 0 0 0;和为奇数,则为 1 1 1

这样的话,只需要将每个数的32位,通过 fetch(A, i, j) 函数提取并计算,最终找到缺失数字,这种方法的时间开销为 Θ ( n ) \Theta(n) Θ(n)


Ⅱ Median Selection


1.

  • 先找到最小值 k
    通过 淘汰赛 的方式寻找:两两一组比较一次,确定二者中的更小值,对于这 n 2 \frac{n}{2} 2n 个数再次两两一组比较,不断循环,最终找到 胜者,即最小值。
    找到最小值总共经过了 n − 1 n-1 n1 次比较。

  • 再找次小值
    考虑最小值 k:在以上 n-1 次比较中,k 一共参与了 ⌈ log ⁡ n ⌉ \lceil\log n\rceil logn比赛,有 ⌈ log ⁡ n ⌉ \lceil\log n\rceil logn 个元素 亲自输给k,也就意味着次小值在这 ⌈ log ⁡ n ⌉ \lceil\log n\rceil logn 个元素中。
    于是,剩下的问题转变为,找到 ⌈ log ⁡ n ⌉ \lceil\log n\rceil logn 个元素中的最小值,即可用上述找最小值的方法同理解决问题。
    找到次小值再经过了 ⌈ log ⁡ n ⌉ − 1 \lceil\log n\rceil-1 logn1 次比较。

最终,总共的比较次数只需将两者相加,为 n + ⌈ log ⁡ n ⌉ − 2 n+\lceil\log n\rceil-2 n+logn2 次比较。


2.

对于数组为 A[1 ... n],通过中位数子程序找到其中位数 A[m]
如果要寻找的顺序统计量k符合:

  • k = m k = m k=m ,则返回 A[m]
  • k ≠ m k \neq m k=m ,则将数组换分为 A[1 ... m-1]A[m+1 ... n] 两个子数组。 k < m k < m k<m 就到 A[1 ... m-1] 继续进行以上步骤; k > m k > m k>m 就到 A[m+1 ... n] 中,并将 k <-- m-k
    不断递归,直至最终寻找到。

以这种方法, T ( n ) ≤ O ( n ) T(n) \le O(n) T(n)O(n),符合题目要求。


3.

由上题已知:找到数组的第 i 小的数是 O ( n ) O(n) O(n)

  • 先找到中位数 m(线性时间选择算法 --> O ( n ) O(n) O(n)
  • 然后通过数组 D[1 ... n-1],记录处中位数以外,数组S中每个元素到中位数距离的绝对值。(遍历数组 --> O ( n ) O(n) O(n)
  • 题目中要求的:最接近中位数的k个元素,即为D中最小的k个元素。
  • 再找到数组D中第k小的元素 D[mk](线性时间选择算法 --> O ( n ) O(n) O(n)
  • 最后再遍历数组D,找到其中k-1个小于 D[mk]的元素。(遍历数组 --> O ( n ) O(n) O(n)

综上所述,该算法时间复杂度为O(n)。


Ⅲ Interval Tree


1.

INTERVAL(T, i)
	n = T.root
	result = T.nil
 	while n != T.nil
 		if i overlap n.int
 			if(result == T.nil || result.low < n.low)
 				result = n
		if n.left != T.nil and n.left.max >= i.low
			n = n.left
		else n = n.right
	return result
  • 初始化变量 n 为区间树 T 的根节点,n 用于遍历区间树的节点,将结果变量 result 设置为 T.nilresult 用于记录找到的具有最小低端点的重叠区间。
  • 先检查区间 i 是否与节点 n 所表示的区间重叠。若重叠,则继续判断:如果 resultT.nil 或者 result 的低端点比 n 的低端点小,那么更新 result 为节点 n。从而找到具有最小低端点的重叠区间。
  • 之后判断:如果节点 n 的左子节点不是 T.nil,并且节点 n 的左子节点的最大高端点大于等于区间 i 的低端点,那么说明左子树中的某些区间可能与 i 重叠。因此,将 n 更新为其左子节点,继续向左子树搜索。
  • 若不满足上述条件,则说明左子树中的所有区间都不与 i 重叠,那么就向右子树搜索。将 n 更新为其右子节点,继续向右子树搜索。

当循环结束后,返回结果 result:包含与区间 i 重叠且具有最小低端点的区间。


2.

Node[] result;
INTERVAL(T, i)
	n = T.root
	result = T.nil;
 	while n != T.nil
 		if i overlap n.int
 			result.add(n)
		if n.left != T.nil and n.left.max >= i.int.low
			m = n.successor
			if(m != T.Nil && m.int.low <= i.int.high)
 				i' = (m.int.low, i.int.high)
 				INTERVAL(T, i')
			n = n.left
		else n = n.right
  • 先检查区间 i 是否与节点 n 所表示的区间重叠。若重叠,则将节点 n 添加到 result 中。

  • 如果节点 n 的左子节点不是 T.nil,并且节点 n 的左子节点的最大高端点大于等于区间 i 的低端点,那么说明左子树中的某些区间可能与 i 重叠。因此对节点 n 的后继节点 m 进行检查,并且 m 的低端点不能大于区间 i 的高端点,否则它们不会重叠。若成立,构造新的区间 i',其低端点为 m 的低端点,高端点为 i 的高端点,然后递归调用 INTERVAL(T, i') 进一步搜索。

  • 再继续向右子树搜索。

  • 最后得到结果。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值