DATA STRUCTURE

python数据结构教程 Day2

2020-07-17  本文已影响0人  XaviSong

本节重点

  1. 抽象、接口、实现
  2. ADT
  3. 评价算法的指标
  4. python原始数据类型的性能

一、抽象、实现

1、概念:

从“ 逻辑Logical ” 或者“ 物理Physical”的不同层次上看待问题及解决方案。抽象取决于你所处于的级别,在调用math.sqrt(16)时,这是典型的过程抽象,是不关心sqrt函数内部操作的,sqrt函数的源码构成了具体对求平方根功能的实现。

2、举例解释:

对于一辆汽车:

从司机观点看来,汽车是一台可以带人去往目的地的代步工具。

对于抽象角度:司机看到汽车的“逻辑”层次,司机可以通过操作各个机构来达到运输的目的。

这些操纵机构(方向盘、油门、档位)就称为“接口 Interface”。

从汽车修理工的角度来看同一辆汽车,就会相当不同,他还需要清楚每项功能是如何实现的。如发动机工作原理,档位操作的机械结构,发动机舱内各 处温度如何测量和控制等等。这些内部构造构成了汽车的“ 物理 ”层次。内部构造的工作过程就称为“实现Implementation

3、总结

一句话:抽象不关心实现,直接使用接口调用实现

二、ADT

定义:

Abstract Data Type:抽象数据类型,是对数据进行处理的一种逻辑描述,并不涉及如何实 现这些处理。

其建立了对数据的封装:封装技术将可能的处理实现细节隐蔽起来 能有效控制算法的复杂度。

ADT工作过程
具体实现:

同一个ADT可以采用不同的数据结构类实现:

比如实现树结构:可以通过嵌套列表,也可以通过其他方式实现。重点是要采用程序设计语言的控制结构和基本数据 类型来实现ADT所提供的逻辑接口。比如对于BST(二叉搜索树)来说,重要的是实现树节点的插入、删除、等操作,这是前面所讲的物理层次。

总的来说,ADT对数据实现“逻辑”层次和“物理”层次的分离,可以定义复杂的数据模型来解决问题,而不需要立即考虑此模型如何实现。这里的数据模型,让底层开发程序员专注于实现和优化数据处理, 又不改变数据的使用接口,让用户专注于用数据接口来进行问题的解决,而无需考虑如何具体实现这些接口。

ADT就是一个层层抽象的过程,来降低问题的复杂度。还不清楚ADT没有关系,后续具体数据模型介绍的过程中,都会采用ADT的形式,熟悉了就明确了。

三、算法评判标准

时间复杂度、空间复杂度,一般采用big O notation来衡量这两个指标的数量级。

时间复杂度:衡量算法的执行时间。

空间复杂度:衡量算法解决需要的存储空间。

计算资源 = 时间+空间
常见的时间复杂度:O(n^2)>O(nlogn)>O(n)>O(logn)>O(1)
我们的理想情况是:二者都向尽量小的方向进行优化。

举例分析:

变位词问题:所谓“变位词”是指两个词之间存在组成字母的重新排列关系,如:heart和earth,python和typhon 为了简单起见,假设参与判断的两个词仅由小写字母构成,而且长度相等。

解题目标:写一个bool函数,以两个词作 为参数,返回这两个词是否变位词。

解法一:

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

def isChangePositionWords(s1,s2):
    alist1 = list(s1).sort()
    alist2 = list(s2).sort()
    index = 0
    isMatch = True
    while index < len(s1) and isMatch:
        if alist1[index] == alist2[index]:
            index += 1
        else:
            isMatch = False
    return matches

这里的时间复杂度为排序算法的时间复杂度,因为相比于单个循环的O(n)来说,排序算法的时间开销会大一些,python中列表的sort方法采用的是快速排序的实现,所以数量级为O(nlogn)。

能不能向O(n)进行优化?

解法二:

对比两个词中每个字母出现的次数,如果26个字母出现的次数都相同的 话,这两个字符串就一定是变位词。为每个词设置一个26位的计数器列表,先检查每个词,在计数器中设定好每个字母出现的次数。

def isChangePositionWords(s1,s2):
    count1 = [0]*26
    count2 = [0]*26
    for i in range(len(s1)):
        pos = ord(s1[i]) - ord('a')
        count1[pos] += 1
    for i in range(len(s2)):
        pos = ord(s2[i]) - ord('a')
        count2[pos] += 1
    index = 0
    isMatch = True
    while index < 26 and isMatch:
        if count1[index] == count2[index]:
            index += 1
        else:
            isMatch = False
    return matches

算法存在三个并列的循环,时间复杂度最长的一个是O(n),同时count1和count2是额外使用的空间,空间复杂度为O(n)。这里牺牲存储空间来换取运行时间,或者相反 ,这种在时间空间之间的取舍和权衡,在 选择问题解法的过程中经常会出现。

四、python原始数据类型的性能

讨论列表、字典上各个操作的大O数量级。

类型 list dict
索引 下标 key
添加 append、extend、insert d[key] = value
删除 pop、remove pop
修改 a[index] = value d[key] = value
正查 a[i]、a[i:j] d[key]、copy
反查 index(value)、count(value)
其他 reverse、sort has_key、update

各个方法的实现原则:让最常用的操作性能最好 ,牺牲不太常用的操作。(遵循二八定律)

List列表类型
1、索引取值、赋值

由于列表的随机访问特性,这两个操作执行时间 与列表大小无关,均为O(1)

2、列表增长

(列表能使用公用运算符+号,是因为实现class LIst的时候实现了又一个魔法方法:__ add __()

3、pop

从中部移除元素的话,要把移除元素后面的元素全部向前挪位复制一遍,这个看起来有点笨拙,但这种实现方法能够保证列表按索引取值和赋值 的操作很快,达到O(1)。

4、list基本操作数量级
list基本操作时间复杂度
5、对比dict字典类型

最常用的取值get和赋值set,其性能为O(1),另一个重要操作contains(in)是判断字典中是否存在某个关键码(key),这个性能也是O(1)。

dict常用操作复杂度
上一篇:python数据结构教程 Day1
下一篇:python数据结构教程 Day3
上一篇 下一篇

猜你喜欢

热点阅读