基本概念
什么是算法
算法是独立存在的一种解决问题的方法和思想,与程序和语言没有必然关系,任何语言都可以用来描述算法的实现。
算法的五大特性
- 输入:0个或多个输入
- 输出:至少1个输出
- 有穷性:算法在有限的步骤之后会自动结束,不会无限循环,且每一步可以在可接受的时间内完成
- 确定性:算法中的每一步都有确切含义,不存在二义性
- 可行性:算法可以使用计算机语言来实现
算法实例
问题
如果a+b+c=1000,且 a 2 + b 2 = c 2 a^{2}+b^{2}=c^{2} a2+b2=c2(a、b、c为自然数),如何求出所有a、b、c可能的组合?
解法一(穷举)
// 本地运行162.42s
import time
start_time = time.time()
for a in range(0,1001):
for b in range(0,1001):
for c in range(0,1001):
if a + b + c == 1000 and a**2 + b**2 == c**2:
print("a,b,c=",a,b,c)
end_time = time.time()
print("time:%.2f"%(end_time-start_time))
解法二(优化)
// 本地运行1.55s
import time
start_time = time.time()
for a in range(0,1001):
for b in range(0,1001):
c = 1000 - a - b
if a + b + c == 1000 and a**2 + b**2 == c**2:
print("a,b,c=",a,b,c)
end_time = time.time()
print("time:%.2f"%(end_time-start_time))
时间复杂度
相同的代码运行在不同的平台上会有不同的效率,所以用时间来衡量一段代码的执行效率是不可靠的。
因此提出了时间复杂度与“大O记法”,用来衡量代码执行基本运算的数量。
通常把一行代码作为一步,不进行继续细分。并将步骤进行进一步抽象,得到与问题规模N有关的复杂度。
时间复杂度计算步骤
- 基本操作,时间复杂度为O(1)
- 顺序结构,时间复杂度按加法计算
- 循环结构,时间复杂度按乘法计算
- 分支结构,时间复杂度取最大
- 只关注最高次项,其它次要项和常数忽略
- 在没有特殊说明时,时间复杂度通常指最坏时间复杂度
常见时间复杂度
执行次数 | 时间复杂度 | 非正式术语 |
---|---|---|
12 12 12 | O ( 1 ) O(1) O(1) | 常数阶 |
2 n + 3 2n+3 2n+3 | O ( n ) O(n) O(n) | 线性阶 |
3 2 + 2 n + 1 3^{2}+2n+1 32+2n+1 | O ( n 2 ) O(n^{2}) O(n2) | 平方阶 |
5 l o g 2 n + 2 = 5log_{2}n+2= 5log2n+2= | O ( l o g n ) O(logn) O(logn) | 对数阶 |
2 n + 3 n l o g 2 n + 19 2n+3nlog_{2}n+19 2n+3nlog2n+19 | O ( n l o g n ) O(nlogn) O(nlogn) | nlog阶 |
6 n 3 + 2 n 2 + 3 n + 4 6n^{3}+2n^{2}+3n+4 6n3+2n2+3n+4 | O ( n 3 ) O(n^{3}) O(n3) | 立方阶 |
2 n 2^{n} 2n | O ( 2 n ) O(2^{n}) O(2n) | 指数阶 |
常见复杂度之间的关系
O
(
1
)
<
O
(
l
o
g
n
)
<
O
(
n
)
<
O
(
n
l
o
g
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O(1) < O(logn) < O(n) < O(nlogn) < O(n^{2}) < O(n^{3}) < O(2^{n}) < O(n!) < O(n^{n})
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
时间复杂度计算(上文解法一)
T = 1000*1000*1000*2
T(n) = n^3*2
g(n) = n^3 相比T函数,省去了系数和常数,称为T函数的渐进函数,也就是该程序的大O表示法
g(n) = O(n^2)
// T = 1000*1000*1000*2
// T = n*n*n*2
// T(n) = n^3*2
// g(n) = n^3 相比T函数,省去了系数和常数,称为T函数的渐进函数,也就是该程序的大O表示法
for a in range(0,1001):
for b in range(0,1001):
for c in range(0,1001):
if a + b + c == 1000 and a**2 + b**2 == c**2:
print("a,b,c=",a,b,c)
最坏时间复杂度
数据的不同,常常决定了时间复杂度的不同。
例如对两个列表升序排序:
a = [5,4,3,2,1]
b = [1,2,3,4,5]
时间复杂度一定是不同的。
因此,算法存在:
- 最优时间复杂度(通常没有意义)
- 最坏时间复杂度(重点关注,提供了保证)
- 平均时间复杂度(不关注)