• 沒有找到結果。

数据结构与算法 基于数组的序列

N/A
N/A
Protected

Academic year: 2021

Share "数据结构与算法 基于数组的序列"

Copied!
99
0
0

加載中.... (立即查看全文)

全文

(1)

数据结构与算法

基于数组的序列

张晓平

武汉大学数学与统计学院

(2)

Table of contents

1. Python 的序列类型

2. 低级数组 (low-level’s array)

3. 动态数组 (Dynamic Arrays)

4. Python 序列类型的效率

5. 基于数组的序列:应用

(3)

Python 的序列类型

(4)

Python 的序列类型

本章主要考察 Python 的序列 (Sequence) 类,即内置的列表 (list),元 组 (tuple) 和字符串 (str) 类.

相同点

• 支持索引来访问元素,如seq[k]

• 使用数组 (array)来表示序列.

不同点

• 抽象

• 在 Python 内部表示的方式

这些类在 Python 程序中的使用非常广泛. 基于这些类我们开发更复杂 的数据结构,因此必须弄清楚它们的公共行为和内部工作机制。

(5)

Python 的序列类型

本章主要考察 Python 的序列 (Sequence) 类,即内置的列表 (list),元 组 (tuple) 和字符串 (str) 类.

相同点

• 支持索引来访问元素,如seq[k]

• 使用数组 (array)来表示序列.

不同点

• 抽象

• 在 Python 内部表示的方式

这些类在 Python 程序中的使用非常广泛. 基于这些类我们开发更复杂 的数据结构,因此必须弄清楚它们的公共行为和内部工作机制。

2

(6)

Python 的序列类型

本章主要考察 Python 的序列 (Sequence) 类,即内置的列表 (list),元 组 (tuple) 和字符串 (str) 类.

相同点

• 支持索引来访问元素,如seq[k]

• 使用数组 (array)来表示序列.

不同点

• 抽象

• 在 Python 内部表示的方式

这些类在 Python 程序中的使用非常广泛. 基于这些类我们开发更复杂 的数据结构,因此必须弄清楚它们的公共行为和内部工作机制。

(7)

Python 的序列类型

本章主要考察 Python 的序列 (Sequence) 类,即内置的列表 (list),元 组 (tuple) 和字符串 (str) 类.

相同点

• 支持索引来访问元素,如seq[k]

• 使用数组 (array)来表示序列.

不同点

• 抽象

• 在 Python 内部表示的方式

这些类在 Python 程序中的使用非常广泛. 基于这些类我们开发更复杂 的数据结构,因此必须弄清楚它们的公共行为和内部工作机制。

2

(8)

低级数组 (low-level’s array)

(9)

低级数组 (low-level’s array)

内存地址

内存的每个字节都与作为其地址的唯一数字相关联.

内存地址通常与内存系统的物理布局相协调,故常以连续方式来 表示这些数字。

3

(10)

低级数组 (low-level’s array)

定义 : 数组

可以将一组相关变量一个接一个地存储在内存的连续区域中,这 样的表示称为数组.

(11)

低级数组 (low-level’s array)

文本字符串以字符的有序序列存储。如下图中的’SAMPLE’,将其 描述为一个 6 个字符的数组,尽管它需要 12 个字节的内存。

将数组中的每个位置称为一个单元格 (cell),并使用整数索引来描 述其在数组中的位置,单元格的编号为 0,1,2,···

5

(12)

低级数组 (low-level’s array)

数组的每个单元格必须使用相同的字节数。这一要求允许根据数 组的索引在恒定时间内访问数组的任意单元格。

给定数组开始的内存地址 start、每个元素的字节数 cellsize 以及 数组中的一个给定索引 index,则给定索引的内存地址为start+

cellsize

index.

(13)

低级数组 (low-level’s array)

引用数组 (Referential Arrays)

(14)

引用数组 (Referential Arrays)

再次强调,Python 数组的每个单元格必须使用相同的字节数.

问题

给定一个列表

[ ’ R e n e ’ , ’ J o s e p h ’ , ’ J a n e t ’ , ’ J o n a s ’ , ’ H e l e n ’ , ’ V i r g i n i a ’ ]

