树在计算机科学的许多领域中使用,包括操作系统,图形,数据库系统和计算机网络。树数据结构与他们的植物表亲有许多共同之处。树数据结构具有根,分支和叶。自然界中的树和计算机科学中的树之间的区别在于树数据结构的根在顶部,其叶在底部。

1 树的相关定义

节点:树的基本部分。它可以有一个名称,我们称之为“键”。节点也可以有附加信息。我们将这个附加信息称为“有效载荷”。虽然有效载荷信息不是许多树算法的核心,但在利用树的应用中通常是关键的。

:树的另一个基本部分。边连接两个节点以显示它们之间存在关系。每个节点(除根之外)都恰好从另一个节点的传入连接。每个节点可以具有多个输出边。

:树的根是树中唯一没有传入边的节点。

路径:路径是由边连接节点的有序列表。
子节点:具有来自相同传入边的节点 c 的集合称为该节点的子节点。
父节点:具有和它相同传入边的所连接的节点称为父节点。
兄弟节点:树中作为同一父节点的子节点的节点被称为兄弟节点。
子树:由父节点和该父节点的所有后代组成的一组节点和边。
叶节点:叶节点是没有子节点的节点。
高度:树的高度等于树中任何节点的最大层数。

定义一:树由一组节点和一组连接节点的边组成。树具有以下属性:

  • 树的一个节点被指定为根节点。
  • 除了根节点之外,每个节点 n 通过一个其他节点 p 的边连接,其中 p 是 n 的父节点。
  • 从根路径遍历到每个节点路径唯一。
  • 如果树中的每个节点最多有两个子节点,我们说该树是一个二叉树。下图展示了合适定义一的。

定义二:树是空的,或者由一个根节点和零个或多个子树组成,每个子树也是一棵树。每个子树的根节点通过边连接到父树的根节点。 下图 说明了树的这种递归定义。使用树的递归定义,我们知道 下图中的树至少有四个节点,因为表示一个子树的每个三角形必须有一个根节点。 它可能有比这更多的节点,但我们不知道,除非我们更深入树。



2 列表表示树结构

2.1 结构示意图

列表为我们提供了一个简单的递归数据结构,我们可以直接查看和检查。在列表树的列表中,我们将根节点的值存储为列表的第一个元素。列表的第二个元素本身将是一个表示左子树的列表。列表的第三个元素将是表示右子树的另一个列表。下图展示了一个简单的树和相应的列表实现。



myTree = ['a',   #root
['b', #left subtree
['d', [], []],
['e', [], []] ],
['c', #right subtree
['f', [], []],
[] ]
]

树的根是 myTree[0],根的左子树是 myTree[1],右子树是 myTree[2]

2.2 数的函数表达

下面我们用列表作为树的函数来形式化树数据结构的定义(并非定义一个二叉树类),帮助我们操纵一个标准列表。

def BinaryTree(r):
return [r, [], []]

BinaryTree 函数简单地构造一个具有根节点和两个子列表为空的列表。

2.3 插入子节点

要将左子树添加到树的根,我们需要在根列表的第二个位置插入一个新的列表。我们必须小心。如果列表已经在第二个位置有东西,我们需要跟踪它,并沿着树向下把它作为我们添加的列表的左子节点。下面代码展示了插图左子节点和右子节点的 python 代码。

def insertLeft(root,newBranch):
t = root.pop(1)
if len(t) > 1:
root.insert(1,[newBranch,t,[]])
else:
root.insert(1,[newBranch, [], []])
return root
def insertRight(root,newBranch):
t = root.pop(2)
if len(t) > 1:
root.insert(2,[newBranch,[],t])
else:
root.insert(2,[newBranch,[],[]])
return root

要插入一个左子节点,我们首先获得与当前左子节点对应的(可能为空的)列表。然后我们添加新的左子树,添加旧的左子数作为新子节点的左子节点。这允许我们在任何位置将新节点拼接到树中。

下面编写一些访问函数来获取和设置根节点的值,以及获取左或右子树,完成这组树形函数。

def getRootVal(root):
return root[0] def setRootVal(root,newVal):
root[0] = newVal def getLeftChild(root):
return root[1] def getRightChild(root):
return root[2]

3 节点表示

3.1 结构示意图

示意图第二种表示树的方法使用节点和引用。在这种情况下,我们将定义一个具有根值属性的类,以及左和右子树。 它的结构类似于下图:

3.2 构造类

这个表示重要的事情是 left和 right 的属性将成为对 BinaryTree 类的其他实例的引用。 例如,当我们在树中插入一个新的左子节点时,我们用 python 创建另一个 BinaryTree 实例如下,并在根节点中修改self.leftChild 来引用新树节点。

