目录
- 一、树形结构特征
- 二、二叉树:概念和性质
- 三、二叉树的list实现
- 四、优先队列
- 五、离散事件模拟
- 六、二叉树的类实现(链接实现)
- 七、哈夫曼树
- 八、树和树林
一、树形结构特征
树形结构是由结点和结点之间的连接关系构成,它与表线性结构不同,特征包括:
- 一个结构如果不为空,其中就存在着唯一的起始结点,称为树根。
- 按照结构外的连接关系,树根外的其余结点都有且只有一个前驱,可以有0个或者多个后继。
- 结构里所有结点都在树根结点通过后继关系可达的节点集合里。
- 结点之间的联系不会形成循环关系。
- 从这种结构的任意两个不同的节点出发,通过后继关系可达的两个结点集合,或者互不相交,或者一个集合是另外一个集合的子集。
二、二叉树:概念和性质
二叉树是树中的每个结点最多关联两个后继结点。
- 一个结点的关联数是0或1或2
- 一个结点的后继结点明确分左右,或为左结点,或为右结点。
1、概念和性质
定义和图示
二叉树的子树可以为空,并且子树有明确的左右之分,如果根结点的两颗子树都为空,那么这就是一棵只包含根结点的二叉树。
几个基本概念
- 不包含任何结点的二叉树称为空树
- 只包含一个结点的二叉树是一棵单点树
- 一棵二叉树的根结点称为该树的子树根结点的父结点。子树的根结点称为二叉树树根结点的子结点。
- 一个二叉树里有些结点的两个子树都是空,没有子结点,这种结点称之为树叶。树中其余结点称为分支结点。
- 一个结点的子结点个数称为该结点的度数。二叉树中树叶结点的度数为0,分支结点的度数为1或者2。
路径、结点的层和树的高度
- 从一个祖先结点到任何子孙结点都有一系列边,这样的一系列边首尾相连就成为了一条路径,我们把边的个数称为该路径的长度。
- 从树根到树中任一结点的路径长度就是该结点所在的层数。
- 一棵二叉树的高度是树中结点的最大层数(也就是这棵树里的最长路径的长度)。
二叉树的性质
二叉树最重要的性质就是树的高度和树中可以容纳的最大结点个数之间的关系。
如果将树跟线性表做比较,那么树的高度就类似于表的长度。但是在长为n的表中只能容纳n个结点。而在高为n的二叉树中可以容纳2n+1个结点。
- 性质1:在非空二叉树第i层中最多有2i个结点(i>=0)
- 性质2:高度为h的二叉树最多有(2h+1-1)个结点(h>=0)
- 性质3:对于任何非空二叉树,如果叶结点的个数为n0,度数为2的结点个数为n2。那么n0=n2+1
满二叉树,扩充二叉树
如果一个二叉树中所有的分支结点的度数都是2,那么它就是一棵满二叉树。
满二叉树的叶节点比分支结点多1。跟二叉树的性质3一样。
对于二叉树T,加入足够多的新叶结点,使T的原有结点都变成度数为2的分支结点,得到的二叉树称为T的扩充二叉树。扩充二叉树中新增的结点称为其外部结点,原树T的结点称为其内部结点。
扩充二叉树的外部路径长度E是从树根到树中各外部结点的路径长度之和,内部路径长度I是从树根到树中各内部结点的路径长度之和。如果该树拥有n个内部结点,那么E=I+2*n。
完全二叉树
对于一棵高度为h的二叉树,如果其第0层到第h-1层的结点都满,并且如果下一层的结点都不满,则所有结点在最左边连续排列,空位都在右边,那么这样的二叉树就是完全二叉树。
* 性质1:n个结点的完全二叉树高度h不大于log2n的最大整数。
* 性质2:完全二叉树如果n个结点的完全二叉树的结点按照层次从左到右的顺序从0开始编号,对于任一结点都有:
1. 序号为0的结点是根。 这说明从完全二叉树到线性结构有很自然的双向映射,可以很方便地从相应线性结构恢复完全二叉树。 2、抽象数据类型 结点是二叉树的基础,通常主要用结点保存与应用有关的信息。此外,还需要记录二叉树的结构信息,保证可以检查结点的父子关系。 二叉树的基本操作应该包括创建二叉树。构造一棵二叉树需要基于两棵已有的二叉树和希望保存在树根结点的一项数据。 3、遍历二叉树 每个二叉树都有唯一的根结点,可以将其看做是这个二叉树的唯一标识,它是基于树结构处理过程的入口。遍历二叉树,就是按照某种系统化的方式访问二叉树里的每个结点一次。 遍历二叉树就像前面讨论过的状态搜索,以根为起始点,存在两种基本方式: 深度优先遍历 按照深度优先方式变量一棵二叉树,需要做三件事情:遍历左子树(L),遍历右子树(R),和访问根节点(D)。 根据这三种工作的不同顺序可以分为三种遍历顺序(假设总是先处理左子树,不然就是6种): 例子:下图 按照先根序遍历得到的结点序列称为先根序列。 如果二叉树中每个结点都有唯一标识,就可以同结点标识描述这些序列。显然,给定一个二叉树唯一确定了它的先根序列、后根序列和中根序列,但是给定一棵二叉树的任一一种遍历序列,都无法唯一确定相应的二叉树。 如果知道了一棵二叉树的对称序列,又知道另一遍历序列,就可以唯一确定这个二叉树。 宽度优先遍历 宽度优先是按照路径长度由近到远访问,这种遍历不能写成一个递归过程。宽度优先遍历又称为按层次顺序遍历,这样遍历产生的结点序列称为二叉树的层次序列。 上面例子中按照宽度优先遍历的层次序列是:ABCDEFGHIJK 遍历与搜索 一次二叉树遍历也就是一次覆盖整个状态的空间搜索,因此,前面关于空间状态搜索的方法和实现技术都可以原样移植到二叉树遍历问题中。 二叉树的特点是一个结点最多有两个子结点,因此从一条路径走下去不会与另外一条路径相交,就不会出现循环问题。因此算法比空间状态搜索要简化一些。 三、二叉树的list实现 二叉树的结点就是一个三元组,元素是左右子树和本结点数据。因此list和tuple都可以实现,如果要实现一种非变动的二叉树,用tuple,要实现可以修改结构的二叉树,用list。 1、设计和实现 利用list实现二叉树很容易。采用下面的设计 例如:下图 如果使用list来表示,就是['A', 2、二叉树的简单应用:表达式树 二元表达式和二叉树 二元表达式可以很自然映射到二叉树中,因为它的运算符都是二元的。 例子:下图 构造表达式 因为建立的表达式不会变化,因此使用tuple的三元组实现比较合理。 这种结构由两部分组成: 在定义表达式处理函数的时候,经常需要区分基本表达式还是符合表达式。 表达式求值 表达式规则: 四、优先队列 1、概念 优先队列的特点是存入其中的每项数据都另外附有一个数值,表示这个项的优先程度,称为优先队列。优先队列应该保证,在任何时候访问或者弹出的,总是当时这个结构里保存的所有元素中优先级最高的。 允许不同元素具有相同优先级的情况,不过如果优先级相同还要先进先出,那么效率要低一些,如果优先级相同,不比先进先出,那么存在效率高的实现。 2、基于线性表的实现 有关实现方法的考虑 由于连续表可以存储数据元素,显然有可能作为优先队列的实现基础。数据项在连续表里的存储顺序可以用来表示数据之间的某种顺序关系,对于优先队列,这个顺序可以用来表示优先级关系。例如,让数据的存储位置按照顺序排列。 有两种实现方案: 基于list实现的优先队列 用第一种方案实现。 3、树形结构和堆 线性和树形结构 分析上面线性表实现优先队列效率低的原因。如果是顺序表,需要移动O(n)个元素,如果是链表,需要爬行O(n)步。这就意味着如果不改变数据的线性顺序存储方式,就不可能突破O(n)的复杂限制。 因此要操作更高,就必须考虑其他数据结构的组织方式。 堆及其性质 采用树形结构实现有线队列的一种有效技术成为堆。从结构上来看,堆就是结点里存储数据的完全二叉树,但是堆中数据要满足一种特殊的堆序,那就是任一个结点里所存的数据先于或等于其子结点里的数据。 如果所要求的序是小元素优先,那么构造出来的堆就是小顶堆。堆中每个结点的数据均小于或者等于其子结点的数据。如果要求大元素优先,那么就是大堆顶。 一个堆是按照堆序排序好的完全二叉树。 4、优先队列的堆实现 解决堆插入和删除的关键操作称为筛选,又分为向上筛选,和向下筛选。 插入元素和向上筛选 向堆的最后插入一个元素,如果要把它恢复成堆,必须做一个向上筛选的操作。 向上筛选的方法是:不断用新加入的元素与其父结点的数据比较,如果e较小(小顶堆)就交换位置,使e上移,直到e的父结点的数据不大于e或者e到达根结点的时候停止,这时就将它重新恢复为一个堆(因为满足堆序)了。 弹出元素和向下筛选 弹出元素的操作分三步: 向下筛选的过程: 由此可以看出弹出操作是O(logn)复杂度,主要是取决于向下筛选的操作。 基于堆的优先队列类 构建操作的复杂性 堆构建的操作时间复杂度是O(n)。但是这个操作只需要做一次。插入和删除的操作复杂度是O(logn),效率比较高。插入操作如果出现替换元素存储区的情况,可能有最坏的情况O(n)出现。它们所有的空间复杂度都是O(1)。 5、堆的应用:堆排序 如果在一个连续表里存储的数据是一个小顶堆,按照优先队列的操作方式反复弹出堆顶的元素,那么就能得到一个递增的序列。不过还有两个问题: 第一个问题已经解决。第二个问题也好解决。随着堆弹出,堆中元素也越来越少,每弹出一个元素,表的后部就会空出一个位置,正好利用这空出的位置存放弹出的元素。 五、离散事件模拟 离散时间系统特征: 例子:过境海关检查 1、通用的模拟框架 基本思路:按照事件发生的时间顺序处理,在模拟系统里用一个优先队列保存已知在将在某些特定时刻发生的事件,系统运行就是不断从优先队列里取出等待事件,一个个处理,直到模拟结束。 在一些时间的处理中,可能会引发新的事件,那么这些事件应该放入优先队列中,在它们发生的时候被系统处理。 2、海关检查站模拟系统 系统的基本要求: 六、二叉树的类实现(链接实现) 与连续表的链接实现类似,用一个数据单元表示一个二叉树的结点,通过子结点链接建立结点之间的关系。 1、二叉树结点类 可以看到,递归定义的二叉树操作具有统一的模式: 2、遍历算法 递归定义的遍历函数 非递归的先根序遍历函数 一种实现思路: 时间复杂性,O(n)。空间复杂性,找到栈的最大深度,所以空间复杂度是O(logn),最坏的空间复杂度是O(n),全部是右分支的情况。 通过生成器函数遍历 非递归算法的一个重要用途就是作为实现迭代器的基础。 非递归的后根序遍历算法 在这种遍历中,每个根结点都要经过三次,第一次遇到它转到左子树,第二次遇到它转到右子树,第三次遇到它处理结点数据。 注意: 非递归的遍历时间复杂度都是O(n) 3、二叉树类 但是在这种表示中,求父结点的操作比较难实现,它相当于单链表结构的求前一个结点操作,只能通过一次从根开始的遍历才能实现,最坏的时间代价是O(n)。 如果在工作中经常需要找父结点,可以考虑给每个结点增加一个父结点链接域,在设置子结点链接关系的同时设置好父结点链接。 七、哈夫曼树 哈夫曼树是一种很重要的二叉树。 1、哈夫曼树和哈夫曼算法 扩充二叉树的外部路径长度,是根到其外部结点的路径长度之和:E=l1+l2+l3+...+lm。 如果二叉树的外部路径带权,那么定义带权的扩充二叉树的外部路径长度为:WPL=w1*l1 + w2*l2 +w3*l3 + ... + wm*lm。其中wm是结点m的权。 上面图的两棵树带权的扩充二叉树,其中左边树的外部路径长度是2*2+2*3+2*6+2*6+2*9 = 2*(2+3+6+9) = 38。右边树的外部路径长度是9+6*2+(2+3)*3 = 36 由此可见,最规整的树未必路径最短。 哈夫曼树的定义 设有实数集W={w0,w1,...,wm-1},T是一棵扩充二叉树,其m个外部结点分别以wi(i=1,2,...n-1)为权,而且T的带权外部路径长度WPL在所有这样的扩充二叉树中达到最小,则称T为数据集W的最优二叉树或者哈夫曼树。 构造哈夫曼树的算法 注意:给定集合W得到的哈夫曼树不唯一,如果T是集合W上的哈夫曼树,交换其中一个或者多个结点的左右子树,得到的仍然是W上的哈夫曼树。 例子:考虑实数集W={2,3,7,10,4,2,5}。从这个集合出发做一棵哈夫曼树。 2、哈夫曼算法的实现 构造算法需要维护一组二叉树,并且要知道每棵树的权值(跟结点的权值),那么可以考虑用二叉树的结点类来构造哈夫曼树,在树根结点记录树的权值。 此外,构造过程中,需要用一个优先队列存放这组二叉树,这样就能很容易选出权值最小的两棵二叉树。 这样算法的思路就很简单了: 此外,还要注意两点: 算法分析 第一个循环建立起m个二叉树复杂度是m(logm),因为加入一个元素需要做一次O(logm)复杂度的筛选,而前面建堆使用的方法只需要O(m),因此可以优化为前面那样的算法。 第二次循环需要做m-1次,因此需要O(logm)时间,上面整个算法的复杂度是O(mlogm) 优化后的算法整个复杂度是O(m)。 算法执行的时候,需要构造一个包含2m-1个结点的树,因此其空间复杂度是O(m)。 3、哈夫曼编码 哈夫曼有很多应用,一个重要的应用就是哈夫曼编码。 定义 最优编码问题: 给定基本集合:C={c0,c1,...,cm-1},w={w0,w1,...,wm-1}其中集合C是需要编码的字符集合,W是C中个字符在实际信息传输中出现的频率。 要求: 哈夫曼编码的生成 哈夫曼提出了一种解决这个问题的方法,就是哈夫曼编码。构造方法就是构造出一棵哈夫曼树。 哈夫曼编码在编码理论里有重要意义,是给定字符集的最优编码。 例子: 首先按照权值做出一棵哈夫曼树(上图)。然后得到编码。a:0000,b:101,c:11,d:100,e:01,f:0001,h:001。现在假设收到报文:001010010000100000010101100,那么根据得到编码,解码正文为:hehadabed。 上面代码构造出来的是一个不同于书上的哈夫曼树(哈夫曼树并不唯一,前面说过的),它的结构是这样的。 因此它的编码是这样的。{'00': 'c', '010': 'b', '011': 'd', '1000': 'f', '1001': 'a', '101': 'h', '11': 'e'}。 八、树和树林 现在考虑一般的树和树的集合,也就是树林。 1、实例和表示 树形结构具有明显的层次性,其中的高层元素可能与低层元素有关,同层元素之间相互无关,也没有从低层到高层的关联。 实际生活中有很多事物可以抽象成一种树形结构,比如:家族关系。公司的组织架构,复杂机械设备的零部件等。 例子:一个四代同堂的关系。曾祖有三个孩子,第二代有分别有两个,一个,三个。第三代已经有第四代,一共14人。 这样的结点集合N和关系R反映了上述家庭的组成情况,形成该家庭组成的一种抽象描述,也就是一棵树。 上述描述不太直观,可以使用文氏图描述,也即是上面的图。还可以使用嵌套括号描述(前面使用tuple构建的二叉树)。 2、定义和相关概念 定义:一棵树是n个结点的有限集T,当T非空时满足: 结点个数为0的树称为空树,一棵树可以只有根没有子树,这就是单结点的树,只包含一个根结点。 一棵树可能有多棵子树,在有序树中,每个结点的子树都有明确的顺序,可以成为第一棵子树,第二棵子树,....。在无序树中,一个结点的不同子树没有顺序关系。 上图中,如果按照有序树,那么就是两棵不同的树,如果按照无序树就是相同的树。由于计算机表示中存在自然的顺序,所以数据结构里主要考虑的是有序树。 相关的概念 基本跟二叉树的类似。 有一点,二叉树中的子结点有明确左右之分,而有序树没有这个区分。因此,即使最大度数为2的有序树,跟二叉树也是比一样的。因为有序树没有左分支右分支的概念。 0棵或者多棵树的集合就是一个树林。 一棵空树就是一个空树林。如果树T非空,它可以分解为T=(r, F),其中r是树根,F是r的所有子树组成的树林。树和树林可以相互递归定义,非空树由树根及其子树树林构成,而树林由一组树构成。 对于树林也有有序树林和无序树林之分。 树、树林与二叉树的关系 有序树林跟二叉树存在一一映射关系,可以把任何一个二叉树映射为有序树林,也可以把任何一个有序树林映射为二叉树。 树林到二叉树的映射: 例子:上图。 二叉树到树林的映射: 树的性质 跟二叉树类似 3、抽象数据类型和操作 树的遍历 与二叉树一样,考虑遍历的时候,同样区分深度优先和宽度优先,并且深度优先有多种不同的遍历顺序。 实际上,在一般状态空间搜索问题上,搜索过程中经历的状态和状态之间的转移关系形成了一棵树,称为搜索树。 4、树的实现 树的表示有几种方法: 子结点引用表示 树的最基本表示方法就是子指针表示法,设计与二叉树的连接表示法类似:用一个数据单元表表示结点,通过结点间的链接表示树结构。但是有一个问题,树的度数不定。一种简单的考虑是只支持度数不超过固定m的树。 子结点引用表示的 父结点引用表示 在任何树形结构里,除了树根之外的每个结点都有且只有一个父结点,利用这点,在子结点里记录与父结点的关系,那么每个结点就只需要一个引用域。但是仅仅这样,并不能组成唯一的树,因此,还需要利用一个顺序表来这样表示树,每个表元素对应于一个结点,这个结点包含两部分:结点数据和父结点的引用(父结点的下标)。 父结点引用表示的 子结点表表示 这种技术是用一个连续表存储树中各个结点的信息,每个结点关联一个字结点表,记录树的结构。其中子结点表使用链接结构实现。 这种表示中有两个单元 采用子结点表表示,每个结点需要一个子结点引用域,每条边需要一个表结点。 长子-兄弟表表示 其实就是树的二叉树表示。采用这种表示,每个树结点对应于二叉树的一个结点。二叉树中结点d的左子结点是原树中d的第一个子结点,而二叉树中d的右子结点是原树中d的下一个兄弟结点。 5、树的python实现
2. 对于i>0,其父节点的编号是(i-1)/2。
3. 2*i+1
按照中根序遍历得到的结点序列称为中根序列。
按照后根序遍历得到的结点序列称为后根序列。
['B', None, None],
['C',
['D', ['F', None, None], ['G', None, None]],
['E', ['I', None, None], ['H', None, None]],
]class BTree:
def __init__(self, data, left=None, right=None):
if not isinstance(right, BTree) or if not isinstance(left, BTree):
raise TypeError
self.tree = [data, left, right]
def is_empty_bintree(self):
return self.tree is None
def root(self):
return self.tree[0]
def left(self):
return self.tree[1]
def right(self):
return self.tree[2]
def set_root(self, data):
self.tree[0] = data
def set_left(self, left):
if not isinstance(left, BTree):
raise TypeError
self.tree[1] = left
def set_right(self, right):
if not isinstance(right, BTree):
raise TypeError
self.tree[2] = right
例如:3 * (2+5)映射到这种结构中就是('*',3,('+',2, 5))
def make_sum(a, b):
return ('+', a, b)
def make_prod(a, b):
return ('*', a, b)
def make_diff(a, b):
return ('-', a, b)
def make_div(a, b):
return ('/', a, b)
e1 = makeprod(3, make_num(2, 5))
def is_basic_exp(a):
return not isinstance(a, tuple)
def eval_exp(e):
if is_basic_exp(e):
return e
op, a, b = e[0], eval_exp(e[1]), eval_exp(e[2])
if op == '+':
return eval_num(a, b)
elif op == '-':
return eval_diff(a, b)
elif op == '*':
return eval_prod(a, b)
elif op == '/':
return eval_div(a, b)
else:
raise ValueError('Unknown operator', op)
def eval_sum(a, b):
if is_number(a) and is_number(b):
return a +b
if is_number(a) and a == 0:
return b
if is_number(b) and b == 0:
return a
return make_sum(a, b)
class PrioQueueError(ValueError):
pass
class PrioQue:
def __init__(self, elist=[]):
self.elems = list(elist) #这里注意默认参数elist,它是一个可变对象,会共享,因此使用list转换,复制成另外一个对象。
self.elems.sort(reverse=True)
def insert(self, e):
'''
找到正确位置,插入元素。可以使用二分法找到位置,不过插入的时候还是要O(n)的复杂度。也可以使用插入排序的方式,在找位置的同时,将小的元素后移。
'''
i = len(self.elems) - 1
while i >= 0:
if self.elems[i] <= e:
i -= 1
else:
break
self.elems.insert(i+1, e)
def is_empty(self):
return not self.elems
def peek(self):
if self.is_empty():
raise PrioQueueError('in top')
return self.elems[-1]
def dequeue(self):
if self.is_empty():
raise PrioQueueError('in pop')
return self.elems.pop()
class PrioQueue:
def __init__(self, elist=[]):
self.elems = list(elist)
if elist:
self.buildheap()
def is_empty(self):
return not self.elems
def peek(self):
if self.is_empty():
raise PrioQueueError('in peek')
return self.elems[0]
def enqueue(self, e):
'''
插入的时候,并没有直接存入,而是拿着它是跟父结点比较。
'''
self.elems.append(None)
self.siftup(e, len(self.elems)-1)
def siftup(self, e, last):
'''
根据性质完全二叉树的子结点下标是i,父结点下标是(i-1)//2
'''
i, j = last, (last-1)//2
elems = self.elems
while i > 0 and e < elems[j]:
elems[i] = elems[j]
i, j = j, (j-1)//2
elems[i] = e
def dequeue(self):
'''
删除的时候,也没有直接把最后一个元素放到堆顶,而是拿着它去做比较
'''
if self.is_empty():
raise PrioQueueError('in dequeue')
elems = self.elems
e0 = elems[0]
e = elems.pop()
if len(elems)>0:
self.siftdown(e, 0, len(elems))
return e0
def siftdown(self, e, begin, end):
elems, i, j = self.elems, begin, begin*2+1
while j < end:
if j+1 < end and elems[j+1] < elems[j]: #右子堆的堆顶元素小于左子堆的堆顶元素
j += 1
if e < elems[j]: # e是三个里面最小的。堆形成。
break
elems[i] = elems[j] # 将最小的元素往上移为父结点
i, j = j, j*2+1
elems[i] = e
def buildheap(self):
end = len(self.elems)
for i in range(end//2, -1, -1): # end//2以下的都是叶结点,因此它们也就是一个个堆,因此从这里开始向前,一个个向下筛选,形成堆。
self.siftdown(self.elems[i], i, end)
def heap_sort(elems):
def shiftdown(elems, e, start, end):
i, j = start, 2*start+1
while j < end:
if j+1 < end and elems[j+1] < elems[j]:
j += 1
if e < elems[j]:
break
elems[i] = elems[j]
i, j = j, 2*j+1
elems[i] = e
end = len(elems)
for i in range(end//2, -1, -1):
shiftdown(elems, elems[i], i, end)
for i in range((end-1), 0, -1):
e = elems[i] # 取出最后的元素,以备向下筛选,放入合适的位置。
elems[i] = elems[0] # 取出最小元素放到最后
shiftdown(elems, e, 0, i)
# 通用的模拟器类
from random import randint
from prioqueue import PrioQueue
from queue_list import SQueue
class Simulation:
def __init__(self, duration):
self.eventq = PrioQueue()
self.time = 0 # 记录当前时间
self.duration = duration # 记录模拟的总时长
def run(self):
while not self.eventq.is_empty():
event = self.eventq.dequeue()
self.time = event.time()
if self.time > self.duration:
break
event.run()
def add_event(self, event):
self.eventq.enqueue(event)
def cur_time(self):
return self.time
class Event:
def __init__(self, event_time, host):
self.ctime = event_time
self.host = host # 表示有关事件发生的模拟系统。
def __lt__(self, other_event):
return self.ctime < other_event.ctime
def __le__(self, other_event):
return self.ctime <= other_event.ctime
def host(self):
return self.host
def time(self):
return self.ctime
def run(self):
pass
class Customs:
def __init__(self, gate_num, duration, arrive_interval, check_interval):
self.simulation = Simulations(duration) #模拟s系统对象
self.waitline = SQueue() #等待队列
self.duration = duration #持续时间
self.gates = [0] * gate_num #检查通道
self.total_wait_time = 0 #总共等待时长
self.total_used_time = 0
self.car_num = 0
self.arrive_interval = arrive_interval # 在一个区间范围时间到达车辆
self.check_interval = check_interval # 在一个区间范围时间检查完毕车辆
def wait_time_acc(self, n):
self.total_wait_time += n
def total_time_acc(self, n):
sel.total_used_time += n
def car_count_1(self):
self.car_num += 1
def add_event(self, event):
self.simulation.add_event(event)
def cur_time(self):
return self.simulation.cur_time()
def enqueue(self, car):
self.waitline.enqueue(car)
def has_queued_car(self):
return not self.waitline.is_empty()
def next_car(self):
return self.waitline.dequeue()
def find_gate(self):
'''
0表示空闲,1表示忙
'''
for i in range(len(self.gates)):
if self.gates[i] == 0:
return i
return None
def free_gate(self, i):
if self.gates[i] == 1:
self.gates[i] = 0
else:
raise ValueError('Clear gate Error')
def simulate(self):
Arrive(0, self)
self.simulation.run()
self.statistics()
def statistics(self):
print("Simulate " + str(self.duration) + " minutes, for "
+ str(len(self.gates)) + " gates")
print(self.car_num, "cars pass the customs")
print("Average waiting time:",
self.total_wait_time/self.car_num)
print("Average passing time:",
self.total_used_time/self.car_num)
i = 0
while not self.waitline.is_empty(): #模拟时间结束后还有多辆车在等待
self.waitline.dequeue()
i += 1
print(i, "cars are in waiting line.")
class Car:
def __init__(self, arrive_time):
self.time = arrive_time
def arrive_time(self):
return self.time
class Arrive(Event):
def __init__(self, arrive_time, customs):
Event.__init__(self, arrive_time, customs)
customs.add_event(self)
def run(self):
time, customs = self.time(), self.host()
# event_log(time, "car arrive")
# genarate the next Arrive event
Arrive(time + randint(*customs.arrive_interval), customs)
# deal with current Arrive car
car = Car(time)
if customs.has_queued_car():
customs.enqueue(car)
return
i = customs.find_gate()
if i is not None:
# event_log(time, "car check")
Leave(time + randint(*customs.check_interval),
i, car, customs)
else:
customs.enqueue(car)
class Leave(Event):
def __init__(self, leave_time, gate_num, car, customs):
Event.__init__(self, leave_time, customs)
self.car = car
self.gate_num = gate_num
customs.add_event(self)
def run(self):
time, customs = self.time(), self.host()
# event_log(time, "car leave")
customs.free_gate(self.gate_num)
customs.car_count_1()
customs.total_time_acc(time - self.car.arrive_time())
if customs.has_queued_car():
car = customs.next_car()
i = customs.find_gate()
# event_log(time, "car check")
customs.wait_time_acc(time - car.arrive_time())
Leave(time + randint(*customs.check_interval),
i, car, customs)
class BinTNode:
def __init__(self, dat, left=None, right=None)
self.data = dat
self.left = left
self.right = right
t = BinTNode(1, BinTNode(2), BinTNode(3)) #一个二叉树
def count_BinTNode(t):
'''
统计树中结点的个数
'''
if t is None:
return 0
else:
return 1 + count_BinTNode(t.left) + count_BinTNode(t.right)
def sum_BinTNode(t):
'''
统计树中保存数值之和
'''
if t is None:
return 0
else:
return t.data + sum_BinTNode(t.left) + sum_BinTNode(t.right)
def preorder(t, proc): #proc是具体的结点数据操作
if t is None:
return
proc(t.data)
preorder(t.left)
preorder(t.right)
def levelorder(t, proc):
qu = SQueue()
qu.enqueue(t)
while not qu.is_empty():
t = qu.dequeue()
if t is None:
continue
qu.enqueue(t.left)
qu.enqueue(t.right)
proc(t.data)
def preorder_nonrec1(t, proc):
'''
自己实现的
'''
st = SStack()
st.push(None) #放一个标志,为了让循环运行。什么都可以
while not st.is_empty():
proc(t.data)
if t.right:
st.push(t.right)
if t.left:
t = t.left
else:
t = st.pop()
def preorder_nonrec(t, proc):
'''
书中例子
'''
st = SStack()
while t or not st.is_empty():
while t is not None:
proc(t.data)
s.push(t.right)
t = t.left
t = st.pop()
def midorder_nonrec1(t, proc):
'''
中根序
'''
st = SStack()
while t or not st.is_empty():
while t:
st.push((t.data, t.right))
t = t.left
data, t = st.pop()
proc(data)
def midorder_nonrec1(t, proc):
'''
给出例子实现
'''
s = SStack()
while t or not s.is_empty():
while t:
s.push(t)
t = t.left
t = s.pop()
proc(t.data)
t = t.right
def preorder_elems(t):
st = SStack()
while t or not st.is_empty():
while t is not None:
yield t.data
st.push(t.right)
t = t.left
t = st.pop()
def postorder_nonrec(t, proc):
st = SStack()
while t or not st.is_empty():
while t is not None:
st.push(t)
if t.left:
t = t.left
else:
t = t.right
t = st.pop()
proc(t.data)
if not st.is_empty() and st.top().left == t:
t = st.top.right
else:
t = None
class BinTree:
def __init__(self):
self.root = None
def is_empty(self):
return self.root is None
def root(self):
return self.root
def leftchild(self):
return self.root.left
def rightchild(self):
return self.root.right
def set_root(self, rootnode):
self.root = rootnode
def set_left(self, leftchild):
self.root.left = leftchild
def set_right(self, rightchild):
self.root.right = rightchild
def preorder_elements(self):
st = SStack()
t = self.root
st.push(None)
while not st.is_empty():
yield t.data
if t.right:
st.push(t.right)
if t.left:
t = t.left
else:
t = st.pop()
其中l是从根到外部结点的路径长度,m是外部结点的个数。
class HTNode(BinTNode):
'''
因为要做比较,所以定义一个小于的魔术方法,可以直接拿对象做比较
'''
def __lt__(self, othernoder):
return self.data < othernoder.data
class HuffmanPrioQ(PrioQueue):
'''
因为要检查剩余元素的个数,所以定义一个检查优先队列的长度函数。
'''
def number(self):
return len(self.elems)
def HuffmanTree(weights):
'''
可以用任何可迭代对象最为参数。
'''
trees = HuffmanPrioQ()
for w in weights:
trees.enqueue(HTNode(w))
while trees.number() > 1:
t1 = trees.dequeue()
t2 = trees.dequeue()
t = HTNode(t1.data+t2.data, t1, t2)
trees.enqueue(t)
return trees.dequeue()
def HuffmanTree(weights):
'''
可以用任何可迭代对象最为参数。
'''
htnodes = map(HTNode, weights)
trees = HuffmanPrioQ(htnodes)
while trees.number() > 1:
t1 = trees.dequeue()
t2 = trees.dequeue()
t = HTNode(t1.data+t2.data, t1, t2)
trees.enqueue(t)
return trees.dequeue()
假设有字符和权值组{'a':2,'b':3,'c':7,'d':4,'e':10,'f':2,'h':5},要求通过哈夫曼的算法做出相应的哈夫曼编码。# 哈夫曼编码的算法
class HTNode2(HTNode):
'''
用key来存放标记值
'''
def __init__(self, dat, key=None, left=None, right= None):
super().__init__(dat, left, right)
self.key = key
def htnodes(dic):
'''
将初始字典转化为结点迭代器。
'''
for key, value in dic.items():
yield HTNode(value, key)
def get_huffman_tree(dic):
'''
获得哈夫曼树
'''
trees = HuffmanPrioQ(htnodes(dic))
while trees.number() > 1:
t1 = trees.dequeue()
t2 = trees.dequeue()
t = HTNode(t1.data+t2.data, left=t1, right=t2)
trees.enqueue(t)
return trees.dequeue()
def get_code(dic):
'''
获得编码字典
'''
t_node = get_huffman_tree(dic)
tree = BinTree()
tree.set_root(t_node)
p = tree.root
st = SStack()
s = ''
code_dic = {}
st.push((s, None))
while not st.is_empty():
if p.right:
st.push((s, p.right))
if p.left:
p = p.left
s += '0'
if not p.right and not p.left:
code_dic[s] = p.key
s, p = st.pop()
s += '1'
return code_dic
def htf_decode(str, dic):
'''
将字符串进行解码。这个匹配算法并不高效。
'''
htf_dic = get_code(dic) # 获得编码字典,key是编码,value是字符。
keys = list(htf_dic.keys())
results = ''
i, j, m = 0, 0, 0 # i是str的下标。j是keys的下标,m是keys[j]的下标
while i < len(str) and j < len(keys):
s = i # 暂存i,如果匹配不成功要进行回溯
while j < len(keys) and m < len(keys[j]):
if str[s] == keys[j][m]: #字符相等,前进一位
s, m = s + 1, m + 1
else: # 匹配失败,j换个,回溯,m初始化归零。
s, j, m = i, j + 1, 0
if m == len(keys[j]): # 匹配成功一个字符,将其解码方法results中。
results += htf_dic[keys[j]]
m, j, i = 0, 0, s
if i == len(str):
return results
else:
raise ValueError
str = '000100111000100110111'
dic = {'a': 2, 'b': 3, 'c': 7, 'd': 4, 'e': 10, 'f': 2, 'h': 5}
s = htf_decode(str, dic)
print(s)
这样结点数据就是下图的布局。
class SubtreeIndexError(ValueError):
pass
def Tree(data, *subtrees):
return [data].extend(subtrees)
def is_empty_tree(tree):
return tree is None
def root(tree):
return tree[0]
def subtree(tree, i):
if i < 1 or i > len(tree):
raise SubtreeIndexError
return tree[i+1]
def set_root(tree, data):
tree[0] = data
def set_subtree(tree, i, subtree):
if i < 1 or i > len(tree):
raise SubtreeIndexError
tree[i+1] = subtree
class TreeNode:
def __init__(self, data, subs=[]):
self.data = data
self.subtrees = list(subs)
class Tree:
def __init__(self):
self.root = None
def is_empty_tree(self):
return self.root is None
def root(self):
if self.is_empty_tree():
raise ValueError('the tree is empty')
return self.root.data
def subtree(self, i):
if i >= len(self.root.subtrees):
raise SubtreeIndexError
return self.root.subtrees[i]
def set_root(self, data):
self.root = TreeNode(data, self.root.subtrees)
def set_subtree(self, i, subtree):
if i >= len(self.root.subtrees):
raise SubtreeIndexError
self.root.subtrees[i] = subtree