第一章作业2—算法时间复杂度和空间复杂度

找好结束的临界点,找好每一次循环所需要的最长时间。

一、单选题
1、

这里写图片描述

答案:B

解析:从要满足的条件下手,或者终止条件下手,因为这里的终止条件是x<(y+1)²,又因为x=n,所以y>根号x-1的时候停止,因为y每一次加1,所以基本语句的频度为根号x-1,所以时间复杂度为根号x;


2、
这里写图片描述

答案:B

解析:
对于选择语句,看最坏的情况,时间复杂度按照大的来看。
法一:外层的N²/100可以看成N²(因为当N无限大的时候,除以的100根本就不起什么作用了)
对于if语句,i=0的时候,内部循环执行N²次,i=1的时候执行N²-1次,依次类推,最终执行0次。所以形成了等差数列,首相为N²,末项为0,公差为1,项数为(末项-首相)/公差+1,所以内层循环大致为N的四次方,所以基本语句的频度为N的四次方,所以时间复杂度为N的四次方。

法二:按照时间最复杂来讲,i从0到N²,里层所需最长的时间为N²次,外层最久需要同理也为N²次,所以最终需要N的四次方次。


这里写图片描述

答案:B

解析:
这个题终止的条件为sum>=n,所以也就是说n=1+2+…+i(虽然说sum=sum+i++ 左边和右边都有sum,但是最终与n进行比较的是左边的sum,所以,将左边的sum带换成刚好停止循环时候的条件,即n,右边仍为i),所以n=(1+i)*i/2,所以i=根号下n,所以时间复杂度为根号下n。


3、
这里写图片描述

答案:D

解析:
j除以m次的2之后,j<1,就会变为0,i可以除以2多少次就相当于logi,因为i要从0到n,按照时间最复杂来看,所以里层最多执行logn次,因为外层执行n次,所以为nlogn。


4、
这里写图片描述

答案:B

解析:
因为i从1到n,所以,里层所用的最长的时间为n-1,外层为n-1,所以为(n-1)²,所以时间复杂度为O(n²)


5、
这里写图片描述

答案:B

解析:
因为如果一个数不是素数是合数, 那么一定可以由两个自然数相乘得到, 其中一个大于或等于它的平方根,一个小于或等于它的平方根。

for(int i=3;i<sqrt(n);i=i+2)
{
    if(n%i==0)
}

n整除m等价于m/n;


6、
这里写图片描述

答案:B

解析:
这个带一个数进去,n=10的时候求值,n=100的时候求值,然后做差,看哪个相差的值最小则为谁。
(1)n大于一定值之后,不同的数量级对应的关系:
常见的算法时间复杂度由小到大依次为:

Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)

(2)log2底(n!)=nlog2底n;


总结:

从终止条件入手,如果for循环中嵌套着的变量与外层变量有关的话,可以按照最坏的情况来解决内层循环次数的问题,之后内层循环次数与外层循环次数相乘。
例如:2-2,2-4,2-5;

在这再举一个例子
这里写图片描述
最里层执行最复杂的时间为j(max),外一层执行最长的时间为i(max),最外层执行最长的时间为n次(imax=n),所以依次回推,时间复杂度为O(n的三次方)。


7、

在这里插入图片描述

答案:D


还需再看,递归方面是一个较难理解的过程

二、函数题
1、爆内存函数实例(6 分)

本题要求实现一个递归函数,用户传入非负整型参数n,用户依次输出1到n之间的整数。所谓递归函数就是指自己调用自己的函数。

说明:
(1)递归函数求解问题的基本思想是把一个大规模问题的求解归结为一个相对较小规模问题的求解,
小规模归结为小小规模,以此类推,直至问题规模小至边界(边界问题可直接求解)。递归函数由两
部分组成,一部分为递归边界,另一部分为递归关系式。以求阶乘函数为例,递归边界Factorial(1)=1;
  递归公式: Factorial(n)=n*Factorial(n-1),它对应的递归函数如下:
  int GetFactorial(int n)
{
    int result;
    if(n==1) result = 1;    //递归边界,此时问题答案易知,可直接求解
    else result =n* GetFactorial(n-1);   //递归关系,大问题求解归结为小问题求解
    return result;
}

(2) 发生函数递归调用(自己调用自己)或者普通函数调用时,系统需要保存调用发生前的执行场景信
息(包括调用发生前的各个变量取值信息以及函数执行位置等),以便被调函数执行完毕后可以顺利返
回并继续执行后续操作。每次调用都需要保存一个场景信息,保存这些场景信息需要的辅助空间的大小
与函数调用的次数呈正比,或者说其空间复杂度是O(n),当中n为调用次数。
(3)本例的目的是让学生编写一个递归函数,并在自己的机器上测试递归调用次数达到多少时会发生内存
被爆而出现内存溢出的错误(我办公室机器上设置参数为66000时会溢出)。同样的这个问题,如果不
用递归函数而改用普通的循环语句解决问题,则不会出现内存溢出!

函数接口定义:

void PrintN (long n);

其中n为用户传入的参数。
裁判测试程序样例:

在这里给出函数被调用进行测试的例子。例如:

#include <stdio.h>
void PrintN(long n);

int main()
{
    PrintN(66000L);
    return 0;
}

/* 请在这里填写答案 */

输入样例:

5

输出样例:

12345

答案

#include <stdio.h>
void PrintN(long n);

int main()
{
    PrintN(66000L);
    return 0;
}
void PrintN(long n)
{

    if(n==1)  printf("1");
    else
    {
        PrintN(n-1);
        printf("%d",n);//这里别忘忘记了输出;
    }
}


递归
1、递归就是有去(递去)有回(归来),“有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决,就像上面例子中的钥匙可以打开后面所有门上的锁一样;“有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。在上面的代码中更能显示归来的过程
2、格外重要的是,这个解决问题的函数必须有明确的结束条件,否则就会导致无限递归的情况。
3、递归的三要素
   在我们了解了递归的基本思想及其数学模型之后,我们如何才能写出一个漂亮的递归程序呢?笔者认为主要是把握好如下三个方面:

(1)明确递归终止条件;

(2)给出递归终止时的处理办法;

(3)提取重复的逻辑,缩小问题规模。

1). 明确递归终止条件

我们知道,递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来。换句话说,该临界点就是一种简单情境,可以防止无限递归。

2). 给出递归终止时的处理办法

我们刚刚说到,在递归的临界点存在一种简单情境,在这种简单情境下,我们应该直接给出问题的解决方案。一般地,在这种情境下,问题的解决方案是直观的、容易的。

3). 提取重复的逻辑,缩小问题规模*

我们在阐述递归思想内涵时谈到,递归问题必须可以分解为若干个规模较小、与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。从程序实现的角度而言,我们需要抽象出一个干净利落的重复的逻辑,以便使用相同的方式解决子问题。

https://blog.csdn.net/sinat_38052999/article/details/73303111
4、递归实例
(1)递归求和1+2+3+…+n

public static Integer recursionSum(Integer n)
{
    if(n>0)
    {
        return n+recursionSum(n-1);
    }
    else
    {
        return 0;
    }
}

(2)递归阶乘n! = n * (n-1) * (n-2) * …* 1(n>0)
public static Integer recursionMulity(Integer n)
{
if(n==1)
{
return 1;
}
return n*recursionMulity(n-1);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值