数据结构和算法的重要性相信学计算机的人都知道,一直以来算法和数据结构就是我的心病,相信和大家一样 这门课要说学吧,太难,自己啃不下来。但是不学吧,感觉又好像怎么着似的,弄的人心理不舒服。今在某软件上花重金买了一门《数据结构和算法》课,鼓起勇气认真的学习一次算法和数据结构,希望能对自己有所帮助。
数据结构和算法本身解决的是“快”和“省”的问题,即如何让自己写的程序运行的更快,如何让程序能够更加节省空间。执行效率是算法一个非常重要的老师指标。那么如何衡量程序的执行效率呢?
为什么:程序的执行时间和占用的内在大小可以通过将程序运行一次而得到,一些数据结构和算法书中把这种方法叫做事后统计法。虽然这种方法是可行的,但是这种统计方法也有很大的局限性。
因此,我们需要通过时间和空间复杂度分析方法来更加合理的了解程序。
大O复杂度:算法的执行效率,粗略的讲,就是指算法的程序执行的时间。
下面来看一段程序:
粗糙的估计,假设CPU执行第行程序的时间都是一样的,为单位时间。因此通过分析可知上面这段程序的总的执行时间T(n)=2n+2。因此可看出,代码的执行时间T(n)与每行代码的执行次数成正比。
依然假设每行的执行时间为单位时间,整体代码的总的执行时间T(n)=(2n2+2n+3)
虽然不知道单位时间的具体值是多少 ,但是可以得出一个结论:所有代码的执行时间T(n)与每行代码的执行次数n成正比。
此刻,大O登场:
T(n) = O(f(n))
其中,T(n)表示代码的执行时间,n表示数据规模,f(n)表示每行代码执行的次数总和,O表示代码的执行时间T(n)与f(n)表达式成正比。
第一个例子中 T(n) = O(2n+2),第二个例子中 T(n) = O(2n2+2n+3)。这就是大O时间复杂度表示法,它并不是代码的真正的执行时间,而是代码执行时间随数据规模增长的变化的趋势,也叫渐进时间复杂度,简称时间复杂度。
当n很大时,例如1000,10000。而公式中的低阶、常量、系数并不影响增长的趋势,所以都可以忽略,我们只需要记一个最大量级就可以了。因此以上两段程序的时间复杂度为T(n) = O(n) ;T(n) = O(n2)
时间复杂度分析:分析一段代码的时间复杂度,这里介绍三种方法:
2.加法法则:总复杂度等于量级最大的那段代码的复杂度
int cal(int n) {
int sum_1 = 0;
int p = 1;
for (; p<100;++p) {
sum_1 = sum_1 + p;
}
"以上代码循环执行100次执行时间为一个常量,与n的规模无关"
int sum_2 = 0;
int q = 1;
for (; q<n;++q) {
sum_2 = sum_2 + q;
}
"复杂度为O(n)"
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
}
"复杂度为O(n)"
}
}
return sum_1 + sum_2 +sum_3
}
所以这段代码总的时间复杂度为O(n2)
3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。
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;
}
以上程序函数f嵌套在函数cal中,在函数cal()中,先假设函数f()为一个普通操作,此时函数cal()的时间复杂度为O(n),而函数f的时间复杂度为O(n),所以整个cal()函数的时间复杂度为O(n2)。
常见的时间复杂度实例分析:
其中,1-5为多项式量级;6、7为非多项式量级。当数据规模n起来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法是非常低效的算法。
i = 1;
while (i <=n) {
i = i * 2
}
为了计算时间复杂度,现需要计算第三行程序被执行了多少次。20 * 21 * …2k=n, 2x = n. 所以,代码的时间复杂度为O(log2 n)
若把代码中的2换成3,则时间复杂度变为O(log3 n)。实际上,不管是以几为底,可以把所有对数时间复杂度都记为O(log 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
}
因为m、n都表示两个数据规模,且我们无法事先评估它们的大小,所以在表示复杂度时不能简单的利用加法法则,活力掉其中的一个,所以。上述代码的时间复杂度为O(m+n)
空间复杂度:全称渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系。
void print(int n) {
int i = 0;
int[] a = new int[n]
for (i;i<n;++i) {
a[i] = i * i;
}
for (i = n-1; i>=0; --i) {
print out a[i]
}
}
第二行代码申请了一个空间存储变量i,但是它是常量阶的,和数据规模n没有关系,可忽略。第三行申请了一个大小为N的int类型数组,其余代码都有占用更多的空间,所以这段代码的空间复杂度为O(n)。
常见的空间复杂度为O(1)、O(n)、O(n2)。
常见的复杂度并不多,从低阶到高阶有O(1)、O(log n)、O(n)、O(n*log n)、O(n2)
对于复杂度分析法则,看到网友做了一个很不错的总结,这里列出来 :