如何在数组中表示这样的列表呢?

(15)

引用数组 (Referential Arrays)

再次强调,Python 数组的每个单元格必须使用相同的字节数.

问题

给定一个列表

[ ’ R e n e ’ , ’ J o s e p h ’ , ’ J a n e t ’ , ’ J o n a s ’ , ’ H e l e n ’ , ’ V i r g i n i a ’ ]

如何在数组中表示这样的列表呢?

7

(16)

引用数组 (Referential Arrays)

方法 1:为每个单元格保留足够的空间来存储最长字符串,但这非常浪 费。

方法 2:Python 使用对象引用数组的内部存储机制表示列表或元组实 例。在最底层,存储的是序列元素所在的连续内存地址序列。

(17)

引用数组 (Referential Arrays)

方法 1:为每个单元格保留足够的空间来存储最长字符串,但这非常浪 费。

方法 2:Python 使用对象引用数组的内部存储机制表示列表或元组实 例。在最底层,存储的是序列元素所在的连续内存地址序列。

8

(18)

引用数组 (Referential Arrays)

尽管各元素的相对大小可能不同,但用于存储每个元素内存地址的位 数 (bits) 是固定的。通过这种方式,Python 可以基于其索引支持对列 表或元组元素的恒定时间访问。

(19)

引用数组 (Referential Arrays)

计算列表的一个切片时,返回的是一个新的列表实例,但这个新 列表引用了原始列表中的相同元素。

10

(20)

引用数组 (Referential Arrays)

修改切片中某元素的值,只会改变该单元格的引用,其他单元格仍然 引用原始列表中的元素。

(21)

引用数组 (Referential Arrays)

d a t a = [0] * 8

生成一个长度为 8 的列表,且所有这 8 个元素的值皆为 0。本质上,

列表中的 8 个单元格都引用同一个对象。

12

(22)

引用数组 (Referential Arrays)

d a t a [2] += 1

索引为 2 的单元格的引用发生了变化,其余单元格的引用不受影响.

(23)

引用数组 (Referential Arrays)

> > > p r i m e s = [2 ,3 ,5 ,7 ,11 ,13 ,17 ,19]

> > > e x t r a s = [23 ,29 ,31]

> > > p r i m e s . e x t e n d ( e x t r a s )

extend 命令用于将一个列表中的所有元素添加到另一个列表的末尾。

扩展列表不接收这些元素的副本,而是接收对这些元素的引用。

14

(24)

引用数组 (Referential Arrays)

> > > p r i m e s = [2 ,3 ,5 ,7 ,11 ,13 ,17 ,19]

> > > e x t r a s = [23 ,29 ,31]

> > > p r i m e s . e x t e n d ( e x t r a s )

extend 命令用于将一个列表中的所有元素添加到另一个列表的末尾。

扩展列表不接收这些元素的副本,而是接收对这些元素的引用。

(25)

低级数组 (low-level’s array)

Python 中的紧凑数组 (Compact Arrays in Python)

(26)

Python 中的紧凑数组 (Compact Arrays in Python)

字符串使用字符数组表示,而非引用数组。

这种更直接的表示形式被称为紧凑数组,因为数组存储的是原始数据 (对于字符串就是字符)。

(27)

Python 中的紧凑数组 (Compact Arrays in Python)

在计算性能方面,相比于引用结构,紧凑数组有如下几个优点:

• 总的内存使用率会低得多;

• 原始数据连续存储在内存中。

16

(28)

Python 中的紧凑数组 (Compact Arrays in Python)

紧凑数组的主要支持位于名为array的模块中。该模块定义了一个类,

也称为array,为原始数据类型的数组提供紧凑存储。

(29)

Python 中的紧凑数组 (Compact Arrays in Python)

> > > f r o m a r r a y i m p o r t a r r a y

> > > p r i n t( a r r a y ( ’ i ’ , [1 ,2 ,3 ,4 ,5]) ) a r r a y ( ’ i ’ , [1 , 2 , 3 , 4])

> > > p r i n t( a r r a y ( ’ f ’ , [1 ,2 ,3 ,4 ,5]) ) a r r a y ( ’ f ’ , [1.0 , 2.0 , 3.0 , 4 . 0 ] )

