南京邮电大学 算法设计与分析 课后习题
南京邮电大学 算法设计与分析 课后习题
慢慢更新。
第二章
2-8
确定划线语句执行次数、计算渐近时间复杂度
i=1; x=0;
do{
x++; i=2*i;
} while i<n;
答案: 设语句执行了k次,2k≥n2^k \ge n2k≥n 时退出循环,因此划线语句执行了 ⌈log2n⌉\lceil log_2 n \rceil⌈log2n⌉ 次。渐进时间复杂度 O(log2n)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=1n∑j=1i∑k=1j1=∑i=1n∑j=1ij=∑i=1ni(i+1)2=12[∑i=1ni+∑i=1ni2]=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=1ni2=n(n+1)(2n+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⌊n2⌋∑2in1=∑i=0⌊n2⌋(n−2i+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)
【为什么是⌊n2⌋\lfloor \frac{n}{2}\rfloor⌊2n⌋?当外部循环运行k次时,划线语句能运行(n+1-2k)次,而划线语句只能执行减少到一次时,下一次循环将终止,此时 n+1-2k=1,有 k=⌊n2⌋k=\lfloor \frac{n}{2}\rfloork=⌊2n⌋】
∑i=0⌊n2⌋(n−2i+1) =(n+1)(⌊n2⌋+1)−2⌊n2⌋(⌊n2⌋+1)2=(⌊n2⌋+1)(⌊n2⌋+1)={n是偶数:(n2+1)2n是奇数:(n+32)(n+12) \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(n2)O(n^2)O(n2)
2-13
设 f1(n)=O(g1(n)),f2(n)=O(g2(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) f1(n)+f2(n)=O(max{g1(n),g2(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) f1(n)+f2(n)=O(g1(n)−g2(n))f_1(n)+f_2(n)=O(g_1(n)-g_2(n))f1(n)+f2(n)=O(g1(n)−g2(n))
答案:(1)解:因为f1(n)∈O(g1(n))f_1(n)\in O(g_1(n))f1(n)∈O(g1(n)),所以存在正常数c1c_1c1和自然数n1n_1n1,当n≥n1n\ge n_1n≥n1,有f1(n)≤c1g1(n)f _1(n)\leq c_1 g_1(n)f1(n)≤c1g1(n)。
因为f2(n)∈O(g2(n))f_2(n)\in O(g_2(n))f2(n)∈O(g2(n)),所以存在正常数c2c_2c2和自然数n2n_2n2,当n≥n2n\ge n_2n≥n2,有f2(n)≤c2g2(n)f _2(n)\leq c_2 g_2(n)f2(n)≤c2g2(n)。
取c3=max{c1,c2},n3=max{n1,n2}c_3=\max\{c_1,c_2\},n_3=\max\{n_1,n_2\}c3=max{c1,c2},n3=max{n1,n2},
则当n≥n0n\geq n_0n≥n0时,有
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)}) \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)解:因为f1(n)=O(g1(n))f_1(n)=O(g_1(n))f1(n)=O(g1(n)),所以存在正常数c1c_1c1和自然数n1n_1n1,当n≥n1n\ge n_1n≥n1,有f1(n)≤c1g1(n)f _1(n)\leq c_1 g_1(n)f1(n)≤c1g1(n)。
因为f2(n)=O(g2(n))f_2(n)=O(g_2(n))f2(n)=O(g2(n)),所以存在正常数c2c_2c2和自然数n2n_2n2,当n≥n2n\ge n_2n≥n2,有f2(n)≤c2g2(n)f _2(n)\leq c_2 g_2(n)f2(n)≤c2g2(n)。
取c=max{c1,c2},n=max{n1,n2}c=\max\{c_1,c_2\},n=\max\{n_1,n_2\}c=max{c1,c2},n=max{n1,n2},
则当n≥n0n\geq n_0n≥n0时,有
f1(n)+f2(n)≤c1g1(n)+c2g2(n)≤cg1(n)+cg2(n)=c(g1(n)+g2(n))=O(g1(n)+g2(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)+2T(n)=T(3/n)+2T(n)=T(3/n)+2。
定理5-1:a,b,c和k为常数,T(n)=aT(n/b)+cnkT(n)=aT(n/b)+cn^kT(n)=aT(n/b)+cnk,T(1)=c,则
T(n)={Θ(nlogba)如果a≥bkΘ(nklogn)如果a=bkΘ(nk)如果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)=Θ(logn)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)证明这一算法的最好、平均和最坏情况的时间复杂度均为 Θ(logn)\Theta(\log{n})Θ(logn) 。
答:(1)
(2)因为该算法总是先经过while循环后再进行条件判断:即使是最好情况下,要寻找的元素恰好在中间,该算法依然要进入while循环。
所以T(n)=T(n/2)+1T(n)=T(n/2)+1T(n)=T(n/2)+1,由定理5-1可得:T(n)=Θ(logn)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,(w0,w1,…,w6)=(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),物品装入背包的收益为:(p0,p1,…,p6)=(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)。求这实例的最优解和最大收益。
由已知可得:
(p0w0,p1w1,⋯ ,p6w6)=(102,53,155,77,61,184,31)=(5,53,3,1,6,92,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)
所以最优解为:(x0,x1,x2,x3,x4,x5,x6)=(1,23,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+23×5+1×15+1×6+1×18+1×3=55131\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(n1.99)0(n^{1.99})0(n1.99),试问选择何种算法求图的最小代价生成树,是普里姆算法还是克卢斯卡尔算法?
因为普里姆算法的时间复杂度为O(n2)O(n^2)O(n2), 克鲁斯卡尔算法的时间复杂度为 O(mlogm)O(m logm)O(mlogm)。
因此当m=O(n1.99)m=O(n^{1.99})m=O(n1.99)时,此时使用克鲁斯卡尔算法的时间复杂度是 O(n1.99log(n1.99)≈O(n2logn)O(n^{1.99} log(n^{1.99})\approx O(n^2logn)O(n1.99log(n1.99)≈O(n2logn),大于普里姆算法的时间复杂度。所以该情况下,普里姆算法效率较好。
更多推荐
所有评论(0)