时空复杂度

1.算法效率

递归代码———斐波那契数列的代码量十分简洁,所有最优解? 实际上使用递归是错误的,当使用递归计算第40位斐波那契数列会WA,究其原因是进行大量重复的计算。那该如和衡量算法的优劣呢?

#include<iostream>
using namespace std;
int fid(int n){
	if(n>2){
	return fid(n-1)+fid(n-2);
	}
	else return 1;
}
int main(){
	int n=0;
	cin>>n;
	int ret=fid(n);
	cout<<"第"<<n<<"个斐波那契数列是"<<ret;
	return 0;
}
算法的复杂度
  1. 算法在编写程序时会,消耗一定的时间和空间资源。因此,衡量一个算法的优劣,得综合考虑时间和空间的复杂度。
  2. 时间复杂度主要衡量一个算法的运行快慢,空间复杂度主要衡量进行算法运行所需的额外空间。
  3. 相对于时间和空间,因为计算机的高速发展,计算机的存储达到了一个很高的程度,所以影响程序的优劣,仅需关注时间复杂度。

时间复杂度

1.什么是时间复杂度
  1. 计算机科学中,算法的时间复杂度是一个函数,描述了算法的运行时间,分析算法耗费的时间,就是分析时间复杂度,以减少上机测试的次数;
  2. 在算法中花费的时间,跟其中语句的执行次数成正比,算法中的基本操作的执行次数,为算法的时间复杂度。
  3. 找到某条基本语句与问题规模N之间的数学表达式就是算法的时间复杂度;
  4. 程序每执行一条语句的执行时间看做一样的,记为一个时间单元
#inclued<iostream>
using namespace std;
int main(){
	int n=100;
	int a;
	for(int i=0;i<n;i++)
	{
	a++;
	}
	cout<<a<<endl;
return 0;
}
  • 其中赋值语句 ,花费3个时间单元;
  • i<n,循环条件, 花费n+1个时间单元;
  • i++ &&a++; 花费2*n个时间单元;
    T(n)=3n+4;线性关系

赋值语句执行1个时间单元,
循环条件执行n+1个时间单元(最后一次还要比较)
执行循环,n个时间单元

可以看出程序消耗的时间与问题规模N有关,成线性关系;

|问题规模N|1 |10000|
|3n+4|7|30004|n变大时,两者几乎相同,可忽略常数项
| 3n | 3 |30000|
| n | 1 |10000|n变大时,系数的作用变小了(两个相同数量级),可以忽略

所以一般我们会保留最高次项并忽略该项系数;

例如
T(n)=n+1;忽略常数项T(n)–n
T ( n ) = n + n 2 ; 忽略低阶项 T ( n ) − − n 2 T(n) =n+n^2 ; 忽略低阶项T(n)--n^2 Tn)=n+n2;忽略低阶项Tnn2
T(n)=3n;忽略最高阶的系数T(n)–n

  1. 由上我们得出 如何计算时间复杂度呢?
    1.忽略常数项;
    2.忽略系数;
    3.只保留最高项;
    常见的时间复杂度:
    从小到大
    O ( 1 ) < o ( l o g n ) < O ( n ) < O ( n l o g n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) O(1)<o(logn)<O(n)< O(nlogn)< O ( n^2 ) <O(n^3)<O(2^n )<O(n!) O(1)<o(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)
数量级名称
O(1)常数阶
O(logn)对数阶
O(n)线性阶
O(nlogn)线性对数阶
O ( n 2 ) O(n^2) On2平方阶
O ( n 3 ) O(n^3) On3立方阶
O ( 2 n ) O(2^n) O2n指数阶
O ( n !) O(n!) On!)阶乘阶

时间复杂度的应用

1.由题目数据范围判断代码是否会超时(TLE);
2.通过题目数据范围反推算法;yxc博客
p8780;

/*
1秒测评机 会执行 10^9次运算 
1.为什么只有50分?
O(n) = 10^6 执行时间小于1秒 
2.为什么是WA而不是TLE? 
	2.1.超时了,一直跑都没跑出来 WA
	2.2.超时了,但是跑出来了和数据是匹配的 TLE 
3.为什么要算最坏的情况? 
最坏的情况:
a = 1 b = 1 n = 10^18
10^18
基本上每个题都有一个极限的数据范围。如果最坏的情况都不会超时说明程序一定不会超时。 
*/ 
#include<iostream>

using namespace std;
typedef long long ll;//用ll 来表示 long long 
 
int main()
{
	ll a, b, n;
	cin >> a >> b >> n;
	//方法1 会超时 
	/*ll cnt = 0;//统计一下当前刷了多少道题
	//枚举天数 
	for(ll i = 1; ; i ++)
	{
		if(i % 7 == 6 || i % 7 == 0) cnt += b;//如果是6和7的倍数就写b道题
		else cnt += a;
		if(cnt >= n) 
		{
			cout << i << endl;
			break;
		}
	} */ 
	//方法2 首先计算出一个星期的总刷题量。
	ll m = a * 5 + 2 * b;//一个星期的刷题量
	ll ans = 0;//记录一共需要多少天
	if(n % m == 0){
		ans = n / m * 7; 
	} else { //是n不是m的倍数 
	   ans = n / m * 7;//花这么多个星期*7天数 
	   n = n - n / m * m;//n/m*m 得到已经做了多少道题。 
	   
	   for(int i = 1; i <= 7; i ++)
	   {
	   		if(i == 6 || i == 7) n -= b;
	   		else n -= a;
	   		if(n <= 0) {
			   	ans += i;
			   	break;
			   } 
	   } 
	}
	cout << ans << endl;
	return 0;
} 

p2249;

/*
1.有循环
	1.1 单重循环 直接看循环次数
	1.2 多重循环 直接把每重循环次数成乘起来
2.无循环 O(1) 
*/ 

#include <iostream>

using namespace std;

const int N = 1000010;

int a[N];
int main()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++) cin >> a[i];//读入
	
	 
//  不减 包括 1.严格单调递增1 2 3 4 5 6 2.非严格单调递增的 1 1 2 2 3 5 6 
	//10^9
	//1.为什么10^9会超时
	//O(n) 系数n 
	//处理m个询问 
	//暴力时间复杂度是O(nm) n=10^6 m=10^5 nm = 10^11 1s 10^9  所以O(nm)一定会超时 
	while(m --) //外层的O(m)是必定的,优化不了 所以我们考虑优化循环里面的操作。 
	{	
		//查找一个数出现的第一个下标 暴力O(n) 正解:二分查找O(log n) 
		int x;
		cin >> x;//带查找的数x 
		int l = 1, r = n;
		while(l < r)
		{
			int mid = l + r >> 1;//取中间点 mid = (l + r) / 2 
			if(a[mid] >= x) r = mid;
			else l = mid + 1;
		}
		if(a[l] == x) cout << l << " ";
		else cout << -1 << " ";
	}
	//正解的时间复杂度:O(mlogn) = 10^5 * log 10^6 = 2*10^6 
	// 2^10 = 1024 约等于 1000=10^3 
	//log 10^6 = 20次 
	return 0;
}

通过合理的算法来,对代码进行优化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值