文章目录
分治策略
步骤
分解(Divide):将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小。(递归情况(Recursive case))
解决(Conquer):递归地求解子问题。如果子问题规模足够小,则停止递归,直接求解。(基本情况(Base case))
合并(Combine):将子问题的解组合成原问题的解。
递归式(recurrence)
T ( n ) = { Θ ( 1 ) , i f n = 1 a T ( n / b ) + f ( n ) , i f n > 1 (1) T(n)=\begin{cases} \Theta(1),if\space n=1\\ aT(n/b) + f(n), if\space n>1 \tag{1} \end{cases} T(n)={Θ(1),if n=1aT(n/b)+f(n),if n>1(1)
三种求解递归式的方法
-
代入法:我们猜测一个界,然后用数学归纳法证明这个界是正确的。
-
递归树法:将递归式转换成一棵树,其结点表示不同层次的递归调用产生的代价。然后采用边界和技术求解递归式。
-
主方法:可求解形如(1)的递归式的界
其中, a ≥ 1 , b > 1 , f ( n ) a \geq 1,b > 1,f(n) a≥1,b>1,f(n)是一个给定的函数。这种形式的递归式很常见,它刻画了这样一个分治算法:生成 a a a个子问题,每个子问题的规模是原问题规模的 1 / b 1/b 1/b,分解和合并共花费的时间为 f ( n ) f(n) f(n)。
最大子数组问题
暴力求解算法
算法思想
只要确定头和尾即可 n n n 天共有 ( n 2 ) \begin{pmatrix} n \\ 2\end{pmatrix} (n2)种日期组合。因为 ( n 2 ) = Θ ( n 2 ) \begin{pmatrix} n \\ 2\end{pmatrix}=\Theta(n^2) (n2)=Θ(n2),所以这种方法运行的时间为 Θ ( n 2 ) \Theta (n^2) Θ(n2)
伪代码:
BruteForceMethod(A)
maxVal = Integer.MIN_VALUE
for i = 0 to A.length - 1
sum = 0
for j = i to A.length - 1
sum += A[i]
if sum > maxVal
maxVal = sum
分治算法
算法思想
假设我们要寻找子数组 A [ l o w ] , … , A [ h i g h ] A[low], \dots ,A[high] A[low],…,A[high]的最大子数组。将子数组划分为两个规模相等的子数组,然后只要求解子数组 A [ l o w ] , … , A [ m i d ] A[low],\dots,A[mid] A[low],…,A[mid]和 A [ m i d + 1 ] , … , A [ h i g h ] A[mid+1],\dots,A[high] A[mid+1],…,A[high]的最大子数组。 A [ l o w ] , … , A [ h i g h ] A[low], \dots ,A[high] A[low],…,A[high]的最大子数组 A [ i ] , … , A [ j ] A[i],\dots,A[j] A[i],…,A[j]是以下三种情况之一:
- 完全位于子数组 A [ l o w ] , … , A [ m i d ] A[low],\dots,A[mid] A[low],…,A[mid]中
- 完全位于子数组 A [ m i d + 1 ] , … , A [ h i g h ] A[mid+1],\dots,A[high] A[mid+1],…,A[high]中
- 跨越了中点 m i d mid mid
伪代码
寻找跨越中点的子数组
findMaxCrossingSubArray(A, low, mid, high)
left-sum = Integer.MIN_VALUE
sum = 0
for i = mid - 1 downto low
sum += A[i]
if sum > left-sum
left-sum = sum
max-left = i
right-sum = Integer.MIN_VALUE
sum = 0
for j = mid to high - 1
sum += A[j]
if sum > right-sum
right-sum = sum
max-right = j
return (max-left, max-right, left-sum + right-sum)
分解
findMaxSubArray(A, low, high)
if low == high
return (low, high, A[low])
mid = (low + high) / 2
(left-low, left-high, left-sum) = findMaxSubArray(A, low, mid)
(right-low, right-high, right-sum) = findMaxSubArray(A, mid, high)
(cross-low, cross-high, cross-sum) = findMAxCrossingSubArray(A, low, mid, high)
if left-sum >= right-sum and left-sum >= cross-sum
return (left-low, left-high, left-sum)
if right-sum >= left-sum and right-sum >= cross-sum
return (right-low, right-high, right-sum)
return (cross-low, cross-high, cross-sum)
分析
递归式
T ( n ) = { Θ ( 1 ) 若 n = 1 2 T ( n / 2 ) + Θ ( n ) 若 n > 1 (2) T(n)=\begin{cases} \Theta (1) 若n=1\\ 2T(n/2)+\Theta(n)若n>1 \end{cases} \tag{2} T(n)={Θ(1)若n=12T(n/2)+Θ(n)若n>1(2)
得到其时间复杂度为 T ( n ) = Θ ( n l o g n ) T(n)=\Theta(nlogn) T(n)=Θ(nlogn)
用代入法求解递归式
代入法求解步骤
- 猜测解的形式。
- 用数学归纳法求解解中的常数,并证明解是正确的。
例如,我们要确定下面递归式的上界:
T
(
n
)
=
2
T
(
⌊
n
/
2
⌋
)
+
n
T(n)=2T(\lfloor n/2 \rfloor)+n
T(n)=2T(⌊n/2⌋)+n
猜测其解为:
T
(
n
)
=
O
(
n
l
o
g
n
)
T(n)=O(nlogn)
T(n)=O(nlogn)。代入法要求证明恰当选择常数
c
>
0
c>0
c>0,有
T
(
n
)
≤
c
n
l
o
g
n
T(n) \leq cnlogn
T(n)≤cnlogn。
所以,
T
(
⌊
n
/
2
⌋
)
≤
c
⌊
n
/
2
⌋
l
g
(
⌊
n
/
2
⌋
)
T(\lfloor n/2 \rfloor) \leq c\lfloor n/2 \rfloor lg(\lfloor n/2 \rfloor)
T(⌊n/2⌋)≤c⌊n/2⌋lg(⌊n/2⌋),将其代入递归式,得到
T
(
n
)
≤
2
(
c
⌊
n
/
2
⌋
l
g
(
⌊
n
/
2
⌋
)
)
+
n
≤
c
n
l
g
(
n
/
2
)
+
n
=
c
n
l
g
n
−
c
n
l
g
2
+
n
=
c
n
l
g
n
−
c
n
+
n
≤
c
n
l
g
n
T(n) \leq 2(c\lfloor n/2 \rfloor lg(\lfloor n/2 \rfloor))+n\\ \leq cnlg(n/2)+n \\ =cnlgn-cnlg2+n \\ =cnlgn-cn+n\\ \leq cnlgn
T(n)≤2(c⌊n/2⌋lg(⌊n/2⌋))+n≤cnlg(n/2)+n=cnlgn−cnlg2+n=cnlgn−cn+n≤cnlgn
只要,
c
≥
1
c\geq 1
c≥1,最后一步都会成立。
做出好的猜测
-
遗憾的是,不存在通用的方法来猜测递归式正确的解。猜测要靠经验,偶尔还要创造力。
-
另一种做出好的猜测的方法是先证明递归式较松的上界和下界,然后缩小不确定的范围。两边夹定理
微妙的细节
-
有时候你可能正确的才出了递归式解的渐进界,但莫名其妙地在归纳证明时失败了,此时,如果修改猜测,减去一个低阶项,证明往往可以顺利进行。
-
我们在归纳证明时一定要显式地证明与假设时的严格一致的形式。如当要证明: T ( n ) = O ( n ) T(n)=O(n) T(n)=O(n)时,需要显式证明 T ( n ) ≤ c n T(n) \leq cn T(n)≤cn。
改变变量(换元)
例如,
T
(
n
)
=
2
T
(
n
)
+
lg
n
T(n)=2T(\sqrt {n})+\lg n
T(n)=2T(n)+lgn
看起来很困难,但是,如果令
m
=
lg
n
m=\lg n
m=lgn,那么有
T
(
2
m
)
=
2
T
(
2
m
/
2
)
+
m
T(2^m)=2T(2^{m/2})+m
T(2m)=2T(2m/2)+m
再令
S
(
m
)
=
T
(
2
m
)
S(m)=T(2^m)
S(m)=T(2m),则
S
(
m
)
=
2
S
(
m
/
2
)
+
m
S(m)=2S(m/2)+m
S(m)=2S(m/2)+m
变成了我们熟知的递归式,其时间复杂度为
S
(
m
)
=
O
(
m
log
m
)
S(m)=O(m\log m)
S(m)=O(mlogm)
于是
T
(
n
)
=
T
(
2
m
)
=
S
(
m
)
=
O
(
m
log
m
)
=
O
(
lg
n
lg
lg
n
)
T(n)=T(2^m)=S(m)=O(m\log m)=O(\lg n \lg \lg n)
T(n)=T(2m)=S(m)=O(mlogm)=O(lgnlglgn)
用递归树求解递归式
例如,对于以下递归式
T
(
n
)
=
2
T
(
n
/
2
)
+
n
T(n)=2T(n/2)+n
T(n)=2T(n/2)+n
对其求和,
T
(
n
)
=
c
n
+
c
n
+
⋯
+
c
n
⏞
log
2
n
=
c
n
log
2
n
=
O
(
n
log
2
n
)
T(n)=\overbrace {cn+cn+\cdots+cn}^{\log_2 n}=cn\log_2n=O(n\log_2 n)
T(n)=cn+cn+⋯+cn
log2n=cnlog2n=O(nlog2n)
用主方法求解递归式
考虑如下形式的递归式:
T ( n ) = a T ( n / b ) + f ( n ) 其 中 , a ≥ 1 , b > 1 T(n)=aT(n/b)+f(n)\\ 其中,a\geq 1,b>1 T(n)=aT(n/b)+f(n)其中,a≥1,b>1
主定理
**令
a
≥
1
a\geq 1
a≥1和
b
>
1
b>1
b>1,是常数,
f
(
n
)
f(n)
f(n)是一个函数,
T
(
n
)
T(n)
T(n)**是定义在非负整数上的递归式:
T
(
n
)
=
a
T
(
n
/
b
)
+
f
(
n
)
T(n)=aT(n/b)+f(n)
T(n)=aT(n/b)+f(n)
其中我们将n/b解释为 ⌊ n / b ⌋ \lfloor n/b \rfloor ⌊n/b⌋或 ⌈ n / b ⌉ \lceil n/b \rceil ⌈n/b⌉.那么 T ( n ) T(n) T(n)有如下渐进界:
- 若对某个常数 ϵ > 0 \epsilon > 0 ϵ>0有 f ( n ) = O ( n log b a − ϵ ) f(n)=O(n^{\log_b {a-\epsilon}}) f(n)=O(nlogba−ϵ),则 T ( n ) = Θ ( n log b a ) T(n)=\Theta(n^{\log_b a}) T(n)=Θ(nlogba)
- 若 f ( n ) = Θ ( n log b a ) f(n)=\Theta(n^{\log_b a}) f(n)=Θ(nlogba),则 T ( n ) = Θ ( n log b a lg n ) T(n)=\Theta(n^{\log_b a} \lg n) T(n)=Θ(nlogbalgn)
- 若对某个常数 ϵ > 0 \epsilon > 0 ϵ>0有 f ( n ) = Ω ( n log b a + ϵ ) f(n)=\Omega(n^{\log_b {a+\epsilon}}) f(n)=Ω(nlogba+ϵ),且对某个常数 c < 1 c<1 c<1和所有足够大的 n n n有 a f ( n / b ) ≤ c f ( n ) af(n/b)\leq cf(n) af(n/b)≤cf(n),则 T ( n ) = Θ ( f ( n ) ) T(n)=\Theta (f(n)) T(n)=Θ(f(n))
使用主方法
-
例1
T ( n ) = 9 T ( n / 3 ) + n T(n)=9T(n/3)+n T(n)=9T(n/3)+n
对于这个递归式, a = 9 , b = 3 , f ( n ) = n a=9, b=3,f(n)=n a=9,b=3,f(n)=n,因此 n log b a = n log 3 9 = Θ ( n 2 ) n^{\log_b a}=n^{\log_3 9}=\Theta(n^2) nlogba=nlog39=Θ(n2)。由于 f ( n ) = O ( n log 3 9 − ϵ ) f(n)=O(n^{\log_3 {9-\epsilon}}) f(n)=O(nlog39−ϵ),其中, ϵ = 1 \epsilon = 1 ϵ=1,由情况1,从而得到解 T ( n ) = Θ ( n 2 ) T(n)=\Theta(n^2) T(n)=Θ(n2) -
例2
T ( n ) = 3 T ( n / 4 ) + n lg n T(n)=3T(n/4)+n\lg n T(n)=3T(n/4)+nlgn
对于这个递归式, a = 3 , b = 4 , f ( n ) = n lg n a=3,b=4,f(n)=n\lg n a=3,b=4,f(n)=nlgn,因此 n log b a = n log 4 3 = O ( n 0.793 ) n^{\log_b a}=n^{\log_4 3}=O(n^{0.793}) nlogba=nlog43=O(n0.793),由于 f ( n ) = Ω ( n log 4 3 + ϵ ) f(n)=\Omega(n^{\log_4 {3+\epsilon}}) f(n)=Ω(nlog43+ϵ),其中, ϵ ≈ 0.2 \epsilon \approx 0.2 ϵ≈0.2.当n足够大时,对于 c = 3 / 4 , a f ( n / b ) = 3 ( n / 4 ) lg ( n / 4 ) ≤ ( 3 / 4 ) n lg n = c f ( n ) c=3/4,af(n/b)=3(n/4)\lg(n/4) \leq (3/4)n\lg n=cf(n) c=3/4,af(n/b)=3(n/4)lg(n/4)≤(3/4)nlgn=cf(n)。因此满足正则条件,根据情况3,得到解 T ( n ) = Θ ( n lg n ) T(n)=\Theta(n\lg n) T(n)=Θ(nlgn) -
例3
T ( n ) = T ( 2 n / 3 ) + 1 T(n)=T(2n/3)+1 T(n)=T(2n/3)+1
对于这个递归式, a = 1 , b = 2 / 3 , f ( n ) = 1 a=1,b=2/3,f(n)=1 a=1,b=2/3,f(n)=1,因此 n log b a = n l o g 3 / 21 = n 0 = 1 n^{\log_b a}=n^{log_3/2{1}}=n^0=1 nlogba=nlog3/21=n0=1,由于 f ( n ) = Θ ( n log b a ) = Θ ( 1 ) f(n)=\Theta(n^{\log_b a})=\Theta(1) f(n)=Θ(nlogba)=Θ(1),因此由情况2,得到解为: T ( n ) = Θ ( lg n ) T(n)=\Theta(\lg n) T(n)=Θ(lgn)
)n\lg n=cf(n) 。 因 此 满 足 正 则 条 件 , 根 据 情 况 3 , 得 到 解 。因此满足正则条件,根据情况3,得到解 。因此满足正则条件,根据情况3,得到解T(n)=\Theta(n\lg n)$
- 例3
T ( n ) = T ( 2 n / 3 ) + 1 T(n)=T(2n/3)+1 T(n)=T(2n/3)+1
对于这个递归式, a = 1 , b = 2 / 3 , f ( n ) = 1 a=1,b=2/3,f(n)=1 a=1,b=2/3,f(n)=1,因此 n log b a = n l o g 3 / 21 = n 0 = 1 n^{\log_b a}=n^{log_3/2{1}}=n^0=1 nlogba=nlog3/21=n0=1,由于 f ( n ) = Θ ( n log b a ) = Θ ( 1 ) f(n)=\Theta(n^{\log_b a})=\Theta(1) f(n)=Θ(nlogba)=Θ(1),因此由情况2,得到解为: T ( n ) = Θ ( lg n ) T(n)=\Theta(\lg n) T(n)=Θ(lgn)