文章来源:http://blog.seclibs.com/数据结构与算法初步认识-2/
什么是数据结构和算法
数据结构和算法经常是结合在一起的东西,数据结构是数据的存储方式,而算法是对数据的具体操作,他们二者是相辅相成的,数据结构是为算法服务的,算法是要作用在特定的数据结构之上的,两者是不可以孤立存在的。
整个列表的内容下来还是非常多的,这里不立什么flag,只是为了给之后所学习的内容做一个更好的铺垫。
数据结构和算法的应用就是为了解决程序的快和省的问题,即运行的够快,占用的空间够少,在评比这两项内容的时候,我们引入了时间复杂度和空间复杂度两个复杂度的概念。虽然说我们可以在写完程序后对程序的时间进行计算和统计等,但那属于事后统计法,有一种事后诸葛亮的感觉,我们需要的是在写程序的时候就对整体的复杂性有一个大概的评估。
这里我们引入的是大O表示法,T(n)=O(f(n)),其中,T(n) 表示代码执行的时间;n 表示数据规模的大小;f(n) 表示每行代码执行的次数总和。因为这是一个公式,所以用 f(n) 来表示。公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比。
然后我们先来说时间复杂度分析,在进行分析的时候,我们只关注跟规模n有关的内容,其余的内容我们默认它们的时间复杂度为O(1),比如
int a=1;
int b=2;
int c=3;
这类型的代码不管在程序有多少,即使是成千上万行,因为它们的数量是固定的,如果在n为无穷大的时候,它们也就可以忽略不计了,所以我们认为它们的时间复杂度为O(1),在计算的时候就可以忽略掉这部分内容了。
那如果是一个简单的循环语句的话,它的时间复杂度就为O(n)
int sum = 0;
int i = 1;
for (; i <= n; ++i) {
sum = sum + i;
}
因为前面的赋值语句在后面的循环的复杂度面前就可以忽略不计了,所以只需要看循环执行的次数即可。
如果是多个语句结合在一起的话就需要仔细进行分析
int cal(int n) {
int sum_1 = 0;
int p = 1;
for (; p < 100; ++p) {
sum_1 = sum_1 + p;
}
int sum_2 = 0;
int q = 1;
for (; q < n; ++q) {
sum_2 = sum_2 + q;
}
int sum_3 = 0;
int i = 1;
int j = 1;
for (; i <= n; ++i) {
j = 1;
for (; j <= n; ++j) {
sum_3 = sum_3 + i * j;
}
}
return sum_1 + sum_2 + sum_3;
}
对于这样的代码,我们可以很清楚的分析出来,三段代码的时间复杂度依次是O(1)、O(n)、O(n²),但因为它们是一个整体的存在,我们选取它们其中最大的一个时间复杂度来作为这段代码所需要的时间复杂度,即O(n²)。
接着我们再来看一段代码
int cal(int n) {
int ret = 0;
int i = 1;
for (; i < n; ++i) {
ret = ret + f(i);
}
}
int f(int n) {
int sum = 0;
int i = 1;
for (; i < n; ++i) {
sum = sum + i;
}
return sum;
}
在这段代码中,我们先看cal函数,如果把f()操作作为一个简单操作,那么它的时间复杂度为O(n),但是f()操作本身的时间复杂度为O(n),所以整体的时间复杂度需要将两个相乘,为O(n²)
紧接着在来一段类似的代码
i=1;
while (i <= n) {
i = i * 2;
}
这段代码看起来跟刚开始说的循环没有太大的却别,但是仔细分辨一下,就会发现不同
如果将i从1开始循环,每次都会将i的值乘2,最后跳出循环的条件应该为2x=n (2的x次方),只要我们求出来x的大小也就知道了这个它的时间复杂度,即O(log2n)(log以2为底,n的对数)
到这里,也就基本能理解时间复杂度的计算方法了
然后来说一下我们常见的几种时间复杂度
在这些时间复杂度中,可以粗略的将它们分成两类,多项式量级和非多项式量级,非多项式量级只有图中画波浪线的两种,当数据规模n越来越大时,非多项式量级的执行时间将会急剧增加,就是我们所谓的指数级爆炸式增长,所以它们的算法是非常低效。
除了上面提到的几种时间复杂度外,还有一种由多个数据规模同时决定的复杂度
int cal(int m, int n) {
int sum_1 = 0;
int i = 1;
for (; i < m; ++i) {
sum_1 = sum_1 + i;
}
int sum_2 = 0;
int j = 1;
for (; j < n; ++j) {
sum_2 = sum_2 + j;
}
return sum_1 + sum_2;
}
可以很容易的看出,第一个的时间复杂度为O(m),第二个时间复杂度为O(n),后面将两个函数的值相加,我们无法轻易的选取其中的一个时间复杂度作为整个模块的时间复杂度,所以它们的时间复杂度为O(m+n)
到这里时间复杂度也就基本说完了,接着说一下空间复杂度的相关内容
这里可以通过类比的方式来说空间复杂度,时间复杂度是执行时间和数据规模之间的增长关系,那么空间复杂度就是存储空间和数据规模之间的增长关系,我们常见到的空间复杂度也就只有 O(1)、O(n)、O(n²)三种,像 O(logn)、O(nlogn) 等一般都是用不到的,所以掌握了时间复杂度的计算方法对空间复杂度也就有大致了解了。
文章首发公众号和个人博客:
公众号:无心的梦呓(wuxinmengyi)
这是一个记录红队学习、信安笔记,个人成长的公众号
记录红队相关学习笔记