把昨天看的第二章巩固一下,做一做编程习题。

2.6:

  第一天交2元罚金,以后每一天都是前一天的平方,第N天罚金将是多少?

这个题目和2.4.4-3介绍的幂运算基本一致。若按相同的递归思路分析,比那个问题要简单,因为从1次幂开始并且指数呈2^(n-1)分布,即1,2,3,4,16……所以没有对指数是奇数时的判定。实际上用循环来求要比用递归快。在不考虑溢出的前提下,解法如下:

  1. #include<iostream>
  2. using namespace std;
  3. typedef unsigned long long uint64;
  4. uint64 r(int n){
  5. if(n==){
  6. return ;
  7. }else{
  8. uint64 tmp=r(n-);
  9. return tmp*tmp;
  10. }
  11. }
  12.  
  13. uint64 w(int n){
  14. int result=;
  15. while(n!=){
  16. result*=result;
  17. n--;
  18. }
  19. return result;
  20. }
  21.  
  22. int main(){
  23. int n;
  24. cin>>n;
  25. cout<<r(n)<<" "<<w(n)<<endl;
  26. }

2.20:

  1. 确定一个正整数是否是素数。这个问题可以简化一下,因为n*n>(n-1)*(n+1)。所以只需要求n是否能被2到sqrt(n)整除就可以了,这里需要注意的就是2是最小的素数:
    for(i=2;i<sqrt(n);i++)对2不能返回正确的结果,因为1.414<2不会进入循环。所以要先行判断:
  1. #include<iostream>
  2. #include<cmath>
  3. using namespace std;
  4. bool ispn(int n){
  5. if((n%)==){
  6. return false;
  7. }else{
  8. int i,max=int(sqrt(n));
  9. for(i=;i<=max;i++){
  10. if((n%i)==) return false;
  11. }
  12. }
  13. return true;
  14. }
  15.  
  16. int main(){
  17. int n;
  18. cin>>n;
  19. cout<<ispn(n)<<endl;
  20. }

当然,也可以if(n==2)然后for(i=2;i<=max;i++)。需要注意的是平方根(max)的条件不是小于,是小于等于。接下来有一个厄拉多塞筛的时间复杂度问题,这里仅记录厄拉多塞筛:取从2到n,n>=2之间的全部素数只需要滤掉那些非素数。根据前面讲述的原理,去掉这些非素数的方法如下:循环去掉2-sqrt(n)之间的全部非素数:

  1. #include<iostream>
  2. #include<cmath>
  3. #include<vector>
  4. using namespace std;
  5. typedef unsigned long long uint64;
  6. void es(vector<int> &arr){
  7. int i,j,n=arr.size(),sqrtnum=int(sqrt(n));
  8. for(i=;i<=sqrtnum;i++){ //从最小的素数m(m=2)开始遍历到sqrt(arr.size())
  9. if(arr[i]==){ //找到后面第一个素数m(标记仍然为0的)
  10. for(j=i*;j<=n;j+=i){ //标记m*n(1<n<=arr.size/m)为非素数。
  11. arr[j]=;
  12. }
  13. }
  14. }
  15. }
  16. int main(){
  17. int i,n;
  18. cin>>n;
  19. vector<int> arr(n+);
  20. es(arr);
  21. for(i=;i<=n;i++){
  22. if(arr[i]==){
  23. cout<<i<<endl;
  24. }
  25. }
  26. }

注意以下几点(设n为上限):

1、代码中arr默认值为0,用以表示假定为素数,最终结果中被标志为1的都不是素数;其下标与数字一一对应,即若输入11,则数组最大下标为11,亦即数组元素为n+1个。

2、根据前面的结论,i只需要从2搜索到sqrt(n),包含sqrt(n)。

3、在进行标志时,从当前素数的2倍开始标志。

在chinaunix上面看到一篇类似的算法,1、2都犯了,只能低效的输出n-1以内的,漏掉了n。例如输入11,得到的是2、3、5、7,例如输入2什么也没出来。

