zoukankan      html  css  js  c++  java
  • python数据结构学习笔记(五)

    5 Array-Based Sequence

    5.2.1 referential arrays

    数组在内存中是连续的地址单元,而且每个单元的大小是相同的。对于python的list来说,里面可以存储不同长度的 字符串或者其他元素,为了存下最长的那个字符串,list必须给每个单元很大的空间,这样实际上有很多单元只利用 了小部分的存储,内存的利用率很低。实际上,python的list中每个单元只存储了一个对象的引用,相当于 只保存了它的地址,这样所有对象的地址都是统一长度的.list中空的位置的引用指向None对象

    5.2.2 compact arrays in python

    string是字符数组,这与一般的list不同,list中存储的是引用,而string中是直接存储字符,所以可以说是 紧密的的数组。如果string中存储的是unicode,则每个字符需要2bytes的空间。

    与紧密数组相比,普通list需要更多的空间,比如我们想要存储一百万个64-bit整数,我们希望每个整数只使用64 bits 来存储。实际上我们需要在数组中给每个整数存储一个64位地址的引用,另外每个int类型在python中是14byte,所以 实际上每个整数用了18bytes来存储。

    array.array
    from array import array
    primes = array('i', [2,3,5,7])
    

    array函数提供紧密数组,其中'i'代表integers,array()函数第一个参数是里面元素的类型。

    python中的ctype模块里提供了类似c语言中的紧密数组。

    5.3 dynamic arrays and amortization

    创建一个低级的紧密数组的时候,必须声明数组的大小,因为系统必须给数组分配连续的内存空间。

    python中的list类则提供了一种可动态扩展的数组,比如可以随时增加一个元素。在创建一个list的时候,实际上会 比它现在的长度分配多一点空间,用来给新增加的元素。如果原来预留的空间都使用完了,则list会重新向系统申请 新的空间,新的空间又比现在所有存储的元素预留多一些空间。这种做法就跟螃蟹成长的过程不断换壳一样。

    len(list)可以获取当前list里面存的元素个数,但不是系统真正分配给list的内存。sys.getsizeof(list)可以 list真正的bytes。

    import sys
    data = []
    for k in range(n):
        a = len(data)
        b = sys.getsizeof(data)
        print "length: {0:3d}; size in types: {1:4d}".format(a,b)
        data.append(None)
    

    结果如下:

    Length: 0;size in bytes: 72
    Length: 1;size in bytes: 104
    Length: 2;size in bytes: 104
    Length: 3;size in bytes: 104
    Length: 4;size in bytes: 104
    Length: 5;size in bytes: 136
    ...
    

    可以发现初始化一个空数组时已经分配了72bytes的空间,后面不断增加元素之后每次增加32bytes。

    5.3.1 implementing a dynamic array

    要实现动态增长的数组,我们可以先用一个固定数组a来存储,当a满的时候,创建更大的数组b,先使得b[i]=a[i], 然后将a指向b,这时候就可以插入新的元素了。如何确定新数组b的容量比较合适?一种做法是取b的容量刚好是a的2倍

    import ctypes
    
    class DynamicArray:
        """a dynamic array class like a simplified python list"""
    
        def __init__(self):
            self._n = 0
            self._capacity = 1
            self._A = self._make_array(self._capacity) # low-level array
    
        def __len__(self):
            return self._n
    
        def __getitem__(self, k):
            if not 0 <= k < self._n:
                raise IndexError('invalid index')
            return self._A[k]
    
        def append(self, obj):
            if self._n == self._capacity:
                self._resize(2 * self._capacity)
            self._A[self._n] = obj
            self._n += 1
    
        def _resize(self, c):
            """resize internal array to capacity c"""
            B = self._make_array(c)
            for k in range(self._n):
                B[k] = self._A[k]
                self._A = B
                self._capacity = c
    
        def _make_array(self, c):
            return (c * ctypes.py_object)()
    

    5.4 efficiency of python's sequence types

    • tuple: nonmutating
    • list

    constant-time operation

    返回序列的长度只需要常数时间,因为序列中维护有这一信息可以直接返回。同样是常数时间的有下标访问data[i]

    字符串拼接

    如要把文档中的所有字母字符取出组成一个字符串, bad code:

    letters = ''
    for c in document:
        if c.isalpha():
            letters += c
    

    这段代码是非常低效的。因为string类型是immutable的,每次执行letters += c,都要重新创建一个string,然后 对letters重新赋值,而每次创建一个字符串的时间与该字符串长度成线性关系,所以总共需要1+2+...+n = O(n*n)的时间。

    一种改进的方法是使用一个list代替string拼接,最后再一次性拼接给string,时间是O(n)

    temp = []
    for c in document:
        if c.isalpah():
            temp.append(c)
    letters = ''.join(temp)
    

    注意最后一行''.join(temp)只需要n的时间

    实际上即使是每次对list进行append操作,虽然摊还时间是O(1),但是仍然可能需要多次动态扩建list,效率不如下面这种 使用comprehension syntax理解性语法。

    letters = ''.join([c for c in document if c.isalpha()])
    

    或者连创建list的过程都不需要

    letters = ''.join(c for c in document if c.islpha())
    

    因为string是immutable的,所以很多时候要对string进行操作的时候可以先讲string转化为list,然后对其进行修改, 操作完成之后再重新赋值给string。string转为list方式如list('bird')可以得到['b', 'i', 'r', 'd']. 反过来list转为string则可以通过''.join(['b','r','i','d'])

    5.6 multidimensional data sets

    二维数组通常也叫矩阵。python中可以用嵌套的list来实现。

    创建二维矩阵的一个错误方法如data = ([0] * c ) * r,因为结果还是一个一维矩阵。

    改进一下,用data = [[0] * 3] * 2来创建2 * 3矩阵,得到结果[[0,0,0],[0,0,0]],好像满足要求。 但实际上这样做还不行,因为里面的两个list实际上指向同一个list,修改其中一个会导致两个都同时改变。

    为了使里面的每个子list都是相互独立的,可以使用理解性语法来创建这样的二维list。 data = [[0] * c for j in range(r)]

  • 相关阅读:
    Linux进程相关的一些笔记
    [Project Euler] 来做欧拉项目练习题吧: 题目007
    [Project Euler] 来做欧拉项目练习题吧: 题目015
    [Project Euler] 来做欧拉项目练习题吧: 题目009
    [Project Euler] 来做欧拉项目练习题吧: 题目017
    [Project Euler] 来做欧拉项目练习题吧: 题目014
    [Project Euler] 来做欧拉项目练习题吧: 题目013
    [Project Euler] 来做欧拉项目练习题吧: 题目006
    [Project Euler] 来做欧拉项目练习题吧: 题目008
    [Project Euler] 来做欧拉项目练习题吧: 题目012
  • 原文地址:https://www.cnblogs.com/jolin123/p/4632516.html
Copyright © 2011-2022 走看看