慢慢更新。
第二章
2-8
确定划线语句执行次数、计算渐近时间复杂度
i=1; x=0;
do{
x++; i=2*i;
} while i<n;
答案: 设语句执行了k次, 2 k ≥ n 2^k \ge n 2k≥n 时退出循环,因此划线语句执行了 ⌈ l o g 2 n ⌉ \lceil log_2 n \rceil ⌈log2n⌉ 次。渐进时间复杂度 O ( l o g 2 n ) O(log_2 n) O(log2n) .
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
for (int k=1;k<=j;k++)
x++;
∑ i = 1 n ∑ j = 1 i ∑ k = 1 j 1 = ∑ i = 1 n ∑ j = 1 i j = ∑ i = 1 n i ( i + 1 ) 2 = 1 2 [ ∑ i = 1 n i + ∑ i = 1 n i 2 ] = n ( n + 1 ) ( n + 2 ) 6 \begin{align} \sum_{i=1}^n\sum_{j=1}^i\sum_{k=1}^j1=\sum_{i=1}^n\sum_{j=1}^ij&=\sum_{i=1}^n\frac{\mathrm{i}(i+1)}{2} \\&=\frac{1}{2}[\sum_{i=1}^n i+\sum_{i=1}^n i^2] \\&=\frac{n(n+1)(n+2)}{6} \end{align} i=1∑nj=1∑ik=1∑j1=i=1∑nj=1∑ij=i=1∑n2i(i+1)=21[i=1∑ni+i=1∑ni2]=6n(n+1)(n+2)
因为 ∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{i=1}^{n}i^{2}={\frac{n(n+1)(2n+1)}{6}} ∑i=1ni2=6n(n+1)(2n+1)
x=n; y=0;
while(x>=(y+1)(y+1)) y++;
答案: 解:当
(
y
+
1
)
2
≤
n
(y+1)^2\leq n
(y+1)2≤n 时执行划线语句,即
:
y
≤
⌊
n
⌋
−
1
:y\leq\left\lfloor\sqrt{n}\right\rfloor-1
:y≤⌊n⌋−1 时执行。
又因为 y 从0开始,因此0~
⌊
n
⌋
−
1
\lfloor\sqrt{\mathrm{n}}\rfloor_{-1}
⌊n⌋−1 共执行
⌊
n
⌋
\lfloor\sqrt{\mathrm{n}}\rfloor
⌊n⌋ 次。
渐近时间复杂度为: O ( n ) O(\sqrt{n}) O(n)。
m=0;
for (int i=1;i<n;i++)
for (int j=2*i;j<=n;j++)
m++;
答案:
∑
i
=
0
⌊
n
2
⌋
∑
2
i
n
1
=
∑
i
=
0
⌊
n
2
⌋
(
n
−
2
i
+
1
)
\sum_{i=0}^{\lfloor \frac{n}{2}\rfloor}\sum_{2i}^{n}1=\sum_{i=0}^{\lfloor \frac{n}{2}\rfloor}(n-2i+1)
i=0∑⌊2n⌋2i∑n1=i=0∑⌊2n⌋(n−2i+1)
【为什么是
⌊
n
2
⌋
\lfloor \frac{n}{2}\rfloor
⌊2n⌋?当外部循环运行k次时,划线语句能运行(n+1-2k)次,而划线语句只能执行减少到一次时,下一次循环将终止,此时 n+1-2k=1,有
k
=
⌊
n
2
⌋
k=\lfloor \frac{n}{2}\rfloor
k=⌊2n⌋】
∑
i
=
0
⌊
n
2
⌋
(
n
−
2
i
+
1
)
=
(n+1)
(
⌊
n
2
⌋
+
1
)
−
2
⌊
n
2
⌋
(
⌊
n
2
⌋
+
1
)
2
=
(
⌊
n
2
⌋
+
1
)
(
⌊
n
2
⌋
+
1
)
=
{
n是偶数:
(
n
2
+
1
)
2
n
是奇数:
(
n
+
3
2
)
(
n
+
1
2
)
\begin{aligned}&\sum_{i=0}^{\lfloor \frac{n}{2}\rfloor}(n-2i+1)\:=\text{(n+1)}(\lfloor \frac{n}{2}\rfloor+1)-2\frac{\lfloor \frac{n}{2}\rfloor(\lfloor \frac{n}{2}\rfloor+1)}{2}\\&=(\lfloor \frac{n}{2}\rfloor+1)(\lfloor \frac{n}{2}\rfloor+1)=\begin{cases}\text{n是偶数:}(\frac{n}{2}+1)^2\\\\n\text{是奇数:}(\frac{n+3}2)(\frac{n+1}2)\end{cases}\end{aligned}
i=0∑⌊2n⌋(n−2i+1)=(n+1)(⌊2n⌋+1)−22⌊2n⌋(⌊2n⌋+1)=(⌊2n⌋+1)(⌊2n⌋+1)=⎩
⎨
⎧n是偶数:(2n+1)2n是奇数:(2n+3)(2n+1)
渐进时间复杂度为:
O
(
n
2
)
O(n^2)
O(n2)
2-13
设
f
1
(
n
)
=
O
(
g
1
(
n
)
)
,
f
2
(
n
)
=
O
(
g
2
(
n
)
)
f_1(n)=O(g_1(n)),\quad f_2(n)=O(g_2(n))
f1(n)=O(g1(n)),f2(n)=O(g2(n)), 证明下列结论成立。
(1)
f
1
(
n
)
+
f
2
(
n
)
=
O
(
max
{
g
1
(
n
)
,
g
2
(
n
)
}
)
f_1(n)+f_2(n)=O(\max\{g_1(n),\quad g_2(n)\})
f1(n)+f2(n)=O(max{g1(n),g2(n)})
(2) f 1 ( n ) + f 2 ( n ) = O ( g 1 ( n ) − g 2 ( n ) ) f_1(n)+f_2(n)=O(g_1(n)-g_2(n)) f1(n)+f2(n)=O(g1(n)−g2(n))
答案:(1)解:因为 f 1 ( n ) ∈ O ( g 1 ( n ) ) f_1(n)\in O(g_1(n)) f1(n)∈O(g1(n)),所以存在正常数 c 1 c_1 c1和自然数 n 1 n_1 n1,当 n ≥ n 1 n\ge n_1 n≥n1,有 f 1 ( n ) ≤ c 1 g 1 ( n ) f _1(n)\leq c_1 g_1(n) f1(n)≤c1g1(n)。
因为
f
2
(
n
)
∈
O
(
g
2
(
n
)
)
f_2(n)\in O(g_2(n))
f2(n)∈O(g2(n)),所以存在正常数
c
2
c_2
c2和自然数
n
2
n_2
n2,当
n
≥
n
2
n\ge n_2
n≥n2,有
f
2
(
n
)
≤
c
2
g
2
(
n
)
f _2(n)\leq c_2 g_2(n)
f2(n)≤c2g2(n)。
取
c
3
=
max
{
c
1
,
c
2
}
,
n
3
=
max
{
n
1
,
n
2
}
c_3=\max\{c_1,c_2\},n_3=\max\{n_1,n_2\}
c3=max{c1,c2},n3=max{n1,n2},
则当
n
≥
n
0
n\geq n_0
n≥n0时,有
f
1
(
n
)
+
f
2
(
n
)
≤
c
1
g
1
(
n
)
+
c
2
g
2
(
n
)
≤
c
3
g
1
(
n
)
+
c
3
g
2
(
n
)
=
c
3
(
g
1
(
n
)
+
g
2
(
n
)
)
≤
2
c
3
max
{
g
1
(
n
)
+
g
2
(
n
)
}
=
O
(
max
{
g
1
(
n
)
+
g
2
(
n
)
}
)
\begin{align} f_1(n)+f_2(n)&\leq c_1g_1(n)+c_2g_2(n) \\&\leq c_3g_1(n)+c_3g_2(n)=c_3(g_{1}(n)+g_{2}(n)) \\&\leq 2c_3 \max\left \{ g_{1}(n)+g_{2}(n) \right \} \\&=O(\max\left \{ g_{1}(n)+g_{2}(n) \right \}) \end{align}
f1(n)+f2(n)≤c1g1(n)+c2g2(n)≤c3g1(n)+c3g2(n)=c3(g1(n)+g2(n))≤2c3max{g1(n)+g2(n)}=O(max{g1(n)+g2(n)})
(2)解:因为
f
1
(
n
)
=
O
(
g
1
(
n
)
)
f_1(n)=O(g_1(n))
f1(n)=O(g1(n)),所以存在正常数
c
1
c_1
c1和自然数
n
1
n_1
n1,当
n
≥
n
1
n\ge n_1
n≥n1,有
f
1
(
n
)
≤
c
1
g
1
(
n
)
f _1(n)\leq c_1 g_1(n)
f1(n)≤c1g1(n)。
因为
f
2
(
n
)
=
O
(
g
2
(
n
)
)
f_2(n)=O(g_2(n))
f2(n)=O(g2(n)),所以存在正常数
c
2
c_2
c2和自然数
n
2
n_2
n2,当
n
≥
n
2
n\ge n_2
n≥n2,有
f
2
(
n
)
≤
c
2
g
2
(
n
)
f _2(n)\leq c_2 g_2(n)
f2(n)≤c2g2(n)。
取
c
=
max
{
c
1
,
c
2
}
,
n
=
max
{
n
1
,
n
2
}
c=\max\{c_1,c_2\},n=\max\{n_1,n_2\}
c=max{c1,c2},n=max{n1,n2},
则当
n
≥
n
0
n\geq n_0
n≥n0时,有
f
1
(
n
)
+
f
2
(
n
)
≤
c
1
g
1
(
n
)
+
c
2
g
2
(
n
)
≤
c
g
1
(
n
)
+
c
g
2
(
n
)
=
c
(
g
1
(
n
)
+
g
2
(
n
)
)
=
O
(
g
1
(
n
)
+
g
2
(
n
)
)
\begin{align} f_1(n)+f_2(n)&\leq c_1g_1(n)+c_2g_2(n)\\&\leq cg_1(n)+cg_2(n)=c(g_{1}(n)+g_{2}(n))\\&=O(g_{1}(n)+g_{2}(n)) \end{align}
f1(n)+f2(n)≤c1g1(n)+c2g2(n)≤cg1(n)+cg2(n)=c(g1(n)+g2(n))=O(g1(n)+g2(n))
5-8
5-8三分搜索算法的做法是: 它先将待查元素x与n/3处的元素比较,然后将x与2n/3处的元素进行比较。比较的结果或者找到x,或者将搜索范围缩小为原来的n/3。
(1) 编写c++程序实现算法;
(2) 分析算法的时间复杂度。
答:(1)
#include <iostream>
#include <cmath>
#define MAX 50
using namespace std;
int l[MAX];
int search(int left, int right, int x);
int main(){
int i,n,x;
cout<<"Enter the number of elements in the list: ";
cin>>n;
cout<<"Enter the elements of the list: ";
for (i=0;i<n;i++)
cin>>l[i];
cout<<"Enter the element to be searched: ";
cin>>x;
i=search(0,n-1,x);
if (i==-1)
cout<<"Element not found"<<endl;
else
cout<<"Element found at position "<<i+1<<endl;
cout << "Press any key to exit...";
cin.ignore();
cin.get();
return 0;
}
int search(int left, int right, int x){
int m1,m2;
if (left==right) {
if (l[left]=-x)
return left;
}
if (left<right){
m1=left+(right-left)/3;
m2=(int)ceil((double)(right-(right-left)/3));
if (x==l[m1]) return m1;
else if (x<l[m1]) return search(left,m1-1,x);
else if (x<l[m2]) return search(m1+1,m2-1,x);
else if (x==l[m2]) return m2;
else return search(m2+1,right,x);
}
else return -1;
}
运行结果:
(2)该算法是有三个分支的尾递归函数,最终只会进入到三种可能范围的其中一种中进行递归。
递推关系式是: T ( n ) = T ( 3 / n ) + 2 T(n)=T(3/n)+2 T(n)=T(3/n)+2。
定理5-1:a,b,c和k为常数,
T
(
n
)
=
a
T
(
n
/
b
)
+
c
n
k
T(n)=aT(n/b)+cn^k
T(n)=aT(n/b)+cnk,T(1)=c,则
T
(
n
)
=
{
Θ
(
n
log
b
a
)
如果a
≥
b
k
Θ
(
n
k
log
n
)
如果a
=
b
k
Θ
(
n
k
)
如果a<b k
T(n)=\begin{cases}\Theta({n^{\log_ba}})&\text{如果a}\geq{b^k}\\\Theta({n^k}\log{n})&\text{如果a}={b^k}&\\\Theta({n^k})&\text{如果a<b k}&\end{cases}
T(n)=⎩
⎨
⎧Θ(nlogba)Θ(nklogn)Θ(nk)如果a≥bk如果a=bk如果a<b k
由该定理可得:
T
(
n
)
=
Θ
(
log
n
)
T(n)=\Theta(\log{n})
T(n)=Θ(logn) 。
5-9
设有对半搜索算法如下:
ResultCode SortableList<T>::Search2( T& x)const
{
int m, left-0, right-n;
while (left<right-1){
m=(left+right)/2;
if(x<l[m]) right-m; else left=m;
}
if(x=l[left]){
x=l[left]; return Success;
}
return NotPresent:
}
(1)画出 n=8 的二叉判定树。
(2)证明这一算法的最好、平均和最坏情况的时间复杂度均为 Θ ( log n ) \Theta(\log{n}) Θ(logn) 。
答:(1)
(2)因为该算法总是先经过while循环后再进行条件判断:即使是最好情况下,要寻找的元素恰好在中间,该算法依然要进入while循环。
所以 T ( n ) = T ( n / 2 ) + 1 T(n)=T(n/2)+1 T(n)=T(n/2)+1,由定理5-1可得: T ( n ) = Θ ( log n ) T(n)=\Theta(\log{n}) T(n)=Θ(logn) 。
5-11
对两组数据:(1,1,1,1,1) 和 (5,5,8,3,4,3,2) 执行程序 5-12 的快速排序,按 5-1 的格式分别列表表示执行过程。
答:(1)
(2)
6-1
设有背包问题 n = 7 , M = 15 , ( w 0 , w 1 , … , w 6 ) = ( 2 , 3 , 5 , 7 , 1 , 4 , 1 ) n=7,M = 15,(w_0,w_1,\dots,w_6)=(2,3,5,7,1,4,1) n=7,M=15,(w0,w1,…,w6)=(2,3,5,7,1,4,1),物品装入背包的收益为: ( p 0 , p 1 , … , p 6 ) = ( 10 , 5 , 15 , 7 , 6 , 18 , 3 ) (p_0,p_1,…,p_6)=(10,5,15,7,6,18,3) (p0,p1,…,p6)=(10,5,15,7,6,18,3)。求这实例的最优解和最大收益。
由已知可得:
(
p
0
w
0
,
p
1
w
1
,
⋯
,
p
6
w
6
)
=
(
10
2
,
5
3
,
15
5
,
7
7
,
6
1
,
18
4
,
3
1
)
=
(
5
,
5
3
,
3
,
1
,
6
,
9
2
,
3
)
(\frac{p_{0}}{w_{0}},\frac{p_{1}}{w_{1}},\cdots,\frac{p_{6}}{w_{6}})=(\frac{10}{2},\frac{5}{3},\frac{15}{5},\frac{7}{7},\frac{6}{1},\frac{18}{4},\frac{3}{1})=(5,\frac{5}{3},3,1,6,\frac{9}{2},3)
(w0p0,w1p1,⋯,w6p6)=(210,35,515,77,16,418,13)=(5,35,3,1,6,29,3)
所以最优解为:
(
x
0
,
x
1
,
x
2
,
x
3
,
x
4
,
x
5
,
x
6
)
=
(
1
,
2
3
,
1
,
0
,
1
,
1
,
1
)
(x_{0},x_{1},x_{2},x_{3},x_{4},x_{5},x_{6})=(1,\frac{2}{3},1,0,1,1,1)
(x0,x1,x2,x3,x4,x5,x6)=(1,32,1,0,1,1,1)
最大收益为 1 × 10 + 2 3 × 5 + 1 × 15 + 1 × 6 + 1 × 18 + 1 × 3 = 55 1 3 1\times10 + \frac{2}{3}\times5+1\times15+1\times6+1\times18+1\times3=55\frac{1}{3} 1×10+32×5+1×15+1×6+1×18+1×3=5531。
6-8
设 w={3,7,8,9,15,16,18,20,23,25,28} ,请按照贪心准则,构造一棵最优3路合并树。
6-9
设 G = ( V , E ) G=(V,E) G=(V,E) 是一个无向连通图, n = ∣ V ∣ , m = ∣ E ∣ n=|V|,m=|E| n=∣V∣,m=∣E∣,且 m = m= m= 0 ( n 1.99 ) 0(n^{1.99}) 0(n1.99),试问选择何种算法求图的最小代价生成树,是普里姆算法还是克卢斯卡尔算法?
因为普里姆算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2), 克鲁斯卡尔算法的时间复杂度为 O ( m l o g m ) O(m logm) O(mlogm)。
因此当 m = O ( n 1.99 ) m=O(n^{1.99}) m=O(n1.99)时,此时使用克鲁斯卡尔算法的时间复杂度是 O ( n 1.99 l o g ( n 1.99 ) ≈ O ( n 2 l o g n ) O(n^{1.99} log(n^{1.99})\approx O(n^2logn) O(n1.99log(n1.99)≈O(n2logn),大于普里姆算法的时间复杂度。所以该情况下,普里姆算法效率较好。