歇一歇,2017年1月1日12:45,以上。

2.26:

  大小为N的数组A,若某个值出现次数超过N/2则称为主要元素。例如:3,3,4,2,4,4,2,4,4的主要元素是4。

  书中给出了一种解决思路:如果A0,A1相等,则把A0放入数组B,否则无操作;对后续数组A2,A3……依次做同样操作,这样得到一个数组B,对B数组进行同样操作……就可以找到A的主要元素。然后检验得到的元素是不是主要元素。这不失为一种好做法:根据主要元素的定义知道,它在数组中过半,也就是说无论如何排列,根据上述做法我们一定能找到一组相邻的主要元素,但当数组元素个数为奇数时,可以正好间隔开(此时需要把最后一个添加到B而不是省略——书中问题b)。递推可知,由B得到的C一定含有A的主要元素,即主要元素总会剩下的,反过来讲:即使数组只剩下一个元素那也一定是主要元素,那么递归终止条件就是最终的数组只有一个元素。等等,也许没有主要元素,那么最终数组中就不会剩下元素,所以递归终止条件是传入的数组中没有元素或只有一个元素——书中问题a。再等等,为什么题目中说还有第二步——检验是不是真的是主要元素呢?根据我们的思路,两两捉对,那么构建一个特例:3,4,5,5,3,4就可以回答这个问题了。

  到现在为止这个代码是这样的:

  1. const int INF=0x7fffffff;
  2. int me(const int a[],int n){
  3. if(n==){
  4. return INF;
  5. }else if(n==){
  6. return a[];
  7. }
  8. int i,bidx=,b[(n+)/];
  9. for(i=;i<n-;i+=){
  10. if(a[i]==a[i+]){
  11. b[bidx]=a[i];
  12. bidx++;
  13. }
  14. }
  15. if((n%)==){
  16. b[bidx]=a[n-];
  17. bidx++;
  18. }
  19. return me(b,bidx);
  20. }
  21.  
  22. bool test(const int a[],int n,int v){
  23. int cnt=;
  24. for(int i=;i<n;i++){
  25. if(a[i]==v){
  26. cnt++;
  27. }
  28. }
  29. if(cnt*>n){
  30. return true;
  31. }else{
  32. return false;
  33. }
  34. }
  35.  
  36. int main(){
  37. int a[]={,,,,,,,,};
  38. int alen=sizeof(a)/;
  39. int val=me(a,alen);
  40. cout<<test(a,alen,val)<<" "<<val<<endl;
  41. }

  以上代码我只进行了简单的测试,可能存在问题,敬请指正。

  这个问题实际上还有更好的解决方法,能够达到线性时间——用一次循环即可。但是在谈这种解决方案之前,我想先说一个类似问题:最大子序列和问题。这两个问题的解决思想非常相似。最大子序列和是这样一个问题:

  求一个数组中连续部分的最大和。例如数组:-1,4,-3,5,-2,-1,2,6,-2的最大子序列和为11(从4开始到6结束)。这个问题可以有很多解法,但其中较好的解法无一不是根据这样的特性:达到最大和的子序列不可能以负数开头;优秀的解法会更充分的利用这一点的推论:达到最大和的子序列任意前n个只和不可能以负数开头。若某一段序列的和为0,我们忽略它。

  现在根据我们的推论来考虑一下实现:从头遍历求和,当和小于0时,放弃之前的一段重新开始;当和大于0时,比较当前和是否大于已记录的最大和,若比记录的最大和还大,则替换最大和为当前值——所以即使后面加了若干负数导致和小于0被舍弃,也不会丢失这个最大和,编码如下:

  1. #include<iostream>
  2. using namespace std;
  3. int maxsum(const int arr[],int arrcnt){
  4. int i,maxsum=,cursum=;
  5. for(i=;i<arrcnt;i++){
  6. cursum+=arr[i]; //计算当前和
  7. if(cursum>maxsum){ //如果当前和大于最大和,替换最大和
  8. maxsum=cursum;
  9. }else if(cursum<){ //如果当前和小于0,说明之前加的是一个负数,并且之前的序列已经没有作用了(大于0的最大和序列如果大于maxsum则已经被记录了)
  10. cursum=; //所以重新开始记录和
  11. }
  12. }
  13. return maxsum;
  14. }
  15.  
  16. int main(){
  17. int a[]={,-,,-,-,,,-};
  18. cout<<maxsum(a,)<<endl;
  19. }

