1 1.1 第 1 章─概论 2 1.1.1 练习题 3 1. 下列关于算法的说法中正确的有( )。Ⅰ.求解某一类问题的算法是唯一的 4 Ⅱ.算法必须在有限步操作之后停止 5 Ⅲ.算法的每一步操作必须是明确的,不能有歧义或含义模糊Ⅳ.算法执行后一定产生确定的结果 6 A. 1 个 B.2 个 C.3 个 D.4 个 7 2. T(n)表示当输入规模为 n 时的算法效率,以下算法效率最优的是( )。 8 A.T(n)= T(n-1)+1,T(1)=1 B.T(n)= 2n2 9 C.T(n)= T(n/2)+1,T(1)=1 D.T(n)=3nlog2n 10 3. 什么是算法?算法有哪些特征? 11 4. 判断一个大于 2 的正整数 n 是否为素数的方法有多种,给出两种算法,说明其中一种算法更好的理由。 12 5. 证明以下关系成立: 13 (1)10n2-2n=(n2) 14 (2)2n+1=(2n) 15 6. 证明 O(f(n))+O(g(n))=O(max{f(n),g(n)}) 。 16 7. 有一个含 n(n>2)个整数的数组 a,判断其中是否存在出现次数超过所有元素一半的元素。 17 8. 一个字符串采用 string 对象存储,设计一个算法判断该字符串是否为回文。 18 9. 有一个整数序列,设计一个算法判断其中是否存在两个元素和恰好等于给定的整数 k。 19 10. 有两个整数序列,每个整数序列中所有元素均不相同。设计一个算法求它们的公共元素,要求不使用 STL 的集合算法。 20 11. 正整数 n(n>1)可以写成质数的乘积形式,称为整数的质因数分解。例如, 12=2*2*3,18=2*3*3,11=11。设计一个算法求 n 这样分解后各个质因数出现的次数,采用 vector 向量存放结果。 21 12. 有一个整数序列,所有元素均不相同,设计一个算法求相差最小的元素对的个数。如序列 4、1、2、3 的相差最小的元素对的个数是 3,其元素对是(1,2),(2,3), 22 (3,4)。 23 13. 有一个 map<string,int>容器,其中已经存放了较多元素。设计一个算法求出其中重复的 value 并且返回重复 value 的个数。 24 14. 重新做第 10 题,采用 map 容器存放最终结果。 25 15. 假设有一个含 n(n>1)个元素的 stack<int>栈容器 st,设计一个算法出栈从栈顶到栈底的第 k(1≤k≤n)个元素,其他栈元素不变。 26 27 28 1.1.2 练习题参考答案 29 1. 答:由于算法具有有穷性、确定性和输出性,因而Ⅱ、Ⅲ、Ⅳ正确,而解决某一类问题的算法不一定是唯一的。答案为 C。 30 2. 答:选项 A 的时间复杂度为 O(n)。选项 B 的时间复杂度为 O(n2)。选项 C 的时间复杂度为 O(log2n)。选项 D 的时间复杂度为 O(nlog2n)。答案为 C。 31 3. 答:算法是求解问题的一系列计算步骤。算法具有有限性、确定性、可行性、输入性和输出性 5 个重要特征。 32 4. 答:两种算法如下: 33 #include <stdio.h> #include <math.h> 34 bool isPrime1(int n) //方法 1 35 { for (int i=2;i<n;i++) 36 if (n%i==0) 37 return false; 38 return true; 39 } 40 bool isPrime2(int n) //方法 2 41 { for (int i=2;i<=(int)sqrt(n);i++) 42 if (n%i==0) 43 return false; 44 return true; 45 } 46 void main() 47 { int n=5; 48 printf("%d,%d\n",isPrime1(n),isPrime2(n)); 49 } 50 方法 1 的时间复杂度为 O(n),方法 2 的时间复杂度为 n,所以方法 2 更好。 51 5. 答:(1)当 n 足够大时,(10n2-2n)/( n2)=10,所以 10n2-2n=(n2)。 52 (2)2n+1=2*2n=(2n)。 53 6. 证明:对于任意 f1(n)∈O(f(n)) ,存在正常数 c1 和正常数 n1,使得对所有 n≥n1, 有 f1(n)≤c1f(n) 。 54 类似地,对于任意 g1(n)∈O(g(n)) ,存在正常数 c2 和自然数 n2,使得对所有 n≥n2, 有 g1(n)≤c2g(n) 。 55 令 c3=max{c1,c2},n3=max{n1,n2},h(n)= max{f(n),g(n)} 。则对所有的 n≥n3,有: 56 f1(n) +g1(n)≤c1f(n) + c2g(n)≤c3f(n)+c3g(n)=c3(f(n)+g(n)) 57 ≤c32max{f(n),g(n)}=2c3h(n)=O(max{f(n),g(n)})。 58 7. 解:先将 a 中元素递增排序,再求出现次数最多的次数 maxnum,最后判断是否满足条件。对应的程序如下: 59 #include <stdio.h> #include <algorithm> using namespace std; 60 61 bool solve(int a[],int n,int &x) 62 { 63 sort(a,a+n); 64 int maxnum=0; //递增排序 65 //出现次数最多的次数 66 int num=1; 67 int e=a[0]; 68 for (int i=1;i<n;i++) 69 { if (a[i]==e) 70 { num++; 71 if (num>maxnum) 72 { maxnum=num; 73 x=e; 74 } 75 } 76 else 77 { e=a[i]; 78 num=1; 79 } 80 } 81 if (maxnum>n/2) 82 return true; 83 else 84 return false; 85 } 86 void main() 87 { int a[]={2,2,2,4,5,6,2}; 88 int n=sizeof(a)/sizeof(a[0]); 89 int x; 90 if (solve(a,n,x)) 91 printf("出现次数超过所有元素一半的元素为%d\n",x); 92 else 93 printf("不存在出现次数超过所有元素一半的元素\n"); 94 } 95 上述程序的执行结果如图 1.1 所示。 96 图 1.1 程序执行结果 97 8. 解:采用前后字符判断方法,对应的程序如下: 98 #include <iostream> #include <string> using namespace std; 99 bool solve(string str) //判断字符串 str 是否为回文 100 { int i=0,j=str.length()-1; 101 while (i<j) 102 { if (str[i]!=str[j]) 103 return false; 104 105 106 i++; j--; 107 } 108 return true; 109 } 110 void main() 111 { cout << "求解结果" << endl; 112 string str="abcd"; 113 cout << " " << str << (solve(str)?"是回文":"不是回文") << endl; 114 string str1="abba"; 115 cout << " " << str1 << (solve(str1)?"是回文":"不是回文") << endl; 116 } 117 上述程序的执行结果如图 1.2 所示。 118 图 1.2 程序执行结果 119 9. 解:先将 a 中元素递增排序,然后从两端开始进行判断。对应的程序如下: 120 #include <stdio.h> #include <algorithm> using namespace std; 121 bool solve(int a[],int n,int k) 122 { sort(a,a+n); //递增排序 123 int i=0, j=n-1; 124 while (i<j) //区间中存在两个或者以上元素 125 { if (a[i]+a[j]==k) 126 return true; 127 else if (a[i]+a[j]<k) 128 i++; 129 else 130 j--; 131 } 132 return false; 133 } 134 void main() 135 { int a[]={1,2,4,5,3}; 136 int n=sizeof(a)/sizeof(a[0]); 137 printf("求解结果\n"); 138 int k=9,i,j; 139 if (solve(a,n,k,i,j)) 140 printf(" 存在: %d+%d=%d\n",a[i],a[j],k); 141 else 142 printf(" 不存在两个元素和为%d\n",k); 143 int k1=10; 144 if (solve(a,n,k1,i,j)) 145 printf(" 存在: %d+%d=%d\n",a[i],a[j],k1); 146 147 else 148 printf(" 不存在两个元素和为%d\n",k1); 149 } 150 上述程序的执行结果如图 1.3 所示。 151 图 1.3 程序执行结果 152 10. 解:采用集合 set<int>存储整数序列,集合中元素默认是递增排序的,再采用二路归并算法求它们的交集。对应的程序如下: 153 #include <stdio.h> #include <set> 154 using namespace std; 155 void solve(set<int> s1,set<int> s2,set<int> &s3) //求交集 s3 156 { set<int>::iterator it1,it2; 157 it1=s1.begin(); it2=s2.begin(); 158 while (it1!=s1.end() && it2!=s2.end()) 159 { if (*it1==*it2) 160 { s3.insert(*it1); 161 ++it1; ++it2; 162 } 163 else if (*it1<*it2) 164 ++it1; 165 else 166 ++it2; 167 } 168 } 169 void dispset(set<int> s) //输出集合的元素 170 { set<int>::iterator it; 171 for (it=s.begin();it!=s.end();++it) 172 printf("%d ",*it); 173 printf("\n"); 174 } 175 void main() 176 { int a[]={3,2,4,8}; 177 int n=sizeof(a)/sizeof(a[0]); 178 set<int> s1(a,a+n); 179 int b[]={1,2,4,5,3}; 180 int m=sizeof(b)/sizeof(b[0]); 181 set<int> s2(b,b+m); 182 set<int> s3; 183 solve(s1,s2,s3); 184 printf("求解结果\n"); 185 printf(" s1: "); dispset(s1); 186 187 188 printf(" s2: "); dispset(s2); 189 printf(" s3: "); dispset(s3); 190 } 191 上述程序的执行结果如图 1.4 所示。 192 图 1.4 程序执行结果 193 11. 解:对于正整数 n,从 i=2 开始查找其质因数,ic 记录质因数 i 出现的次数,当找到这样质因数后,将(i,ic)作为一个元素插入到 vector 容器 v 中。最后输出 v。对应的算法如下: 194 #include <stdio.h> #include <vector> using namespace std; 195 struct NodeType //vector 向量元素类型 196 { int p; //质因数 197 int pc; //质因数出现次数 198 }; 199 void solve(int n,vector<NodeType> &v) //求 n 的质因数分解 200 { int i=2; 201 int ic=0; 202 NodeType e; 203 do 204 { if (n%i==0) 205 { ic++; 206 n=n/i; 207 } 208 else 209 { if (ic>0) 210 { e.p=i; 211 e.pc=ic; 212 v.push_back(e); 213 } 214 ic=0; 215 i++; 216 } 217 } while (n>1 || ic!=0); 218 } 219 void disp(vector<NodeType> &v) //输出 v 220 { vector<NodeType>::iterator it; 221 for (it=v.begin();it!=v.end();++it) 222 printf(" 质因数%d 出现%d 次\n",it->p,it->pc); 223 } 224 225 void main() 226 { vector<NodeType> v; 227 int n=100; 228 printf("n=%d\n",n); 229 solve(n,v); 230 disp(v); 231 } 232 上述程序的执行结果如图 1.5 所示。 233 图 1.5 程序执行结果 234 12. 解:先递增排序,再求相邻元素差,比较求最小元素差,累计最小元素差的个数。对应的程序如下: 235 #include <iostream> #include <algorithm> #include <vector> using namespace std; 236 int solve(vector<int> &myv) //求 myv 中相差最小的元素对的个数 237 { sort(myv.begin(),myv.end()); //递增排序 238 int ans=1; 239 int mindif=myv[1]-myv[0]; 240 for (int i=2;i<myv.size();i++) 241 { if (myv[i]-myv[i-1]<mindif) 242 { ans=1; 243 mindif=myv[i]-myv[i-1]; 244 } 245 else if (myv[i]-myv[i-1]==mindif) 246 ans++; 247 } 248 return ans; 249 } 250 void main() 251 { int a[]={4,1,2,3}; 252 int n=sizeof(a)/sizeof(a[0]); 253 vector<int> myv(a,a+n); 254 cout << "相差最小的元素对的个数: " << solve(myv) << endl; 255 } 256 上述程序的执行结果如图 1.6 所示。 257 258 259 图 1.6 程序执行结果 260 13. 解:对于 map<string,int>容器 mymap,设计另外一个 map<int,int>容器 tmap, 将前者的 value 作为后者的关键字。遍历 mymap,累计 tmap 中相同关键字的次数。一个参考程序及其输出结果如下: 261 #include <iostream> #include <map> #include <string> using namespace std; void main() 262 { map<string,int> mymap; 263 mymap.insert(pair<string,int>("Mary",80)); 264 mymap.insert(pair<string,int>("Smith",82)); 265 mymap.insert(pair<string,int>("John",80)); 266 mymap.insert(pair<string,int>("Lippman",95)); 267 mymap.insert(pair<string,int>("Detial",82)); 268 map<string,int>::iterator it; 269 map<int,int> tmap; 270 for (it=mymap.begin();it!=mymap.end();it++) 271 tmap[(*it).second]++; 272 map<int,int>::iterator it1; 273 cout << "求解结果" << endl; 274 for (it1=tmap.begin();it1!=tmap.end();it1++) 275 cout << " " << (*it1).first << ": " << (*it1).second << "次\n"; 276 } 277 上述程序的执行结果如图 1.7 所示。 278 图 1.7 程序执行结果 279 14. 解:采用 map<int,int>容器 mymap 存放求解结果,第一个分量存放质因数,第二个分量存放质因数出现次数。对应的程序如下: 280 #include <stdio.h> #include <map> 281 using namespace std; 282 void solve(int n,map<int,int> &mymap) //求 n 的质因数分解 283 284 285 286 287 else 288 { if (ic>0) 289 mymap[i]=ic; 290 ic=0; 291 i++; 292 } 293 } while (n>1 || ic!=0); 294 } 295 void disp(map<int,int> &mymap) //输出 mymap 296 { map<int,int>::iterator it; 297 for (it=mymap.begin();it!=mymap.end();++it) 298 printf(" 质因数%d 出现%d 次\n",it->first,it->second); 299 } 300 void main() 301 { map<int,int> mymap; 302 int n=12345; 303 printf("n=%d\n",n); 304 solve(n,mymap); 305 disp(mymap); 306 } 307 上述程序的执行结果如图 1.8 所示。 308 图 1.8 程序执行结果 309 15. 解:栈容器不能顺序遍历,为此创建一个临时 tmpst 栈,将 st 的 k 个元素出栈并进栈到 tmpst 中,再出栈 tmpst 一次得到第 k 个元素,最后将栈 tmpst 的所有元素出栈并进栈到 st 中。对应的程序如下: 310 #include <stdio.h> #include <stack> using namespace std; 311 int solve(stack<int> &st,int k) //出栈第 k 个元素 312 { stack<int> tmpst; 313 int e; 314 for (int i=0;i<k;i++) //出栈 st 的 k 个元素并进 tmpst 栈 315 { e=st.top(); 316 st.pop(); 317 tmpst.push(e); 318 } 319 e=tmpst.top(); //求第 k 个元素 320 tmpst.pop(); 321 while (!tmpst.empty()) //将 tmpst 的所有元素出栈并进栈 st 322 { st.push(tmpst.top()); 323 tmpst.pop(); 324 325 326 } 327 return e; 328 } 329 void disp(stack<int> &st) //出栈 st 的所有元素 330 { while (!st.empty()) 331 { printf("%d ",st.top()); 332 st.pop(); 333 } 334 printf("\n"); 335 } 336 void main() 337 { stack<int> st; 338 printf("进栈元素 1,2,3,4\n"); 339 st.push(1); 340 st.push(2); 341 st.push(3); 342 st.push(4); 343 int k=3; 344 int e=solve(st,k); 345 printf("出栈第%d 个元素是: %d\n",k,e); 346 printf("st 中元素出栈顺序: "); 347 disp(st); 348 } 349 上述程序的执行结果如图 1.9 所示。 350 图 1.9 程序执行结果 351 1.2 第 2 章─递归算法设计技术 352 1.2.1 练习题 353 1. 什么是直接递归和间接递归?消除递归一般要用到什么数据结构? 354 2. 分析以下程序的执行结果: 355 #include <stdio.h> 356 void f(int n,int &m) 357 { if (n<1) return; 358 else 359 { printf("调用f(%d,%d)前,n=%d,m=%d\n",n-1,m-1,n,m); 360 n--; m--; 361 f(n-1,m); 362 printf("调用f(%d,%d)后:n=%d,m=%d\n",n-1,m-1,n,m); 363 } 364 365 } 366 void main() 367 { int n=4,m=4; 368 f(n,m); 369 } 370 3. 采用直接推导方法求解以下递归方程: 371 T(1)=1 372 T(n)=T(n-1)+n 当 n>1 373 4. 采用特征方程方法求解以下递归方程: 374 H(0)=0 375 H(1)=1 376 H(2)=2 377 H(n)=H(n-1)+9H(n-2)-9H(n-3) 当 n>2 378 5. 采用递归树方法求解以下递归方程: 379 T(1)=1 380 T(n)=4T(n/2)+n 当 n>1 381 6. 采用主方法求解以下题的递归方程。 382 T(n)=1 当 n=1 383 T(n)=4T(n/2)+n2 当 n>1 384 7. 分析求斐波那契 f(n)的时间复杂度。 385 8. 数列的首项 a1=0,后续奇数项和偶数项的计算公式分别为 a2n=a2n-1+2,a2n+1=a2n- 1+a2n-1,写出计算数列第 n 项的递归算法。 386 9. 对于一个采用字符数组存放的字符串 str,设计一个递归算法求其字符个数(长度)。 387 10. 对于一个采用字符数组存放的字符串 str,设计一个递归算法判断 str 是否为回文。 388 11. 对于不带头结点的单链表 L,设计一个递归算法正序输出所有结点值。 389 12. 对于不带头结点的单链表 L,设计一个递归算法逆序输出所有结点值。 390 13. 对于不带头结点的非空单链表 L,设计一个递归算法返回最大值结点的地址(假设这样的结点唯一)。 391 14. 对于不带头结点的单链表 L,设计一个递归算法返回第一个值为 x 的结点的地址,没有这样的结点时返回 NULL。 392 15. 对于不带头结点的单链表 L,设计一个递归算法删除第一个值为 x 的结点。 393 16. 假设二叉树采用二叉链存储结构存放,结点值为 int 类型,设计一个递归算法求二叉树 bt 中所有叶子结点值之和。 394 17. 假设二叉树采用二叉链存储结构存放,结点值为 int 类型,设计一个递归算法求二叉树 bt 中所有结点值大于等于 k 的结点个数。 395 18. 假设二叉树采用二叉链存储结构存放,所有结点值均不相同,设计一个递归算法求值为 x 的结点的层次(根结点的层次为 1),没有找到这样的结点时返回 0。 396 397 398 1.2.2 练习题参考答案 399 1. 答:一个 f 函数定义中直接调用 f 函数自己,称为直接递归。一个 f 函数定义中调用 g 函数,而 g 函数的定义中调用 f 函数,称为间接递归。消除递归一般要用栈实现。 400 2. 答:递归函数f(n,m)中,n是非引用参数,m是引用参数,所以递归函数的状态为 401 (n)。程序执行结果如下: 402 调用f(3,3)前,n=4,m=4 调用f(1,2)前,n=2,m=3 调用f(0,1)后,n=1,m=2 调用f(2,1)后,n=3,m=2 403 3. 解:求 T(n)的过程如下: 404 T(n)=T(n-1)+n=[T(n-2)+n-1)]+n=T(n-2)+n+(n-1) 405 =T(n-3)+n+(n-1)+(n-2) 406 =… 407 =T(1)+n+(n-1)+…+2 408 =n+(n-1)+ +…+2+1=n(n+1)/2=O(n2)。 409 4. 解:整数一个常系数的线性齐次递推式,用 xn 代替 H(n),有:xn=xn-1+9xn-2-9xn-3, 两边同时除以 xn-3,得到:x3=x2+9x-9,即 x3-x2-9x+9=0。 410 x3-x2-9x+9=x(x2-9)-(x2-9)=(x-1)(x2-9)=(x-1)(x+3)(x-3)=0。得到 r1=1,r2=-3,r3=3 411 则递归方程的通解为:H(n)=c1+c2(-3)n+c33n 代入 H(0)=0,有 c1+c2+c3=0 412 代入 H(1)=1,有 c1-3c2+3c3=1 413 代入 H(2)=2,有 c1+9c2+9c3=2 414 415 ( ‒ 1)n ‒ 1 416 417 418 419 n ‒ 1 1 420 421 422 求出:c1=-1/4,c2=-1/12,c3=1/3,H(n)=c1+c2(-3)n+c33n=( 4 + 1)3 423 424 ‒ 4。 425 426 5. 解:构造的递归树如图 1.10 所示,第 1 层的问题规模为 n,第 2 的层的子问题的问题规模为 n/2,依此类推,当展开到第 k+1 层,其规模为 n/2k=1,所以递归树的高度为log2n+1。 427 第1层有1个结点,其时间为n,第2层有4个结点,其时间为4(n/2)=2n,依次类推,第k 层有4k-1个结点,每个子问题规模为n/2k-1,其时间为4k-1(n/2k-1)=2k-1n。叶子结点的个数为n 个,其时间为n。将递归树每一层的时间加起来,可得: 428 T(n)=n+2n+…+ 2k-1n+…+n≈
相关文章: