树 (tree) 是一种非常高效的非线性存储结构。树,可以很形象的理解,有根,有叶子,对应在数据结构中就是根节点、叶子节点,同一层的叶子叫兄弟节点,邻近不同层的叫父子节点,非常好理解。

注:定义来自百度百科。

其他概念解释

  • 二叉树,就是每个节点都至多有二个子节点的树。

  • 满二叉树,就是除了叶子节点外,每个节点都有左右两个子节点,这种二叉树叫做满二叉树。

  • 完全二叉树,就是叶子节点都在最底下两层,最后一层叶子节都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫做完全二叉树。

在接下来的内容里,我们将逐步介绍二叉树的具体功能是如何实现的。

思路:

  1. 先定义一个节点 node 类,存储数据 data 和左子节点 left 以及 右子节点 right。

  2. 再实现二叉树 binary_tree 的类,应至少有以下属性和函数: 属性:有一个根节点(root) , 它是 node 类。 函数:添加子节点 add ,返回父节点 get_parent,删除子节点 delete。

步骤如下:

1. 创建 Node 类

创建一个 Node 的类,作为基础数据结构:链点,并初始化对应的内参。

具体实现代码如下:

class Node(object):
def __init__(self,item):
self.item = item #表示对应的元素
self.left=None #表示左子节点
self.right=None #表示右子节点
def __str__(self):
return str(self.item) #print 一个 Node 类时会打印 __str__ 的返回值

2. 创建 Tree 类

创建一个 Tree 的类,定义根节点。

具体实现代码如下:

class Tree(object):
def __init__(self):
self.root=Node('root') #根节点定义为 root 永不删除,作为哨兵使用。

3. 添加 add 函数

添加一个 add(item) 的函数,功能是添加子节点到树里面。

具体实现代码如下:

def add(self,item):
node = Node(item)
if self.root is None: #如果二叉树为空,那么生成的二叉树最终为新插入树的点
self.root = node
else:
q = [self.root] # 将q列表,添加二叉树的根节点
while True:
pop_node = q.pop(0)
if pop_node.left is None: #左子树为空则将点添加到左子树
pop_node.left = node
return
elif pop_node.right is None: #右子树为空则将点添加到右子树
pop_node.right = node
return
else:
q.append(pop_node.left)
q.append(pop_node.right)

4. 添加 get_parent 函数

添加一个 get_parent(item) 函数,功能是找到 item 的父节点。

具体实现代码如下:

def get_parent(self, item):
if self.root.item == item:
return None # 根节点没有父节点
tmp = [self.root] # 将tmp列表,添加二叉树的根节点
while tmp:
pop_node = tmp.pop(0)
if pop_node.left and pop_node.left.item == item: #某点的左子树为寻找的点
return pop_node #返回某点,即为寻找点的父节点
if pop_node.right and pop_node.right.item == item: #某点的右子树为寻找的点
return pop_node #返回某点,即为寻找点的父节点
if pop_node.left is not None: #添加tmp 元素
tmp.append(pop_node.left)
if pop_node.right is not None:
tmp.append(pop_node.right)
return None

5. 添加 delete 函数

添加一个 delete(item) 函数,功能是从二叉树中删除一个子节点。

思路如下:

先获取待删除节点 item 的父节点。
如果父节点不为空,判断 item 的左右子树:
如果左子树为空,那么判断 item 是父节点的左孩子,还是右孩子;
如果是左孩子,将父节点的左指针指向 item 的右子树,反之将父节点的右指针指向 item 的右子树。
如果右子树为空,那么判断 item 是父节点的左孩子,还是右孩子;
如果是左孩子,将父节点的左指针指向 item 的左子树,反之将父节点的右指针指向 item 的左子树。
如果左右子树均不为空,寻找右子树中的最左叶子节点 x ,将 x 替代要删除的节点。
删除成功,返回 True。
删除失败, 返回 False。