> > > p r i n t( a r r a y ( ’ d ’ , [1 ,2 ,3 ,4 ,5]) ) a r r a y ( ’ d ’ , [1.0 , 2.0 , 3.0 , 4 . 0 ] )

18

(30)

Python 中的紧凑数组 (Compact Arrays in Python)

(31)

动态数组 (Dynamic Arrays)

(32)

动态数组 (Dynamic Arrays)

创建低级数组时,必须显式地声明该数组的精确大小,以便系统为其 存储正确分配连续的内存。

(33)

动态数组 (Dynamic Arrays)

由于系统可能会指定相邻的内存位置来存储其他数据,因此不能通过 扩展到后续单元来简单地增加数组的容量。注意,在表示 Python 元组 或 str 实例时,这个约束没有问题,因为它们是不可变对象

21

(34)

动态数组 (Dynamic Arrays)

Python 的 list 类提供了一个更有趣的抽象。虽然列表在构造时有一个 指定长度,但是类允许我们向列表中添加元素,并且对列表的总容量 没有明显的限制。为提供这种抽象,Python 依赖于一种称为“动态数 组”的算法技巧。

(35)

动态数组 (Dynamic Arrays)

列表实例维护一个底层数组,该数组的容量通常大于当前列表长度。

有了这个额外的容量,通过使用数组的下一个可用单元格,就可以很 容易将新元素附加到列表中。

23

(36)

动态数组 (Dynamic Arrays)

如果用户继续将元素追加到列表中,则预留的容量会最终耗尽。

• 在此情况下,类会从系统请求一个新的更大的数组,并初始化新 的数组,使得原数组中的元素依次存入新数组的靠前位置上.

• 然后原数组不再需要,系统将其回收.

非常像寄居蟹 (hermit crab)

(37)

动态数组 (Dynamic Arrays)

如果用户继续将元素追加到列表中,则预留的容量会最终耗尽。

• 在此情况下,类会从系统请求一个新的更大的数组,并初始化新 的数组,使得原数组中的元素依次存入新数组的靠前位置上.

• 然后原数组不再需要,系统将其回收.

非常像寄居蟹 (hermit crab)

24

(38)

动态数组 (Dynamic Arrays)

i m p o r t sys try:

n = int( sys . a r g v [ 1 ] ) e x c e p t:

n = 100 d a t a = []

for k in r a n g e( n ) : a = len( d a t a )

b = sys . g e t s i z e o f ( d a t a )

p r i n t( f ’ L e n g t h : { a :3 d }; S i z e in b y t e s : { b :4 d } ’ )

d a t a . a p p e n d ( N o n e )

(39)

动态数组 (Dynamic Arrays)

$ p y t h o n l i s t s i z e . py 6

L e n g t h : 0; S i z e in b y t e s : 64 L e n g t h : 1; S i z e in b y t e s : 96 L e n g t h : 2; S i z e in b y t e s : 96 L e n g t h : 3; S i z e in b y t e s : 96 L e n g t h : 4; S i z e in b y t e s : 96 L e n g t h : 5; S i z e in b y t e s : 128

26

(40)

动态数组 (Dynamic Arrays)

动态数组的实现

(41)

动态数组的实现

尽管 Python 的 list 类提供了动态数组的高度优化实现,但是看看它是 如何实现的仍具有指导意义。

实现动态数组的关键是提供一种方法来扩展存储列表元素的数组 A。

27

(42)

动态数组的实现

尽管 Python 的 list 类提供了动态数组的高度优化实现,但是看看它是 如何实现的仍具有指导意义。

实现动态数组的关键是提供一种方法来扩展存储列表元素的数组 A。

(43)

动态数组的实现

若底层数组已满,欲将元素追加到列表,将执行以下步骤:

• 分配具有更大容量的数组 B;

• 令 B[i ]=

A[i ]

,

i

=0,··· ,

n

1,其中 n 表示当前项目数;

• 令 A=

B,此后使用 B 作为支持列表的数组。

新数组的容量应该多大呢?。

一个常用的规则是新数组的容量是已经填充的现有数组的两倍。

