慢慢更新。

第二章

2-8

确定划线语句执行次数、计算渐近时间复杂度

i=1; x=0;
do{
	x++; i=2*i;
} while i<n;

答案: 设语句执行了k次,2k≥n2^k \ge n2kn 时退出循环,因此划线语句执行了 ⌈log2n⌉\lceil log_2 n \rceillog2n 次。渐进时间复杂度 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=1nj=1ik=1j1=i=1nj=1ij=i=1n2i(i+1)=21[i=1ni+i=1ni2]=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)2n 时执行划线语句,即:y≤⌊n⌋−1:y\leq\left\lfloor\sqrt{n}\right\rfloor-1:yn 1 时执行。
又因为 y 从0开始,因此0~⌊n⌋−1\lfloor\sqrt{\mathrm{n}}\rfloor_{-1}n 1 共执行⌊n⌋\lfloor\sqrt{\mathrm{n}}\rfloorn 次。

渐近时间复杂度为: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=02n2in1=i=02n(n2i+1)
【为什么是⌊n2⌋\lfloor \frac{n}{2}\rfloor2n?当外部循环运行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=02n(n2i+1)=(n+1)(⌊2n+1)222n(⌊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_1nn1,有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_2nn2,有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_0nn0时,有
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_1nn1,有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_2nn2,有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_0nn0时,有
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)={Θ(nlog⁡ba)如果a≥bkΘ(nklog⁡n)如果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)如果abk如果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)

1

(2)因为该算法总是先经过while循环后再进行条件判断:即使是最好情况下,要寻找的元素恰好在中间,该算法依然要进入while循环。

所以T(n)=T(n/2)+1T(n)=T(n/2)+1T(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)

image-20240325205815334

(2)

image-20240325204833316

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路合并树。

2

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),大于普里姆算法的时间复杂度。所以该情况下,普里姆算法效率较好。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