效果演示:对已知二叉树删除元素 32

具体实现代码如下:

def delete(self, item):
if self.root is None: # 如果根为空,就什么也不做
return False parent = self.get_parent(item)
if parent:
del_node = parent.left if parent.left.item == item else parent.right # 待删除节点
if del_node.left is None:
if parent.left.item == item:
parent.left = del_node.right
else:
parent.right = del_node.right
del del_node
return True
elif del_node.right is None:
if parent.left.item == item:
parent.left = del_node.left
else:
parent.right = del_node.left
del del_node
return True
else: # 左右子树都不为空
tmp_pre = del_node
tmp_next = del_node.right
if tmp_next.left is None:
# 替代
tmp_pre.right = tmp_next.right
tmp_next.left = del_node.left
tmp_next.right = del_node.right else:
while tmp_next.left: # 让tmp指向右子树的最后一个叶子
tmp_pre = tmp_next
tmp_next = tmp_next.left
# 替代
tmp_pre.left = tmp_next.right
tmp_next.left = del_node.left
tmp_next.right = del_node.right
if parent.left.item == item:
parent.left = tmp_next
else:
parent.right = tmp_next
del del_node
return True
else:
return False

最终完整代码如下:

class Node(object):
def __init__(self,item):
self.item=item #表示对应的元素
self.left=None #表示左节点
self.right=None #表示右节点
def __str__(self):
return str(self.item) #print 一个 Node 类时会打印 __str__ 的返回值
class Tree(object):
def __init__(self):
self.root=Node('root') #根节点定义为 root 永不删除,作为哨兵使用。
def add(self,item):
node = Node(item)
if self.root is None: #如果二叉树为空,那么生成的二叉树最终为新插入树的点
self.root = node
else:
q = [self.root] # 将q列表,添加二叉树的根节点
while True:
pop_node = q.pop(0)
if pop_node.left is None: #左子树为空则将点添加到左子树
pop_node.left = node
return
elif pop_node.right is None: #右子树为空则将点添加到右子树
pop_node.right = node
return
else:
q.append(pop_node.left)
q.append(pop_node.right)
def get_parent(self, item):
if self.root.item == item:
return None # 根节点没有父节点
tmp = [self.root] # 将tmp列表,添加二叉树的根节点
while tmp:
pop_node = tmp.pop(0)
if pop_node.left and pop_node.left.item == item: #某点的左子树为寻找的点
return pop_node #返回某点,即为寻找点的父节点
if pop_node.right and pop_node.right.item == item: #某点的右子树为寻找的点
return pop_node #返回某点,即为寻找点的父节点
if pop_node.left is not None: #添加tmp 元素
tmp.append(pop_node.left)
if pop_node.right is not None:
tmp.append(pop_node.right)
return None
def delete(self, item):
if self.root is None: # 如果根为空,就什么也不做
return False parent = self.get_parent(item)
if parent:
del_node = parent.left if parent.left.item == item else parent.right # 待删除节点
if del_node.left is None:
if parent.left.item == item:
parent.left = del_node.right
else:
parent.right = del_node.right
del del_node
return True
elif del_node.right is None:
if parent.left.item == item:
parent.left = del_node.left
else:
parent.right = del_node.left
del del_node
return True
else: # 左右子树都不为空
tmp_pre = del_node
tmp_next = del_node.right
if tmp_next.left is None:
# 替代
tmp_pre.right = tmp_next.right
tmp_next.left = del_node.left
tmp_next.right = del_node.right else:
while tmp_next.left: # 让tmp指向右子树的最后一个叶子
tmp_pre = tmp_next
tmp_next = tmp_next.left
# 替代
tmp_pre.left = tmp_next.right
tmp_next.left = del_node.left
tmp_next.right = del_node.right
if parent.left.item == item:
parent.left = tmp_next
else:
parent.right = tmp_next
del del_node
return True
else:
return False

二叉搜索树又称二叉查找树,亦称二叉排序树,如下图所示:

它主要用于搜索。 它或者是一棵空树,或者是具有下列性质的二叉树:

  1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  2. 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  3. 左、右子树也分别为二叉排序树。

平衡二叉树(平衡二叉树又被称为 AVL 树 )是基于二分法的策略提高数据的查找速度的二叉树的数据结构。

特点:平衡二叉树是采用二分法思维把数据按规则组装成一个树形结构的数据,用这个树形结构的数据减少无关数据的检索,大大的提升了数据检索的速度;平衡二叉树的数据结构组装过程有以下规则:

  1. 非叶子节点只能允许最多两个子节点存在。

  2. 每一个非叶子节点数据分布规则为左边的子节点小于前节点的值,右边的子节点大于当前节点的值(这里值是基于自己的算法规则而定的,比如 hash 值)。

注:定义来自百度百科。

遍历原理

二叉树的遍历:是指从根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。

这里有两个关键词:访问和次序。

访问其实是要根据实际的需要来确定具体做什么,比如对每个结点进行相关计算,输出打印等。它算作是一个抽象操作。

二叉树的遍历次序不同于线性结构,最多也就是从头到尾、循环和双向等简单的遍历方式。树的结点之间不存在唯一的前驱和后继关系,在访问一个结点后,下一个被访问的结点面临着不同的选择。

二叉树的遍历方式可以有很多,如果我们限制从左到右的顺序,就主要分为三种:

  1. 中序遍历

  2. 后序遍历

  3. 前序遍历

中序遍历

  1. 先处理左子树,然后处理当前节点,再处理右子树;

  2. 对于一颗二叉查找树,所有的信息都是有序排列的,中序遍历可以是信息有序输出,且运行时间为 O(n);

  3. 递归实现中序遍历。

在之前的 Tree 类里面添加 inorder 函数

参考代码如下:

def inorder(self,node):  # 中序遍历
if node is None:
return []
result = [node.item]
left_item = self.inorder(node.left)
right_item = self.inorder(node.right)
return left_item + result + right_item

中序遍历的效果演示:

后序遍历
  1. 先处理左右子树,然后再处理当前节点,运行时间为 O(n)

  2. 递归实现后序遍历

参考代码如下:

def postorder(self,node):  # 后序遍历
if node is None:
return []
result = [node.item]
left_item = self.postorder(node.left)
right_item = self.postorder(node.right)
return left_item + right_item + result
 
先序遍历
  1. 先处理当前节点,再处理左右子树;

  2. 递归实现先序遍历。

参考代码如下:

def preorder(self,node):  # 先序遍历
if node is None:
return []
result = [node.item]
left_item = self.preorder(node.left)
right_item = self.preorder(node.right)
return result + left_item + right_item

  