28

(44)

动态数组的实现

若底层数组已满,欲将元素追加到列表,将执行以下步骤:

• 分配具有更大容量的数组 B;

• 令 B[i ]=

A[i ]

,

i

=0,··· ,

n

1,其中 n 表示当前项目数;

• 令 A=

B,此后使用 B 作为支持列表的数组。

新数组的容量应该多大呢?。

一个常用的规则是新数组的容量是已经填充的现有数组的两倍。

(45)

动态数组的实现

若底层数组已满,欲将元素追加到列表,将执行以下步骤:

• 分配具有更大容量的数组 B;

• 令 B[i ]=

A[i ]

,

i

=0,··· ,

n

1,其中 n 表示当前项目数;

• 令 A=

B,此后使用 B 作为支持列表的数组。

新数组的容量应该多大呢?。

一个常用的规则是新数组的容量是已经填充的现有数组的两倍。

28

(46)

动态数组的实现

(47)

动态数组的实现

f r o m c t y p e s i m p o r t p y _ o b j e c t c l a s s D y n a m i c A r r a y (o b j e c t) :

def _ _ i n i t _ _ ( s e l f ) : s e l f . _n = 0

s e l f . _ c a p a c i t y = 1

s e l f . _A = s e l f . _ m a k e _ a r r a y ( s e l f . _ c a p a c i t y ) def _ m a k e _ a r r a y ( self , c ) :

r e t u r n ( c * p y _ o b j e c t ) ()

30

(48)

动态数组的实现

def _ _ l e n _ _ ( s e l f ) : r e t u r n s e l f . _n

def _ _ g e t i t e m _ _ ( self , k ) : if not 0 <= k < s e l f . _n :

r a i s e I n d e x E r r o r ( ’ i n v a l i d i n d e x ’ ) r e t u r n s e l f . _A [ k ]

(49)

动态数组的实现

def a p p e n d ( self , obj ) :

if s e l f . _n == s e l f . _ c a p a c i t y : se l f . _ r e s i z e (2 * s e l f . _ c a p a c i t y ) s e l f . _A [ s e l f . _n ] = obj

s e l f . _n += 1

def _ r e s i z e ( self , c ) : B = s e l f . _ m a k e _ a r r a y ( c ) for k in r a n g e( s e l f . _n ) :

B [ k ] = s e l f . _A [ k ] s e l f . _A = B

s e l f . _ c a p a c i t y = c

32

(50)

动态数组的实现

def i n s e r t ( self , k , v a l u e ) : if s e l f . _n == s e l f . _ c a p a c i t y :

se l f . _ r e s i z e (2 * s e l f . _ c a p a c i t y ) for j in r a n g e( s e l f . _n , k , -1) :

se l f . _A [ j ] = s e l f . _A [ j -1]

s e l f . _A [ k ] = v a l u e s e l f . _n += 1

def r e m o v e ( self , v a l u e ) : for k in r a n g e( s e l f . _n ) :

if s e l f . _A [ k ] == v a l u e :

for j in r a n g e( k , s e l f . _n - 1) : se l f . _A [ j ] = s e l f . _A [ j +1]

se l f . _A [ s e l f . _n - 1] = N o n e se l f . _n -= 1

r e t u r n

r a i s e V a l u e E r r o r ( ’ v a l u e not f o u n d ’ ) 33

(51)

动态数组的实现

def pop ( self , k = N o n e ) : if k == N o n e :

v a l u e = s e l f . _A [ s e l f . _n -1]

se l f . _A [ s e l f . _n -1] = N o n e se l f . _n -= 1

r e t u r n v a l u e e l s e:

if not 0 <= k < s e l f . _n :

r a i s e I n d e x E r r o r ( ’ i n v a l i d i n d e x ’ ) v a l u e = s e l f . _A [ k ]

for j in r a n g e( k , s e l f . _n -1) : se l f . _A [ j ] = s e l f . _A [ j +1]

se l f . _A [ s e l f . _n -1] = N o n e se l f . _n -= 1

r e t u r n v a l u e

34

(52)

动态数组的实现

def _ _ r e p r _ _ ( s e l f ) :

