数据结构与算法——复杂度(上)
虽然我之前也学习了数据结构与算法,但是也是感觉没有将知识系统化,因此,我会将自己的知识边学习边以博客的形式总结并分享给大家,在学习的过程中,如果大家发现错误,欢迎大家指出并讨论。
基础知识
数据结构: 数据结构其实就是一组数据的存储方式。
算法:算法是对数据结构进行的一组操作。
数据结构与算法之间是相辅相成的,不能单独的讨论数据结构与算法。数据结构是为算法而服务的,而算法也是针对于不同的数据结构的。比如,对一组有序的数据进行查找,如果我们使用数组进行存储,我们可以使用二分查找,但是如果我们使用链表这种数据结构,二分查找就不能使用了,因为数组的结构天然的支持随机存取,但是链表并不支持。
内容梳理
学习数据结构与算法,主要是包含以下三个重要的内容:
① 复杂度分析
② 常用的数据结构
③ 常用的算法
数据结构与算法有很多,但是对于大部分的情况下,常使用的并不多,因此我只讲叙常用的数据结构与算法。
10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树;
10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。
复杂度(上)
首先我们来讲一下复杂度,因为复杂度是数据结构与算法的精髓,占据了数据结构与算法的“半壁江山”。那我们就来讲一讲它是为何出现以及为何如此重要。
数据结构与算法这门课出现的目的就是:如何最省最快的存储以及处理数据。 那么我们写完代码之后如何来知道自己使用了都少的时间与空间。大家可能最开始想到的就是运行代码,计算它所用的时间与空间,这种方法叫做事后统计法。
这种方法存在着以下几种缺点:
① 依赖于测试环境
对于使用不同的硬件,代码的执行速度是不同的,比如说我们使用i9与i3的处理器分别执行同一段代码,我们可以明显的知道i9的执行速度比i3高。
② 测试结果受数据规模的影响
比如,对于小规模的数据,插入排序可能比快速排序更快(不懂的话等到讲到排序算法时我会详细讲述)
因此,我们需要一个不用具体的测试数据就可以粗略的估计代码的执行效率。这就是我们今天要将的时间、空间复杂度。
大O复杂度
如何能够在不执行代码的情况下,肉眼的得出一段代码的复杂度呢?
举例:从1,2, 3,…n的累加和
int cal(int n){
int i = 1;
int sum = 0;
for(; i <= n; i++){
sum += i;
}
return sum;
}
对于代码的执行过程,我们简单的可以认为cpu读取指令,运算然后将结果写回内存。对于每一条代码,它的执行时间时不同的,但是我们为了粗略的估计,我们假设,每一条代码它的执行时间是相同的,都是单位时间unit。在该假设的情况下我们分析以下,这段代码会执行多长的时间。
第二、三行只会执行一次,而四、五行会各自执行n次。那么这段代码具体的执行时间是T(n)=(2n+2)unit。可以看出代码的执行时间与数据的规模成正比。
然后我们再来看一个例子:
int cal(int n){
int i = 1;
int j = 1;
int sum = 0;
for(; i <= n; i++){
j = 1;
for(; j <= n; j++){
sum += i * j;
}
}
return sum;
}
我们可以看到这段代码2,3,4行都只执行了一次,5,6行执行了n次,而7,8行执行了n2次,那么这段代码总的执行时间为T(n)=(2n2+2n+3)unit时间。根据以上两个例子,我们可以看到所有代码的执行时间与数据的规模n成正比。
我们将这个规律总结为一个公式:
T(n) = O(f(n))
其中T(n)表示代码的执行时间,f(n)是每行代码执行时间的总和。
大O复杂度的特点
1、一般情况下大O复杂度中的常数、低阶、系数都可以忽略,因为在数据量n足够大的时候,它们并不左右时间的变化。比如:O(2n2+2n+3)=O(n2)、O(2n+2)=O(n)
2、加法法则
int cal(int n){
int sum = 0;
int i = 1;
for(; i <= 100; i++){
sum += i;
}
int k = 1;
for(; k<= n; k++){
sum += k;
}
int j = 1;
for(; j <= n; j++){
int m = 1;
for(; m <= n; m++){
sum += j * m;
}
}
return sum;
}
这段代码的时间复杂度为O(1)+O(n)+O(n2)=O(n2)
int cal(int m, int n){
int sum = 0;
int i = 1;
int j = 1;
for(; i <= m; i++){
sum += i;
}
for(; j <= n; j++){
sum += j;
}
return sum;
}
时间复杂度:O(m) + O(n) = O(m + n)
3、乘法法则
int cal(int n){
int i = 1;
int sum = 0;
for(; i <= n; i++){
sum += i * f(i);
}
return sum;
}
int f(int m){
int j = 1;
int sum = 0;
for(; j <= m; j++){
sum += j;
}
return sum;
}
这段代码的时间复杂度是O(m)O(n) = O(mn)
常见的时间复杂度:O(1), O(logn), O(n),O(nlogn),O(n2),O(n3),…
学会了时间复杂度,空间复杂度也是类似的,我就不展开讲了,今天的学习就到这里啦~