代码真的很少,而且时间复杂度为O(N)。所以,任何优秀的代码都是建立在对问题的深入剖析基础上的,而这往往需要敏锐的观察。怎么能敏锐呢?个人觉得就是遇到问题多思考,多动笔,多写代码尝试;对问题多类比,多对比,多归纳总结。

  那么,让我们继续剖析数组的主要元素这个问题:依然用上面的数组3,3,4,2,4,4,2,4,4,根据主要元素的定义我们知道它的个数超过任何其他元素个数的总和,即主元素个数至少比其他元素总和多1个。向上面最大子序列和靠拢:我们把数组中的元素分成两派:主元素派和其他元素派,然后进行计数:主元素使得计数+1,其他元素使得计数-1。可以预见的是,最终计数大于等于1。现在,问题已经基本和最大子序列和相同,只是最大子序列和中求的是元素和,而这里我们求的是计数和——其他元素计数-1,主元素计数+1而已。所以我们是不是可以考虑,从头开始遍历,假定a[0]是主元素,那么我们数一数,当到达a[4]时,计数和变为-1——从这里继续,假定a[4]为主元素,数到数组结尾计数为3。那么我们认为a[4]就是主元素。等等,这还不一定正确,让我们检验一下,如果开始我们找到的就是主元素,但是和前面的a[0]一样,被若干其他元素消除了计数结论还正确吗?这显而易见,把计数变负数时需要的其他元素个数多于前面的主元素个数,这导致后面会堆积至少相同个数个主元素。所以,如果存在主元素,那么我们的结论是正确的。现在,考虑不存在主元素时,我们的扫描结果的正确性:数组3,3,1,2,5扫描结果是5。所以,不存在主元素时我们的算法返回了错误结果,同样需要用test函数来检验。而这种算法本身的编码很简单:

  1. int me1(const int a[],int n){
  2. int i,cnt=,curval=a[];
  3. for(i=;i<n;i++){
  4. if(a[i-]==a[i]){
  5. cnt++;
  6. }else{
  7. cnt=;
  8. curval=a[i];
  9. }
  10. }
  11. return curval;
  12. }

在之前的main函数结尾添加以下代码来测试它:

  1. val=me1(a,alen);
  2. cout<<test(a,alen,val)<<" "<<val<<endl;

2017-1-1 22:40 以上