r e t u r n f ’ { s e l f . _A [0: s e l f . _n ]} ’

(53)

动态数组的实现

if _ _ n a m e _ _ == ’ _ _ m a i n _ _ ’ : a = D y n a m i c A r r a y ()

a . a p p e n d ( ’ 1 ’ ) a . a p p e n d ( ’ 2 ’ ) a . i n s e r t (1 , ’ abc ’ ) a . a p p e n d ( ’ 2 ’ ) p r i n t( a ) a . r e m o v e ( ’ 2 ’ ) p r i n t( a ) a . pop (1) p r i n t( a ) a . pop () p r i n t( a )

36

(54)

动态数组的实现

[ ’ 1 ’ , ’ abc ’ , ’ 2 ’ , ’ 2 ’ ] [ ’ 1 ’ , ’ abc ’ , ’ 2 ’ ] [ ’ 1 ’ , ’ 2 ’ ]

[ ’ 1 ’ ]

(55)

Python 序列类型的效率

(56)

Python 序列类型的效率

Python 的列表和元组类

(57)

Python 序列类型的效率

38

(58)

常数操作

• len(data)

• data[j]

(59)

查找值

• count

计算计数的循环必须贯穿整个序列

• index, __contains__

一旦发现元素的索引或确定元素的索引,一旦找到了期望值的最 左边出现,则循环立即退出。

40

(60)

查找值

data = list(range(10000000))

• 5 in data: Best

• 9999995 in data: Middle

• -5 in data: Worst

(61)

创建新实例 (Creating New Instances)

渐近行为与结果的长度成正比。

• 切片data[6000000:6000008]几乎可以立即构造,因为它只 有八个元素;

• 切片data[6000000:7000000] 有 100 万个元素,因此创建 比较耗时。

42

(62)

列表的可变性

(63)

列表的可变性

最简单的可变行为是data[j] = val,并由特殊方法__setitem__支 持。该操作的时间复杂度为 O(1),因为

• 它只是用一个新值替换列表中的一个元素

• 其他元素不受影响,底层数组的大小也不会改变。

44

(64)

向列表中添加元素

• append方法

由于底层数组已调整大小,其时间复杂度为 O(n),但在摊余意义 上为 O(1).

(65)

向列表中添加元素

• insert方法

insert(k, value)将给定值插入到索引 0

k

n 处,同时将所

有后续元素向右移动以腾出空间。

46

(66)

Adding Elements to a List

def i n s e r t ( self , k , v a l u e ) : if s e l f . _n == s e l f . _ c a p a c i t y :

se l f . _ r e s i z e (2 * s e l f . _ c a p a c i t y ) for j in r a n g e( s e l f . _n , k , -1) :

se l f . _A [ j ] = s e l f . _A [ j -1]

s e l f . _A [ k ] = v a l u e s e l f . _n += 1

(67)

Adding Elements to a List

• 在列表的开头插入是最昂贵的,每次操作需要线性时间;

• 中间插入需要大约一半的时间,但仍然是 O(n) 时间;

• 在末尾插入为 O(1) 时间,类似于append方法。

48

(68)

Adding Elements to a List

• 在列表的开头插入是最昂贵的,每次操作需要线性时间;

• 中间插入需要大约一半的时间,但仍然是 O(n) 时间;

• 在末尾插入为 O(1) 时间,类似于append方法。

(69)

从列表中删除元素

• pop(): 从列表中删除最后一个元素

这是最有效的,因为所有其他元素都保留在其原始位置。这实际 上是一个 O(1) 操作,不过 Python 偶尔会收缩底层动态数组以节 省内存.

49

(70)

从列表中删除元素

• pop(k): 移除位于列表中索引 k<

n 处的元素,将所有后续元素向

左移动以填补移除所产生的空白。

此操作的效率为 O(n−

k),因为移位量取决于索引 k 的选择。

(71)

从列表中删除元素

• remove方法

remove(value)允许调用方指定要删除的值。

此操作的效率为 O(n)。流程的一部分从开始搜索到在索引 k 处找 到值,而其余部分从 k 到结束迭代,以便将元素向左移动。

51

(72)

从列表中删除元素

