关于C++算法中的时间复杂度问题(刷题中的个人见解)

究竟什么是时间复杂度???

时间复杂度就是用来方便开发者估算出程序的运行时间,我们该如何估计程序运行时间呢,我们通常会估计算法的操作单元数量,来代表程序消耗的时间。用一个不规范的话来说(我的个人见解):时间复杂度就是自己写的程序与输入数据的一个方程式;假设给定的数据是n,你的时间复杂度就是f(n),时间复杂度可能会根据你的数据大小进行变化,通常是呈指数型递增,当然也会有按比例递增,具体还是要看代码。

关于时间复杂度的概念,百度百科中是这样给我们解释的:

算法题中的时间复杂度:

在算法题中,基本上所有的题都会对时间和空间进行要求,但是其中一些题是对时间复杂度没有过多要求的,基本都是1s,比如一些模拟算法题,即使使用暴力也能过;但是对于一些考察算法的题目,就对时间的要求比较高,比较基础一点的就是素数筛快速排序、插入排序;由于给定的数据范围比较大,按照常规的思路来进行代码实现,那么在遇到比较大的数据时,在给定的1s内是不能够计算出来的。因此也就有了各种的算法,就是根据数学规律,用更少的时间计算出结果。

通过素数筛我们就能很直观的感受到时间复杂度(本蒟蒻在刚开始学习算法的时候对时间复杂度的理解就是通过筛法)。

常规的判断素数:

只是通过素数的定义来判断当前数字是否为素数。

#include<stdio.h>
int main()
{
    int i,n;
    scanf("%d",&n);
    for(i=2;i<n;i++)
    {
        if(n%i==0)
        break;
    }
        if(i>=n && n!=1) 
        printf("Yes");
        else
        printf("No");
    return 0;
}

欧拉筛法:

通过素数的关系:比一个合数大的质数和该合数的乘积可用一个更大的合数和比其小的质数相乘得到

//欧拉筛法
//时间复杂度O(n)
//和埃氏筛法的区别是对于每一个要筛除的数,欧拉筛法只筛除一次
#include<stdio.h>
#define MAX 100
int check[MAX];  //对数字进行标记
int prime[MAX];  //存放素数
int tot = 0;
void euler();
int main()
{
    euler();
    for (int i = 1; i <= tot; ++i)
        printf("%d\n", prime[i]);
    return 0;
}
void euler() 
{
    for (int i = 2; i <= MAX; ++i) 
    {
        if (!check[i]) {  //若为素数则添加进prime[]  ||  if (!check[i]) == if(check[i]==0)
            prime[++tot] = i;
        }
        //进入下面循环的数字i为合数
        for (int j = 1; j <= tot && i * prime[j] <= MAX; ++j) {
            check[i * prime[j]] = 1;
            //合数标为1,同时,prime[j]是合数i*prime[j]的最小素因子
            if (i % prime[j] == 0)
                break;
            //即比一个合数大的质数和该合数的乘积可用一个更大的合数和比其小的质数相乘得到
        }
    }
}

通过素数筛的例子我们应该就对时间复杂度有了一个基本的概念,而在我们学习算法的过程中,涉及到的节省时间的算法还是比较多的(太多了根本学不过来),包括还有深搜宽搜,以及动态规划算法... ...

由数据范围反推算法复杂度及算法内容

在c++中,1s可以进行多少运算量呢?

在写算法的过程中,我个人的理解是1s大概可以进行1亿次运算,所以在刷题的时候,如果给的测试数据范围比较大的情况下,在完成代码实现之后我会考虑一下时间复杂度问题考虑数据最大值,然后带入代码中去,计算一下可能会出现的最多的运行次数,如果大于该题给定的时间,这时就需要考虑两种情况了。1、若可能的最多运行次数超出了题目给定的时间但很少,那么这时就需要考虑对代码进行剪枝。2、若可能的最多运行次数超出了题目给定的时间比较多,那么应该就需要换一种思路,这就需要我们对各种算法的了解,采取更加节省时间的算法。

在不同数据范围下,代码的时间复杂度和算法该如何选择:

n<=1000=>O(n2),O(n2logn)=>dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford
n<=10000=>O(n*根号n),块状链表、分块、莫队
n<=100000=>O(nlogn)=>种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、Kruskal、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树
n<=1000000=>O(n),以及常数较小的 O(nlogn)算法 => 单调队列、 hash、双指针扫描、并查集,kmp、AC自动机,常数比较小的 O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa
n<=10000000=>O(n),双指针扫描、kmp、AC自动机、线性筛素数
n<=109=>O(根号n),判断质数
n<=1018=>O(logn),最大公约数,快速幂,数位DP
n<=101000=>O((logn)2)高精度加减乘除
n<=10100000=>O(logk*loglogk),k表示位数,高精度加减、FFT/NTT
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值