先说基本的线性结构,常用的就是线性结构,也是比较简单的,特点如下:

内存连续,一开始就会分配一块固定的内存给它,可以通过下标去快速访问常用的数组和列表。


在python 里面有array和list两种

Array:

python中array用到的机会没有list多,

先演示一下array的用法(https://docs.python.org/2/library/array.html):

from array import array

arr = array('u', 'abcdefg')
print (arr[0])
print (arr[3])
print (arr[5])

结果:

a

d

f

缺点也比较明显:

(1)只能存同一种类型,不想list可以在不同下标存不同类型的元素;

(2)只能存一些比较基本的数值、字符类型,用得不是很多,一般用numpy里面的array,来做一些数值的处理。


List:

list也是一个线性结构

(1)list的工作过程

通过C解释器代码可以看到是按照 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ....的顺序增长的

https://github.com/python/cpython/blob/master/Objects/listobject.c

image.png


append:


操作内存分配策略平均时间复杂度
init[]*  pylistobjO(1)
append(0)

image.png
append(0)会分配4个pylistobj,不会一次只分配一个,会占用空间

当分配足够的空间,时间复杂度是O(1),否则它就会重新开辟并拷贝原来的数据到新开辟的空间中去,这时就会退化,时间复杂度是O(n)
append(1)image.png
append(2)image.png
append(3)

image.png

append(4)

image.png当超出它的大小限制的时候,会进行一个resize的操作,因为不够装了,所以重新分配8个空间

























insert:

insert

insert 一般会往中间插入,这样会比较耗费时间,会重新进行内存分配,比如insert一个元素 -1,这时就要重新开辟空间,总长度就变成了16

image.png
数组的容量称为capacity(总内存分配的容量),而length指的是有多少个元素,比如之前append了5个元素,insert一个-1,这时length就是6,即6个元素,而capacity是16,所以insert操作会重新的去分配,所以他的时间复杂度平均下来是O(n)

O(n)


pop:

pop

pop默认是移除最后一个元素,所以时间复杂度是O(1)

image.png
因为只是要把第5位指针往前面移动一位,这时length的长度变成5个,时间复杂度O(1);

但如果从中间进行pop的时候,这样就会把后面的元素往前移,这时候时间复杂度就会退化,变成O(n).

O(1)

O(n)


remove:

remove

image.png
加入要把中间的元素删除掉,要把后面的每一个元素全都往前移,这时候的时间复杂度是O(n)

O(n)


如图:

操作平均时间复杂度
list[index]O(1)
list.appendO(1)
list.insertO(n)
list.pop(index), default last elementO(1)
list.removeO(n)


所以在用list的时候,频繁的进行insert/remove的时候,或是在中间进行pop的话,

可能list就不是一个合适的数据结构,要选用其他更高效的数据结构。


练习:用list实现array的ADT

实现一个定长的数组array的ADT

ps:在其他一些语言中,内置的数组结构就是定长的。

class Array(object):
    def __init__(self, size=32):
        self._size = size
        self._items = [None] * size     #定义一个长度为size,元素是none的列表

    def __getitem__(self, index):
        return self._items[index]       #定义方法,可以通过[index]进行获取元素

    def __setitem__(self, index, value):
        self._items[index] = value

    def __len__(self):
        return self._size

    def clean(self, value=None):        #清空数组
        for i in range(len(self._items)):
            self._items[i] = value

    def __iter__(self):
        for item in self._items:
            yield item



#单测(pip install pytest)
def test_array():
    size = 10           #指定size长度
    a = Array()         #实例化
    a[0] = 1            #set方法
    assert a[0] == 1    #get方法

    a.clean()           #clean方法
    assert a[0] is None #clean方法


#pytest array_list.py