class BinaryTree:
def __init__(self,rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None

构造函数希望获取某种对象存储在根中。 就像你可以在列表中存储任何你喜欢的对象一样,树的根对象可以是对任何对象的引用。 对于我们的先前示例,我们将存储节点的名称作为根值。使用节点和引用来表示 Figure 2 中的树,我们将创建 BinaryTree 类的六个实例。

3.2.1 添加节点

要向树中添加一个左子树,我们将创建一个新的二叉树对象,并设置根的左边属性来引用这个新对象。 代码如下:

def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t

我们必须考虑两种插入情况。 第一种情况的特征没有现有左孩子的节点。当没有左子代时,只需向树中添加一个节点。 第二种情况的特征在于具有现有左子代的节点。在第二种情况下,我们插入一个节点并将现有的子节点放到树中的下一个层。第二种情况由 Listing 5 第 4 行的 else 语句处理。同理,在根和现有的右子代之间插入节点。

def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t

3.2.2 获取左右子代和根植

def getRightChild(self):
return self.rightChild def getLeftChild(self):
return self.leftChild def setRootVal(self,obj):
self.key = obj def getRootVal(self):
return self.key

3 完整的二叉树结构

使用节点 a 作为根的简单树,并将节点 b 和 c 添加为子节点。下面代码创建树并查看存储在 key,left 和 right 中的一些值。

class BinaryTree:
def __init__(self,rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t def getRightChild(self):
return self.rightChild def getLeftChild(self):
return self.leftChild def setRootVal(self,obj):
self.key = obj def getRootVal(self):
return self.key r = BinaryTree('a')
print(r.getRootVal())
print(r.getLeftChild())
r.insertLeft('b')
print(r.getLeftChild())
print(r.getLeftChild().getRootVal())
r.insertRight('c')
print(r.getRightChild())
print(r.getRightChild().getRootVal())
r.getRightChild().setRootVal('hello')
print(r.getRightChild().getRootVal())

参考资料:《problem-solving-with-algorithms-and-data-structure-using-python》 
http://www.pythonworks.org/pythonds

python实现树结构的更多相关文章

  1. python 实现树结构

    简述: 研究  MCTS 过程中, 需要用到树结构.  baidu  google 了一番, 找不到自己能满足自己的库或代码参考,只好再造个轮子出来 我造的树用来下五子棋 和 围棋用的,   有其它不 ...

  2. python 实现树结构的打印

    class TreeNode: def __init__(self,value): self.children = [] self.value = value def add_child(self,* ...

  3. 一行python代码实现树结构

    树结构是一种抽象数据类型,在计算机科学领域有着非常广泛的应用.一颗树可以简单的表示为根, 左子树, 右子树. 而左子树和右子树又可以有自己的子树.这似乎是一种比较复杂的数据结构,那么真的能像我们在标题 ...

  4. 用python脚本通过excel生成文件夹树结构

    大概这样写标题是对的吧... 目标: 通过excel目录结构文档生成文件夹树结构. 也就是: 通过下面的excel

  5. Python 文件复制&按目录树结构拷贝&批量删除目录及其子目录下的文件

    文件复制&按目录树结构拷贝&批量删除目录及其子目录下的文件 by:授客 QQ:1033553122 测试环境: Python版本:Python 3.3.2 Win7 64 代码实践 # ...

  6. python 实现服务树结构化

    1.  所有服务树数据 tree_list = [{'id': 1, 'pid': 0, 'name': '1211', 'path': '1211', 'leaf': 0, 'type': 0}, ...

  7. 【Machine Learning】决策树案例:基于python的商品购买能力预测系统

    决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...

  8. Python多线程爬虫爬取电影天堂资源

    最近花些时间学习了一下Python,并写了一个多线程的爬虫程序来获取电影天堂上资源的迅雷下载地址,代码已经上传到GitHub上了,需要的同学可以自行下载.刚开始学习python希望可以获得宝贵的意见. ...

  9. PYTHON解析XML的多种方式效率对比实测

    在最初学习PYTHON的时候,只知道有DOM和SAX两种解析方法,但是其效率都不够理想,由于需要处理的文件数量太大,这两种方式耗时太高无法接受. 在网络搜索后发现,目前应用比较广泛,且效率相对较高的E ...

随机推荐

  1. cookie、session、sessionid ,jsessionid 的区别

    本文是转载虫师博客的文章http://www.cnblogs.com/fnng/archive/2012/08/14/2637279.html cookie.session.sessionid 与js ...

  2. Django之MTV

    一.MTV模型 Django的MTV分别代表: Model(模型):负责业务对象与数据库的对象(ORM) Template(模版):负责如何把页面展示给用户 View(视图):负责业务逻辑,并在适当的 ...

  3. 6.3 Controllers -- Managing Dependencies Between Controllers

    1. 有时候,特别是当嵌套资源的时候,我们需要两个controllers之间的某种连接.让我们拿这个router当做例子: app/router.js var Router = Ember.Route ...

  4. Linux系统——Nginx基础

    Nginx是一个开源的,支持高性能.高并发(特别是静态资源)的www服务和代理服务软件,还具有反向代理复杂均衡功能和缓存服务功能,与lvs负载均衡及Haproxy等专业代理软件相比,nginx部署更简 ...

  5. 26QTimer定时器的使用

    前面介绍过定时器事件(QTimerEvent),有个弊端,就是每启动一个定时器都要对应的ID.本次介绍在设计器中使用Qtimer. 首先在设计器中添加一个LCD Number,和两个按钮. 头文件 # ...

  6. router基本使用

    摘自:https://blog.csdn.net/qq_39894133/article/details/78992923 1.vue2 路由的使用流程: 1.vue2中的路由定义层:<rout ...

  7. springcloud16---zuul-filter

    package com.itmuch.cloud; import org.springframework.boot.SpringApplication; import org.springframew ...

  8. AVAudioFoundation(1):使用 AVAsset

    本文转自:AVAudioFoundation(1):使用 AVAsset | www.samirchen.com 本文主要内容来自 AVFoundation Programming Guide. 要了 ...

  9. Core Java 4

    p272~p273 1.除捕获异常外的另一种异常处理方式:将异常继续传递给方法调用者. 即:在方法首部添加throws说明符号,取代 try catch语句. 对于方法的调用者而言:要么处理异常,要么 ...

  10. PHP多进程学习(三)__代码案例来了解父进程与子进程的执行顺序

    pcntl_fork创建子进程成功的话,系统就有了2个进程,一个为父进程,一个为子进程,父进程和子进程都继续向下执行,子进程的id号为$pid(父进程会获取子进程的$pid也就是$pid不为0,而子进 ...