Python实现树的更多相关文章

  1. python Trie树和双数组TRIE树的实现. 拥有3个功能:插入,删除,给前缀智能找到所有能匹配的单词

    #coding=utf- #字典嵌套牛逼,别人写的,这样每一层非常多的东西,搜索就快了,树高26.所以整体搜索一个不关多大的单词表 #还是O(). ''' Python 字典 setdefault() ...

  2. python kd树 搜索 代码

    kd树就是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构,可以运用在k近邻法中,实现快速k近邻搜索.构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,依次选择坐标轴对空间 ...

  3. python数据结构树和二叉树简介

    一.树的定义 树形结构是一类重要的非线性结构.树形结构是结点之间有分支,并具有层次关系的结构.它非常类似于自然界中的树.树的递归定义:树(Tree)是n(n≥0)个结点的有限集T,T为空时称为空树,否 ...

  4. Python 数据结构 树

    什么是树 数是一种抽象的数据类型(ADT)或是作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合,它是由n(n>1)的有限个节点和节点之间的边组成的一个有层次关系的集合. 树的组成 ...

  5. 机器学习之路: python 回归树 DecisionTreeRegressor 预测波士顿房价

    python3 学习api的使用 git: https://github.com/linyi0604/MachineLearning 代码: from sklearn.datasets import ...

  6. Python:树的遍历

    各种遍历顺序如下图所示: 树的最大深度  # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = ...

  7. Python数据结构--树遍历算法

    ''' 遍历是访问树的所有节点的过程,也可以打印它们的值. 因为所有节点都通过边(链接)连接,所以始终从根(头)节点开始. 也就是说,我们不能随机访问树中的一个节点. 这里介绍三种方式来遍历一棵树 - ...

  8. python 生成 树状结构

    树状结构: 字典里只有一个键值对, key 为根, 值为一个列表, 列表里的某个或多个元素可以再进行分支(分支还是列表) 比如: 邮件的发件人, 收件人, 转发关系树状结构 forwarding_re ...

  9. Python数据结构-树与树的遍历

    树:是一种抽象的数据类型 树的作用:用来模拟树状结构性质的数据集合 树的特点: 每个节点有零个或者多个节点 没有父节点的节点,叫做根节点 每一个根节点有且只有一个父节点 除了根节点外,每个节点可以分成 ...

随机推荐

  1. BZOJ 4500: 矩阵 带权并查集

    这个思路挺巧妙的 ~ 定义一行/列的权值为操作后所整体增加的值. 那么,我们会有若干个 $a[x]+b[y]=c$ 的限制条件. 但是呢,我们发现符号是不能限制我们的(因为可加可减) 所以可以将限制条 ...

  2. java的多线程之入门

    一.java多线程基本概念 调用run():在主线程调用子线程的run()方法会中断主线程等到子线程执行完毕之后再执行主线程. 调用start():在主线程中执行子线程的start()后会与主线程同步 ...

  3. Python-内存泄漏 持续增长 检查点

    仅个人目前遇见的内存问题, 可能不适用所有问题 一下只是简单的实例代码, 可能跑不起来, 只是看看 可变变量参数 小例子: def foo(a, b=[]): b.append(a) print b ...

  4. 【概率DP】$P2059$ 卡牌游戏

    链接 题目描述 N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡片上的数字为X,则庄家 ...

  5. 2019.10.1 qbxt模拟题

    第一题 考虑树上\(DP\),f[i][j][0/1]表示以\(i\)为根的子树,入读为零点的个数为\(j\),点\(i\)的入度为\(0\)/不为\(0\)时的方案数 转移的时候考虑\(u\)的一个 ...

  6. sysmain服务怎么启动 & Win7 SuperFetch无法启动

    在控制面板/管理工具/服务中,只需找到Superfetch这个服务,双击,然后将其启动类型改为自动,并点击启动按钮并确定即可. Superfetch无法启动,系统找不到指定档案 ms-windows ...

  7. java NIO面试题剖析

    转载:https://mp.weixin.qq.com/s/YIcXaH7AWLJbPjnTUwnlyQ 首先我们分别画图来看看,BIO.NIO.AIO,分别是什么? BIO:传统的网络通讯模型,就是 ...

  8. centos7安装hadoop2.7.7

    下载hadoop-2.7.7 网址如下 https://www-eu.apache.org/dist/hadoop/core/ 移动到/opt 路径下 在/opt下新建一个文件夹,名为app mkdi ...

  9. [Beta阶段]第十次Scrum Meeting

    Scrum Meeting博客目录 [Beta阶段]第十次Scrum Meeting 基本信息 名称 时间 地点 时长 第十次Scrum Meeting 19/05/16 大运村寝室6楼 30min ...

  10. ==和Equal()

    1.a==null与 null==a null放在前面就是为了避免变量为空时 引了空指针异常 如: if(a==null) 如果a 真为空时,现在就相当用调用了变量a的方法,a 都为空了还调用他的方法 ...