def r e m o v e ( self , v a l u e ) : for k in r a n g e( s e l f . _n ) :

if s e l f . _A [ k ] == v a l u e :

for j in r a n g e( k , s e l f . _n - 1) : se l f . _A [ j ] = s e l f . _A [ j +1]

se l f . _A [ s e l f . _n - 1] = N o n e se l f . _n -= 1

r e t u r n

r a i s e V a l u e E r r o r ( ’ v a l u e not f o u n d ’ )

(73)

扩展列表

• extend方法

将一个列表的所有元素添加到第二个列表的末尾 调用data.extend(other)产生与以下代码相同的结果:

for e l e m e n t in o t h e r : da t a . a p p e n d ( e l e m e n t )

在这两种情况下,运行时间都与另一个列表的长度成正比,并且 是摊余的,因为第一个列表的底层数组可能会调整大小以容纳其 他元素。

在实际应用中,由于渐近分析中隐藏的常数因子明显较小,因此扩展 方法优于重复调用追加。

53

(74)

扩展列表

• extend方法

将一个列表的所有元素添加到第二个列表的末尾 调用data.extend(other)产生与以下代码相同的结果:

for e l e m e n t in o t h e r : da t a . a p p e n d ( e l e m e n t )

在这两种情况下,运行时间都与另一个列表的长度成正比,并且 是摊余的,因为第一个列表的底层数组可能会调整大小以容纳其 他元素。

在实际应用中,由于渐近分析中隐藏的常数因子明显较小,因此扩展 方法优于重复调用追加。

(75)

扩展列表

• extend方法

将一个列表的所有元素添加到第二个列表的末尾 调用data.extend(other)产生与以下代码相同的结果:

for e l e m e n t in o t h e r : da t a . a p p e n d ( e l e m e n t )

在这两种情况下,运行时间都与另一个列表的长度成正比,并且 是摊余的,因为第一个列表的底层数组可能会调整大小以容纳其 他元素。

在实际应用中,由于渐近分析中隐藏的常数因子明显较小,因此扩展 方法优于重复调用追加。

53

(76)

扩展列表

扩展的高效性源于三个方面:

1. 使用合适的 Python 方法总会有一些好处,因为这些方法通常是用 编译语言(而不是解释的 python 代码)实现的。

2. 与多次调用一些单独的函数相比,调用单个函数以完成所有工作 的开销更小。

3. 扩展效率的提高源于这样一个事实:更新后的列表的结果大小可 以提前计算。如果第二个数据集相当大,则在使用重复的追加调 用时,可能会多次调整底层动态数组的大小。使用一个扩展调用,

最多将执行一个调整大小操作。

(77)

构造新列表

• 列表推导式

s q u a r e s = [ k * k for k in r a n g e(1 , n +1) ]

• 循环

s q u a r e s = []

for k in r a n g e (1 , n +1) : s q u a r e s . a p p e n d ( k * k )

列表推导式比通过重复追加来构建列表要快得多。

55

(78)

构造新列表

使用乘法运算符初始化常量值列表,如[0]*n,以生成长度为 n 且所有 值都等于零的列表。它比增量构建这样一个列表更有效。

(79)

基于数组的序列:应用

(80)

基于数组的序列:应用

记分牌 (score board)

(81)

记分牌 (score board)

问题

为保持一个高分序列,我们设计一个名为 ScoreBoard 的类. 一 个记分牌只能存储一定数量的高分. 若记分牌已满,则只有当新 的分数高于记分牌中的最低分时才能进入记分牌。记分牌的容量 取决于游戏,如被设置为 10、50、500 等,正因如此,我们会在 ScoreBoard 的构造器中指定一个容量的参数。

57

(82)

记分牌 (score board)

假设已有一个容量为 7 的记分牌,

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k ’ , 5 1 0 ) ]

• 有一个选手 Jill,其得分为 740,记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k

’ , 5 1 0 ) ]

• 又有一个选手 Jane,得分为 500,则她不能进入记分牌,记 分牌不更新。

• 再有一个选手 David,得分为 600,则记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ David , 6 0 0 ) , ( ’ R o s e

’ , 5 9 0 ) ]

(83)

记分牌 (score board)

