❤️一文搞懂算法时间复杂度❤️
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️一文搞懂算法时间复杂度❤️相关的知识,希望对你有一定的参考价值。
文章目录
一、前言
很多人觉得算法难,是因为被困在了时间和空间这两个维度上。如果不考虑时间和空间的因素,其实我们可以把所有问题都通过穷举解决,也就是你告诉计算机你要做什么,然后通过它强大的算力帮你计算。
今天,我就和大家来聊一下时间复杂度。
二、穷举
1、小试牛刀
- 所谓穷举,也就是我们通常所说的枚举,就是把所有情况都遍历了的意思。举个最简单的例子:
【例1】给定 n ( n ≤ 1000 ) n(n \\le 1000) n(n≤1000) 个元素 a i a_i ai,求其中是偶数的数的个数。
- 判断一个数是偶数还是奇数,只需要求它除上 2 的余数是 0 还是 1,那么我们把所有数都判断一遍,并且对符合条件的情况进行计数,最后返回这个计数器就是答案,这里需要遍历所有的数,这就是穷举。如图二-1-1所示:
图二-1-1 - c++ 代码实现如下:
int countEven(int n, int a[]) {
int cnt = 0;
for(int i = 0; i < n; ++i) {
if(a[i] % 2 == 0)
++cnt;
}
return cnt;
}
2、乘胜追击
- 经过上面的例子,相信读者对穷举已经有一定的理解,那么我们来看看稍微复杂一点的情况。
【例2】给定 n ( n ≤ 1000 ) n(n \\le 1000) n(n≤1000) 个元素 a i a_i ai,求有多少个二元组 ( i , j ) (i,j) (i,j),满足 a i + a j a_i + a_j ai+aj 是偶数 ( i < j ) (i \\lt j) (i<j)。
- 秉承穷举的思想,并且这里需要两个变量
i
i
i 和
j
j
j,所以可以枚举
a
i
a_i
ai 和
a
j
a_j
aj,再对
a
i
+
a
j
a_i + a_j
ai+aj 进行奇偶性判断,所以很快设计出一个利用穷举的算法。如图二-2-1所示:
图二-2-1 - c++ 代码实现如下:
int countEvenPair(int n, int a[]) {
int cnt = 0;
for(i = 0; i < n; ++i) {
for(j = i+1; j < n; ++j) {
if( (a[i] + a[j]) % 2 == 0)
++cnt;
}
}
return cnt;
}
3、举一反三
- 经过这两个例子,是不是对穷举已经有点感觉了?那么,我们继续来看下一个例子。
【例3】给定 n ( n ≤ 1000 ) n(n \\le 1000) n(n≤1000) 个元素 a i a_i ai,求有多少个三元组 ( i , j , k ) (i,j,k) (i,j,k),满足 a i + a j + a k a_i + a_j + a_k ai+aj+ak 是偶数 ( i < j < k ) (i \\lt j \\lt k) (i<j<k)。
- 给 10 秒倒计时,思考一下!
图二-3-1 - 相信聪明的你也已经猜到了,直接给出代码:
int countEvenTriple(int n, int a[]) {
int cnt = 0;
for(i = 0; i < n; ++i) {
for(j = i+1; j < n; ++j) {
for(int k = j+1; k < n; ++k) {
if( (a[i] + a[j] + a[k]) % 2 == 0)
++cnt;
}
}
}
return cnt;
}
- 要求多少个元组,就是枚举多少层循环。
完全懂了的你
4、返璞归真
【例4】给定 n ( n ≤ 1000 ) n(n \\le 1000) n(n≤1000) 个元素 a i a_i ai 和一个整数 k ( k ≤ n ) k (k \\le n) k(k≤n),求有多少个有序 k k k 元组,满足 它们的和 是偶数。
- 一层循环,两层循环,三层循环, k k k 层循环?
- 我们需要根据 k k k 的不同,决定写几层循环, k k k 的最大值为 1000,也就意味着我们要写 1000 的 i f e l s e if \\ else if else。
- 显然,这样是无法接受,比较暴力的做法是采用到递归,c++ 代码实现如下:
int dfs(int n, int a[], int start, int k, int sum) {
if(k == 0)
return sum % 2 ? 0 : 1; // (1)
int s = 0;
for(int i = start; i < n; ++i)
s += dfs(n, a, i+1, k-1, sum + a[i]); // (2)
return s;
}
- 1)
dfs(int n, int a[], int start, int k, int sum)
这个函数的含义是:给定 n n n 元素的数组 a [ ] a[] a[],从下标 s t a r t start start 开始,选择 k k k 个元素,得到的和为 s u m sum sum 的情况下的方案数,当 k = 0 k=0 k=0 时代表的是递归的出口; - 2)当前第 i i i 元素选择以后,剩下就是从 i + 1 i+1 i+1 个元素开始选择 k − 1 k-1 k−1 个的情况,递归求解。
- 我们简单分析一下, n n n 个元素选择 k k k 个,根据排列组合,方案数为: C n k C_n^k Cnk,当 n = 1000 , k = 500 n=1000,k=500 n=1000,k=500 时已经是天文数字,这段代码是完全出不了解的。
当然,对于初学者来说,这段代码如果不理解,问题也不大,只是为了说明穷举这个思想。
充满求知欲的你
三、时间复杂度
1、时间复杂度的表示法
1、时间函数
- 时间复杂度往往会联系到一个函数,自变量表示规模,应变量表示执行时间。
- 这里所说的执行时间,是指广义的时间,也就是单位并不是 “秒”、“毫秒” 这些时间单位,它代表的是一个 “执行次数” 的概念。我们用 f ( n ) f(n) f(n) 来表示这个时间函数。
2、经典函数举例
- 在【例1】中,我们接触到了单层循环,这里的 n n n 是一个变量,随着 n n n 的增大,执行次数增大,执行时间就会增加,所以就有了时间函数的表示法如下:
-
f
(
n
)
=
n
f(n) = n
f(n)=n
图三-2-1 - 这个就是最经典的线性时间函数。
- 在【例2】中,我们接触到了双层循环,它的时间函数表示法如下:
-
f
(
n
)
=
n
(
n
−
1
)
2
f(n) = \\frac {n(n-1)} 2
f(n)=2n(n−1)
图三-2-2 - 这是一个平方级别的时间函数。
- 在【例3】中,我们接触到了三层循环,它的时间函数表示法如下:
-
f
(
n
)
=
n
(
n
−
1
)
(
n
−
2
)
6
f(n) = \\frac {n(n-1)(n-2)} 6
f(n)=6n(n−1)(n−2)
图三-2-3 - 这是一个立方级别的时间函数。
3、时间复杂度
- 一个算法中的语句执行次数称为语句频度或时间频度。记为 T ( n ) T(n) T(n)。
- 并且我们有一个更加优雅的表示法,即:
- T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))
- 其中 O O O 念成 “大O”;
- 1)当 f ( n ) = n f(n) = n f(n)=n,我们称这个算法拥有线性时间复杂度,记作 O ( n ) O(n) O(n);
- 2)当
f
(
n
)
=
n
(
n
−
1
)
2
f(n) = \\frac {n(n-1)} 2
f(n)=2n(n−
以上是关于❤️一文搞懂算法时间复杂度❤️的主要内容,如果未能解决你的问题,请参考以下文章 以❤️简单易懂❤️的语言带你搞懂有监督学习算法附Python代码详解机器学习系列之KNN篇
❤️《画解数据结构》全网最全队列总结,九张动图搞懂队列 ❤️(文末有投票)
❤️《画解数据结构》全网最清晰哈希表入门,三张动图搞懂哈希 ❤️(建议收藏)