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)]

  • 相关阅读:
    OSI安全体系结构
    PHP 二维数组根据相同的值进行合并
    Java实现 LeetCode 17 电话号码的字母组合
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 16 最接近的三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 15 三数之和
    Java实现 LeetCode 14 最长公共前缀
  • 原文地址:https://www.cnblogs.com/jolin123/p/4632516.html
Copyright © 2011-2022 走看看