数据结构与算法 之 复杂度(python版)

Posted Coderusher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法 之 复杂度(python版)相关的知识,希望对你有一定的参考价值。

@[toc]

一、前言

1984年被授予图灵奖的计算机编程领域的祖师爷 尼古拉斯•威茨(Niklaus Wirth),有一句名言在计算机领域人尽皆知,那就是:

关于公式中的三个元素含义可以对照下表

元素 含义
程序 特定问题解决方案的具体实现
算法 解决特定问题的有限求解步骤(思想)
数据结构 数据与数据之间的结构(逻辑)关系

关于这句名言有不少争论,感兴趣的可以移步: "算法+数据结构=程序"过时了吗?

二、正文

1 复杂度

1.1 什么是复杂度?

公式表示:

其中:

字符 含义
$T_(n)$ 表示代码执行的时间
$n$ 表示数据规模大小
$f_(n)$ 表示每行代码执行的次数总和
$O(f_(n))$ 表示代码的执行时间T(n)与f(n)表达式成正比
  • 举个栗子
def sumOfN(n):
    theSum = 0
    for i in range(1, n+1):
        theSum += i
    return theSum

上述代码的目的是为了计算前 n 个整数累计求和的结果,怎么计算复杂度呢?

在python中,度量复杂度的指标可以选取赋值语句的执行次数

则有

当计算规模 $n -> ∞$ ,得到该代码的复杂度为:

可以看出, $T(n)$ 的精确值并不重要,最终决定 $T(n)$ 的是增速最快的主导部分。

那么,下式的复杂度为多少呢?

很显然,答案是: $O_(n^2)$

1.2.复杂度分析法则

  1. 单段代码看高频:比如循环。
  2. 多段代码取最大:比如一段代码中有单循环和多重循环,那么取多重循环的复杂度。
  3. 嵌套代码求乘积:比如递归、多重循环等
  4. 多个规模求加法:比如方法有两个参数控制两个循环的次数,那么这时就取二者复杂度相加。

1.3 常见的大O数量级函数

  • 当 n 较小时,难以确定其数量级
  • 当 n 增长到较大时,容易看出其变化数量级
$f(n)$ 名称
$1$ 常数
$log(n)$ 对数
$n$ 线性
$n*log(n)$ 对数线性
$n^2$ 平方
$n^3$ 立方
$2^n$ 指数

变化曲线如图

1.4 变位词

为了对于空间复杂度有更好的理解,下面引出 ”变位词“

例如 heart和earth,python和typhon

现在有一个题目,要求:写一个函数,以两个词作为参数,返回这两个词是否为变位词

解法1:逐字检查

  • 实现思路

  • 具体代码
    
    def anagramSolution1(s1,s2):
    alist = list(s2)  # 复制s2到列表
    pos1 = 0
    still0K = True
    while pos1 < len(s1) and stillOK:  # 循环s1的每个字符
        pos2 = 0
        found = False
        while pos2 < len(alist) and not found:
            if s1[pos1] == alist[pos2]:  # 在s2逐个对比
                found = True
            else:
                pos2.=pos2.+.1
                if found:
                    alist[pos2] = None  # 找到,打勾
                else:
                    stillOK = Falsepos1 = pos1 +1  # 未找到,失败
    return still0K

print(anagramSolution1( abcd , dcba ) )


- 代码规模计算:

> 外层循环遍历s1每个字符,将内层循环执行n次而内层循环在s2中查找字符,每个字符的对比次数,分别是1、2…n中的一个,而且各不相同

两重**循环**,使用乘积来进行计算,所以总执行次数为:

> $∑_i=1^n i = n(n+1)/2 = 1/2 n^2 + 1/2 n -> O(n^2)$

#### 解法2:排序比较

- 实现思路
> 将两个字符串都按照字母顺序排好序再逐个字符对比是否相同,如果相同则是变位词有任何不同就不是变位词

![image.png](https://s2.51cto.com/images/20220827/1661585455903920.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)

- 具体代码
```python
def anagramSolution2(s1,s2):
    alist1 = list(s1) # 转为列表
    alist2 = list(s2)
    alist1.sort( )  # 分别排序
    alist2.sort( )
    pos = 0
    matches = True
    while pos < len(s1) and matches:
        if alist1[pos] == alist2[pos]:
            pos = pos + 1
        else:
            matches = False  # 逐个对比
    return matches

print(anagramSolution2(  abcde , edcba ) )
  • 代码规模计算:

解法3:暴力枚举

  • 实现思路

暴力枚举往往算作下策,因为 $n!$ 的增长速度是大于 $2^n$ 的

因此暴力枚举恐怕不能算是个好算法

解法4:计数比较

  • 实现思路
def anagramSolution4(s1, s2):
    c1 = [0]* 26
    c2 = [0]* 26
    for i in range(len(s1) ):  # 分别都计数
        pos = ord( s1[i]) - ord( a )
        c1[pos] = c1[pos] +1
    for i in range(len(s2) ):
        pos = ord(s2[i])_- ord ( a)
        c2[pos] = c2 [pos] +1
    j = 0
    still0K = True  # 计数器比较
    while j < 26 and still0K:
        if c1[j] == c2[j]:
            j = i + 1
        else:
            stillOK = False
    return still0K

print(anagramSolution4( " apple, pleap  ))
  • 代码规模计算:

前两个循环用于对字符串进行计数,操作次数等于字符串长度n
第3个循环用于计数器比较,操作次数总是26次
所以总操作次数为

其数量级为

这是一个线性数量级的算法,也就是说是四种解法中性能最优的,但我们就应该选择它吗?

1.5 算法的选择

以上是关于数据结构与算法 之 复杂度(python版)的主要内容,如果未能解决你的问题,请参考以下文章

在python版数据结构与算法中posts是啥意思

python 数据结构与算法之排序(冒泡,选择,插入)

Python编程之数据结构与算法练习_010

算法题之找出数组里第K大的数

二分查找算法(Python版)

Python版数据结构与算法