数据结构-基于数组的序列

Python序列类型

列表(list)、元组(tuple)、字符串(str)
每个类别都使用数组这种低层次的概念表示序列。

低层次数组

计算机存储:

  1. 典型的存储单元为字节,相当于8位。
  2. 每个单元与存储地址相关联。
  3. 访问元素的时间为 O ( 1 ) O(1) O(1)
  4. 存储在主存(随机存储存储器RAM)当中,任何字节都能被有效地访问。

编程语言:

  1. 标识符与地址构建对应关系;
  2. 数组:一组变量能一个接一个地存储在计算机存储器的一块连续区域内。

例子:
在这里插入图片描述

  1. 每个Unicode字符对应16位。
  2. 我们将数组中的每个位置称为单元(不再拘泥于8位)。
  3. 数组中的每个单元占据着相同数量的字节。目的是为了允许索引值能在常量时间内访问数组内的任一单元。
  4. 索引方式: s t a r t + c e l l s i z e ∗ i n d e x start+cellsize*index start+cellsizeindex

引用数组

缘由:存储元素不同,所需要的内存也不同,但数组要求单元大小一致,这会严重浪费内存空间,因此采用引用数组。

  1. 在底层,存储的是一块连续的内存地址序列,这些地址指向一组元素序列。
  2. 每个元素存储地址的位数是固定的(32/64)
  3. 当列表元素改变时,不过是更改地址。两个列表元素一致时,指向的可能是同一个元素,这称为元素共享。
    当序列不可变时,以上的分析无效。
拷贝
a = [1,2,3]
b = list(a)

以上是一个浅拷贝操作,相当于构建了一个和a指向同样元素的列表,当a中存在可变元素时,改变这个可变元素的值,b也会发生改变,但是深拷贝就不会,浅拷贝复制的列表指向了不同的地址的相同元素。

Example

counters = [0]*8
在这里插入图片描述

若counters[2]+=1,不过是改变了存储地址。
extend()的作用也是一样的,将引用添加至末尾。

紧凑数组(Compact Arrays)

缘由:紧凑数组的优势:
1.占用更少的内存;(不用存储地址)
2.原始数据在内存中是连续存放的。便于高性能的计算。

创建:

import array
import sys
arr_1 = ('i',[1,2,3,4,5])
arr_2 = [1,2,3,4,5]
print(sys.getsizeof(arr_1),sys.getsizeof(arr_2)) #56 96

不支持用户自定义数据类型。
在这里插入图片描述

动态数组

以下内容与元组和字符串无关。
动态数组是一种算法技巧,是底层数组的一种特性。

  1. 创建数组时,必须声明其长度。
  2. 一张列表通常关联着一个底层数组,该数组通常比列表长度更长。
  3. 当容量全部被填满时,动态数组机制触发。
    数组扩展流程如下:
    1.A数组存储已满,新的元素需要加入;
    2.向系统申请新的数组B,数组B的长度通常为为A的两倍;
    3.元素进入B,数组A舍弃;
    4.新的元素进入B。

Example

在这里插入图片描述
我们创建一个数组,当数组中不含任何元素时,已经占用了一些内存,其中保存了一些数组的信息。当我们给予第一个元素时,数组扩展了32个字节,预留了3个隐藏字节。当填入第5个元素时,给元素提供的内存达到了64,扩展了一倍,这与先前所介绍的完全吻合。

摊销

命题-1:设S是一个由具有初始大小的动态数组实现的数组,实现策略为:当数组已满时,将此数组大小扩大为原来的2倍。S最初为空,对S连续执行n个添加操作的运行时间为O(n)。

该命题的证明方法采用了摊销分析。
每次操作的摊销运行时间为O(1)。

摊销在这里,是一种评估运行时间的操作,因为数组添加操作的运行时间不是统一的,我们通过取平均的方式予以可度量化。

在实际的操作过程中,每一个增添步骤的时间复杂度不是一致的,在摊销边界处,时间复杂度会更大一些。

事实上,在数组未满时,添加操作的复杂度是 O ( 1 ) O(1) O(1)

而扩张时,复杂度为 O ( n ) O(n) O(n)

一般而言,当数组满时,增添元素,数组长度变为原来的两倍。减少元素,当为原来的四分之一时,数组长度变为原来的一半。

大小按几何增长

数组大小以任意几何增长基数扩大,每次操作的摊销运行时间仍然为 O ( 1 ) O(1) O(1).

避免使用等差数列

等差数列策略:每次调整数组大小时,预留固定数量的额外单元。
命题-2:对初始为空的动态数组执行连续n个增添操作,若每次调整数组大小时采用固定的增量,则运行时间 Ω ( n 2 ) \Omega(n^2) Ω(n2)
也就是说,执行n个append操作花费的时间为 O ( n 2 ) O(n^2) O(n2);
而采用几何增长的话,是 O ( n ) O(n) O(n)

Python序列类型和效率

Python的列表和元组类

元组比列表的内存利用率更高,因为元组固定不变,不需要创建额外剩余空间。
在这里插入图片描述

  1. 常数时间操作:1&2;
  2. 查找:3&4&5;
  3. 比较:6;
  4. 创建新的对象:7&8&9。
  5. 改变列表中的值(9):
    在这里插入图片描述

Python的字符串类

其中最值得一提的是

str1 += str2

这一操作与列表中的可不一样。

比如我们想从一个字符串中提取所有的字母:
在这里插入图片描述
这一操作的时间复杂度是 O ( n 2 ) O(n^2) O(n2)
在操作过程中,是一个求和公式。
这是因为,字符串不可变,每次都是创建一个新的字符串。

为了更高效的解决这个问题,我们可以这么做:
在这里插入图片描述
这样,花费的时间仅仅为 O ( n ) O(n) O(n)

排序

插入排序

在这里插入图片描述
最坏的情况(反序)为 Ω ( n ) \Omega(n) Ω(n),最好的情况(顺序)为 O ( n ) O(n) O(n)

#升序 7行
for k in range(1,len(A)): # 起始不是0
	cur = A[k] # 给为排序最左标记
	j = k
	while j > 0 and A[j-1] > cur: # 满足两个条件
		A[j] = A[j-1] # 移动位置
		j -= 1
	A[j] = cur # 最后才插入

归并排序

在这里插入图片描述
共logn层,每一层有n个操作。共O(nlogn)。

多维数据集

list、tuple、string都是一维的;

但是很多应用都需要高维数据:

  1. 计算机图形:2D or 3D;
  2. 地理信息: 2D;
  3. 医学图像: 3D;
data = [[1,2,3],[4,5,6],[7,8,9]]
data[1][2]

二维数据集通常表示为列表的列表,索引方式也已经给出。

创建多维列表

data = ([0]*c)*r : 长度为rc的一维数组
data = [[0]*c]*r : 结构类似,但是是重叠的。
在这里插入图片描述
数组的数组,其内部是可变元素(数组),因此出现了混叠,和浅拷贝类似。

data = [[0]*c for j in range(r)]
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

右边是我女神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值