假设已有一个容量为 7 的记分牌,

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k ’ , 5 1 0 ) ]

• 有一个选手 Jill,其得分为 740,记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k

’ , 5 1 0 ) ]

• 又有一个选手 Jane,得分为 500,则她不能进入记分牌,记 分牌不更新。

• 再有一个选手 David,得分为 600,则记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ David , 6 0 0 ) , ( ’ R o s e

’ , 5 9 0 ) ]

58

(84)

记分牌 (score board)

假设已有一个容量为 7 的记分牌,

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k ’ , 5 1 0 ) ]

• 有一个选手 Jill,其得分为 740,记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k

’ , 5 1 0 ) ]

• 又有一个选手 Jane,得分为 500,则她不能进入记分牌,记 分牌不更新。

• 再有一个选手 David,得分为 600,则记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ David , 6 0 0 ) , ( ’ R o s e

’ , 5 9 0 ) ]

(85)

记分牌 (score board)

假设已有一个容量为 7 的记分牌,

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k ’ , 5 1 0 ) ]

• 有一个选手 Jill,其得分为 740,记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J a c k

’ , 5 1 0 ) ]

• 又有一个选手 Jane,得分为 500,则她不能进入记分牌,记 分牌不更新。

• 再有一个选手 David,得分为 600,则记分牌将更新为

[( ’ M i k e ’ , 1 1 0 5 ) , ( ’ Rob ’ , 7 5 0 ) , ( ’ J i l l ’ , 7 4 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ David , 6 0 0 ) , ( ’ R o s e

’ , 5 9 0 ) ]

58

(86)

记分牌 (score board)

(87)

记分牌 (score board)

首先构造用于描述选手信息的类 GameEntry,其中包括选手姓名和分 数:

c l a s s G a m e E n t r y (o b j e c t) :

def _ _ i n i t _ _ ( self , name , s c o r e ) : s e l f . _ n a m e = n a m e

s e l f . _ s c o r e = s c o r e def g e t _ n a m e ( s e l f ) :

r e t u r n s e l f . _ n a m e def g e t _ s c o r e ( s e l f ) :

r e t u r n s e l f . _ s c o r e def _ _ s t r _ _ ( s e l f ) :

r e t u r n f ’ ({ s e l f . _ n a m e } , { s e l f . _ s c o r e }) ’

60

(88)

记分牌 (score board)

然后构造描述记分牌的 ScoreBoard 类,其中包含 add 方法,用于添加 新进选手的信息:

c l a s s S c o r e B o a r d (o b j e c t) :

def _ _ i n i t _ _ ( self , c a p a c i t y =5) : s e l f . _ b o a r d = [ N o n e ] * c a p a c i t y s e l f . _n = 0

def _ _ g e t i t e m _ _ ( self , k ) : r e t u r n s e l f . _ b o a r d [ k ] def _ _ s t r _ _ ( s e l f ) :

r e t u r n ’ \ n ’ . j o i n (str( s e l f . _ b o a r d [ j ]) for j in r a n g e( s e l f . _n ) )

(89)

记分牌 (score board)

def add ( self , e n t r y ) :

s c o r e = e n t r y . g e t _ s c o r e ()

g o o d = s e l f . _n < len( s e l f . _ b o a r d ) or s c o r e >

s e l f . _ b o a r d [ -1]. g e t _ s c o r e () if g o o d :

if s e l f . _n < len( s e l f . _ b o a r d ) : se l f . _n += 1

j = s e l f . _n -1

w h i l e j > 0 and s e l f . _ b o a r d [ j - 1 ] . g e t _ s c o r e () < s c o r e :

se l f . _ b o a r d [ j ] = s e l f . _ b o a r d [ j -1]

j -= 1

se l f . _ b o a r d [ j ] = e n t r y

62

(90)

记分牌 (score board)

(91)

记分牌 (score board)

