各算法时间复杂度总结—持续更新

来源:互联网 发布:变声器男变女甜美软件 编辑:程序博客网 时间:2024/06/04 00:46

这篇文章写得不会太好,毕竟我只是个小白,写来仅供参考,自己理顺一下时间复杂度方面的一些东西,大家可以到一些大牛那看到更加严密的证明方法。敲打

 

我越来越发现,学ACM,算法是学多了,但是发现却越来越做不出题目了,因为水题开始变少,数据量开始变大,如果不打好分析题目的基础的话,就算你看得懂题,却已经落后大牛们一个level了。

 

比如,给你100000个数字,求和,每个数字的范围是-10000到10000,看似很简单,但是1e6 * 1e4 = 1e10,一百亿,int型整数是无法储存这么大的数字的。所以要上long long。当然,你所有题目都用long long,该过得过,本来就过不了的就要用字符型什么的,好像也无可厚非。不过分析问题的数据才是关键,无脑用long long并不能代替这种能力。

 

时间复杂度其实要怎么分析的呢?时间复杂度是遵循一个原则的,就是按照数据最大的那个数去得出结论。我们做题通常都是看见:题目给出多种情况,T<=10。最后计算的时候直接将T=10带入。

时间复杂度其实也是见仁见智的,因为我们一般只找复杂度高的那一部分,比如平方级别,指数级别,这些都放心的话,线性级别的读入啊,遍历啊,都是可以忽略的。

 

现在要来说一说OJ服务器的运算量了。一般遇到的题目都是1S的,1S的话跑一百万的数据是绝对可以的,一千万的话一般都可以过,但是到了一亿,除非是超级简单的逻辑判断,否则基本过不了了。

 

我现在主攻图论最短路的题目,就先从图论的算法开始说说。

Dijkstra点对点最短路:

    for(i=1;i<=n;i++) {        min=MAX;        for(j=1;j<=n;j++)        {            if(!mark[j] && dist[j]<min)            { //取出不在mark里的最小的dist[i]                min=dist[j];                pos=j;//标记            }        }                    if(min==MAX)//已经不能通了            break;        mark[pos]=1;//把K加进来        //做松弛操作        for(j=1;j<=n;j++)        {            if(!mark[j] && dist[j]>dist[pos]+map[pos][j]) //start->j or start->pos,pos->j             {                dist[j]=dist[pos]+map[pos][j];//这步跟prim算法有点不同            }        }    }

这个是一个模板,是这个算法最核心的部分,其中n代表有多少个点,所以复杂度是O(2*n²),其余线性的部分可以忽略不计。但是其实dijkstra是可以优化的,百度上也有说,使用斐波那契堆这些优化,但是本小白还不怎么看得懂堆得东西。

 

Bellman—Ford单源最短路算法(可带负权,但不能有负权环)

bool Bellman_Ford(){for(int i = 1; i <= nodenum; ++i) dis[i] = (i == original ? 0 : MAX);for(int i = 1; i <= nodenum - 1; ++i)for(int j = 1; j <= edgenum; ++j)if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) {dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;pre[edge[j].v] = edge[j].u;}bool flag = 1; //判断是否含有负权回路for(int i = 1; i <= edgenum; ++i)if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost){flag = 0;break;}return flag;}

nodenum就是点的个数,edgenum就是边的个数,从上面这段代码可以明显看出,时间复杂度应该为O(VE+E),V为点的个数,E为边的个数。这种算法有个很严重的问题,就是冗余量太大,进入两个for循环那部分很多时候都是无法操作的。所以有了一种算法叫SPFA,用队列和一个数组标记来去掉那么多冗余的部分。

SPFA单源最短路算法(可带负权,不可有负环)

这种算法的话,复杂度理论上说是O(K*E)k是进队列的次数,E是边数,但是百度上也说了,这种算法在国际上是不被认可的,他最坏的情况依然会回到Bellman的复杂度。

不过我觉得还是要支持下得,毕竟是国人提出的一种算法,而且你说快排最快可以达到nlgn的复杂度,慢也可以慢到n²的复杂度,但也是要用的,毕竟大多数情况是优化成功的。


 

Floyd-Warshall全局最短路算法:

void floyd_warshall(int n){    int i,j,k;    for (k=1;k<=n;k++)        for (i=1;i<=n;i++)            for (j=1;j<=n;j++)                if (mat[i][k] + mat[k][j] < mat[i][j])                mat[i][j] = mat[i][k] + mat[k][j];           }
这个复杂度就很高了,n依然是代表有多少个点,时间复杂度高达O(n^3)。这样可以算算,当有1000个点的时候,运算量是1e9,也就是10亿,适用范围就很局限了。



0 0
原创粉丝点击