数据结构与算法分析C++表述第二章编程题的更多相关文章

  1. 『CUDA C编程权威指南』第二章编程题选做

    第一题 设置线程块中线程数为1024效果优于设置为1023,且提升明显,不过原因未知,以后章节看看能不能回答. 第二题 参考文件sumArraysOnGPUtimer.cu,设置block=256,新 ...

  2. 《python语言程序设计》_第二章编程题

    2.1 程序: Celsius=eval(input("Enter a degree in Celsius:"))#输入摄氏度的值Celsiusfahrenheit =(9/5)* ...

  3. Java程序设计(2021春)——第二章课后题(选择题+编程题)答案与详解

    Java程序设计(2021春)--第二章课后题(选择题+编程题)答案与详解 目录 Java程序设计(2021春)--第二章课后题(选择题+编程题)答案与详解 第二章选择题 2.1 面向对象方法的特性 ...

  4. 20165237 2017-2018-2 《Java程序设计》第四周考试补做及2-3章编程题

    20165237 2017-2018-2 <Java程序设计>第四周考试补做及2-3章编程题 测试JDB: 用JDB调试上一个程序,输入1.2.3: 2-3章编程题代码托管 (程序的运行结 ...

  5. javascript 数据结构和算法读书笔记 > 第二章 数组

    这章主要讲解了数组的工作原理和其适用场景. 定义: 一个存储元素的线性集合,元素可以通过索引来任意存取,索引通常是数字,用来计算元素之间存储位置的偏移量. javascript数组的特殊之处: jav ...

  6. 面向对象程序设计--Java语言第二周编程题:有秒计时的数字时钟

    有秒计时的数字时钟 题目内容: 这一周的编程题是需要你在课程所给的时钟程序的基础上修改而成.但是我们并不直接给你时钟程序的代码,请根据视频自己输入时钟程序的Display和Clock类的代码,然后来做 ...

  7. 《数据结构与算法Python语言描述》习题第二章第一题(python版)

    题目:定义一个表示时间的类Timea)Time(hours,minutes,seconds)创建一个时间对象:b)t.hours(),t.minutes(),t.seconds()分别返回时间对象t的 ...

  8. c++primer 第二章编程练习答案

    2.7.1 #include<iostream> int main() { using namespace std; ]; ]; cout << "input nam ...

  9. java语言程序设计与数据结构(基础篇)第二章答案

    答案为本人自己求解,若有错误,还望海涵并及时告知.如有雷同,纯属巧合. 2.1 import java.util.Scanner; public class Welcome { public stat ...

随机推荐

  1. MySQL 编译安装并且开启DEBUG模式

    因为想分析下mysql中一些操作的内部执行过程,单纯的看源码太枯燥了,所以决定结合mysql的执行过程来分析,mysql作为一款成熟的数据库软件,在设计的时候就考虑到了调试的问题,只是想开启调试模式的 ...

  2. 模拟搭建Web项目的真实运行环境(一)

    序言 最近尝试完整搭建一个Web项目的运行环境,总结一下这几个月学到的知识点. 后面的文章主要包括一下几个内容: A. 搭建一个Linux服务器,用来部署Redis.Mongo等数据存储环境: B. ...

  3. CSS后代选择器可能的错误认识

    一.关于类选择器的一个问题 CSS代码: .red { color: red; } .green { color: green; } HTML代码: <div class="red&q ...

  4. 微信开发之Author网页授权

     微信开发中,经常有这样的需求:获得用户头像.绑定微信号给用户发信息.. 那么实现这些的前提就是授权!   1.配置安全回调域名: 在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的&q ...

  5. EpochConverter

    地址:http://www.epochconverter.com/ How to get the current epoch time in ... PHP time() more ... Pytho ...

  6. OSI七层模型及TCP/IP四层模型

    1)  OSI七层模型及TCP/IP四层模型 OSI七层模型:是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系.它是一个七层的.抽象的模型,不仅包括一系列抽象的术语或概念,也包 ...

  7. 关于jqgrid数据不显示问题

    近日有个需求要用到jqgrid,原本用着一切都很顺利,但是在需求变动后,只是修改部分字段名称jqgrid就不显示数据了,后台数据也能传到前台,但是就是不给我显示,到嘴的肉就是没法吃,蛋疼,郁闷都无法形 ...

  8. iOS--创建炫酷的渐变色界面

    { CAGradientLayer *_layer; } //创建渐变层 _layer =[CAGradientLayer layer]; _layer.frame=self.view.frame; ...

  9. linux 查看Java 进程的内存使用情况

    top -b -n 1 | grep java| awk '{print "PID:"$1",mem:"$6",CPU percent:"$ ...

  10. vs发布的程序不依赖运行时库msvcp100.dll

      [摘要:msvcr100.dll:MS Visual C Runtime 100 msvcp100.dll:MS Visual CPp 100 vs建立的工程,运转时库的范例有MD(MDd)战MT ...