if _ _ n a m e _ _ == ’ _ _ m a i n _ _ ’ : b o a r d = S c o r e B o a r d (5) for e in (

( ’ Rob ’ , 7 5 0 ) , ( ’ M i k e ’ ,1105) , ( ’ R o s e ’ , 5 9 0 ) , ( ’ J i l l ’ , 7 4 0 ) ,( ’ J a c k ’ , 5 1 0 ) , ( ’ A n n a ’ , 6 6 0 ) , ( ’ P a u l ’ , 7 2 0 ) , ( ’ Bob ’ , 4 0 0 ) ,

) :

ge = G a m e E n t r y ( e [0] , e [ 1 ] ) b o a r d . add ( ge )

p r i n t( f ’ A f t e r c o n s i d e r i n g { ge } , s c o r e b o a r d is : ’ )

p r i n t( b o a r d ) p r i n t()

64

(92)

基于数组的序列:应用

插入排序

(93)

插入排序

问题

对一个无序序列进行非递减排序。

算法描述

• 首先考虑数组中的第一个元素。注意一个元素本身已经排序。

• 再考虑下一个元素。如果它比第一个小,则交换它们。

• 接下来考虑第三个元素,将其向左交换,直到与前两个元素 的顺序正确为止。

• 然后考虑第四个元素,将其向左交换,直到与前三个元素的 顺序正确为止。

• 以这种方式继续使用第五个元素、第六个元素等,直到对整 个数组进行排序。

65

(94)

插入排序

问题

对一个无序序列进行非递减排序。

算法描述

• 首先考虑数组中的第一个元素。注意一个元素本身已经排序。

• 再考虑下一个元素。如果它比第一个小,则交换它们。

• 接下来考虑第三个元素,将其向左交换,直到与前两个元素 的顺序正确为止。

• 然后考虑第四个元素,将其向左交换,直到与前三个元素的 顺序正确为止。

• 以这种方式继续使用第五个元素、第六个元素等,直到对整 个数组进行排序。

(95)

Insert-Sorting

66

(96)

Insert-Sorting

(97)

Insert-Sorting

def i n s e r t i o n _ s o r t ( A ) :

for k in r a n g e(1 , len( A ) ) : cur = A [ k ]

j = k

w h i l e j > 0 and A [ j -1] > cur : A [ j ] = A [ j -1]

j -= 1 A [ j ] = cur

68

(98)

Insert-Sorting

if _ _ n a m e _ _ == ’ _ _ m a i n _ _ ’ : A = [5 , 6 , 4 , 3]

p r i n t( ’ b e f o r e sort , A = ’ , A ) i n s e r t i o n _ s o r t ( A )

p r i n t( ’ a f t e r sort , A = ’ , A )

b e f o r e sort , A = [5 , 6 , 4 , 3] a f t e r sort , A = [3 , 4 , 5 , 6]

(99)

Insert-Sorting

if _ _ n a m e _ _ == ’ _ _ m a i n _ _ ’ : A = [5 , 6 , 4 , 3]

p r i n t( ’ b e f o r e sort , A = ’ , A ) i n s e r t i o n _ s o r t ( A )

p r i n t( ’ a f t e r sort , A = ’ , A ) b e f o r e sort , A = [5 , 6 , 4 , 3]

a f t e r sort , A = [3 , 4 , 5 , 6]

69

參考文獻

相關文件

甲型禽流感 H7N9 H7N9 H7N9 H7N9 H7N9 H7N9 H7N9 H7N9 - - 疾病的三角模式 疾病的三角模式 疾病的三角模式 疾病的三角模式 疾病的三角模式

Ø 该类抑制剂与 COX-2 的共晶结构表明,甲磺酰基或氨磺 酰基可作用于 COX-2 通道上由缬氨酸

[r]

[r]

Zivot and Andrews(1992) 將 Perron(1989) 擴充成考慮未知結構性 轉變的單根檢定 , 其概念與之前 max-Chow 檢定一樣 : 找出一個轉 變點

許多時間序列資料在公布時已經做過季節調整 , 如美國普查局 (the U.S. Census Bereau) 發展並使用 X-11 與 X-12 調整法。. EViews

即使各種新檢定並不能適用在每一個模型設定 , 這些新檢定的表現 都遠勝過傳統 ADF/PP 檢定。 因此 , Maddala and Kim (1998) 建議 應該揚棄 ADF/PP 檢定 (it is time to completely

一階隨機差分方程式.