Python树与树算法

树的概念

树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 每个节点有零个或多个子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树;

比如说:

树的术语

  • 节点的度:一个节点含有的子树的个数称为该节点的度;
  • 树的度:一棵树中,最大的节点的度称为树的度;
  • 叶节点终端节点:度为零的节点;
  • 父亲节点父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 树的高度深度:树中节点的最大层次;
  • 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
  • 节点的祖先:从根到该节点所经分支上的所有节点;
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  • 森林:由m(m>=0)棵互不相交的树的集合称为森林;

树的种类

  • 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;

  • 有序树

    :树中任意节点的子节点之间有顺序关系,这种树称为有序树;

    • 二叉树

      :每个节点最多含有两个子树的树称为二叉树;

      • 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
      • 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
      • 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);
    • 霍夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;

    • B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

树的存储与表示

顺序存储:将数据结构存储在固定的数组中,然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。

链式存储:

由于对节点的个数无法掌握,常见树的存储表示都转换成二叉树进行处理,子节点个数最多为2

常见的一些树的应用场景

1.xml,html等,那么编写这些东西的解析器的时候,不可避免用到树

2.路由协议就是使用了树的算法

3.mysql数据库索引

4.文件系统的目录结构

5.所以很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构

二叉树

二叉树的基本概念

二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)

二叉树的性质(特性)

性质1: 在二叉树的第i层上至多有2^(i-1)个结点(i>0)

性质2: 深度为k的二叉树至多有2^k - 1个结点(k>0)

性质3: 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;

性质4:具有n个结点的完全二叉树的深度必为 \(log_2\)(n+1)

性质5:对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)

(1)完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。

(2)满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

二叉树的节点表示以及树的创建

通过使用Node类中定义三个属性,分别为elem本身的值,还有lchild左孩子和rchild右孩子

  1. class Node(object):
  2. """节点"""
  3. def __init__(self, item):
  4. self.elem = item # 添加数据
  5. self.lchild = None # 左子节点
  6. self.rchild = None # 右子节点

树的创建,创建一个树的类,并给一个root根节点,一开始为空,随后添加节点

  1. class Tree(object):
  2. """二叉树"""
  3. def __init__(self):
  4. self.root = None # 根节点
  5. def add(self, item):
  6. node = Node(item) # 实例化节点
  7. if self.root is None: # 判断根节点,如果为空则直接将根节点指向实例化节点
  8. self.root = node
  9. return
  10. queue = [self.root] # 创建一个队列,并将根节点传入
  11. while queue: # 如果queue不为空,则一直进行循环
  12. cur_node = queue.pop(0) # 弹出队列第一个节点,并在下面进行比较
  13. if cur_node.lchild is None: # 如果 cur_node 的左子节点为空,就添加到左子节点上,并返回
  14. cur_node.lchild = node
  15. return
  16. else:
  17. # 如果 cur_node 的左子节点不为空,把子节点添加到队列中
  18. queue.append(cur_node.lchild)
  19. if cur_node.rchild is None: # 如果 cur_node 的右子节点为空,就添加到右子节点上,并返回
  20. cur_node.rchild = node
  21. return
  22. else:
  23. # 如果 cur_node 的左子节点不为空,把子节点添加到队列中
  24. queue.append(cur_node.rchild)

二叉树的遍历

树的遍历是树的一种重要的运算。所谓遍历是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次,我们把这种对所有节点的访问称为遍历(traversal)。那么树的两种重要的遍历模式是深度优先遍历和广度优先遍历,深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。

深度优先遍历

对于一颗二叉树,深度优先搜索(Depth First Search)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。

那么深度遍历有重要的三种方法。这三种方式常被用于访问树的节点,它们之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做先序遍历(preorder),中序遍历(inorder)和后序遍历(postorder)。我们来给出它们的详细定义,然后举例看看它们的应用。

  • 先序遍历 在先序遍历中,我们先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树

    根节点->左子树->右子树

    1. def preorder(self, node):
    2. """先序遍历"""
    3. if node is None: # 如果节点为空,直接返回
    4. return
    5. print(node.elem, end=" ") # 打印几点的elem属性
    6. self.preorder(node.lchild) # 递归调用从左子节点开始
    7. self.preorder(node.rchild)
  • 中序遍历 在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树

    左子树->根节点->右子树

    1. def inorder(self, node):
    2. """中序遍历"""
    3. if node is None:
    4. return
    5. self.inorder(node.lchild)
    6. print(node.elem, end=" ")
    7. self.inorder(node.rchild)
  • 后序遍历 在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点

    左子树->右子树->根节点

    1. def postorder(self, node):
    2. """后序遍历"""
    3. if node is None:
    4. return
    5. self.postorder(node.lchild)
    6. self.postorder(node.rchild)
    7. print(node.elem, end=" ")

广度优先遍历(层次遍历)

从树的root开始,从上到下从从左到右遍历整个树的节点

  1. def breadth_travel(self):
  2. """广度遍历"""
  3. if self.root is None: # 如果根节点为空,则返回空
  4. return
  5. queue = [self.root] # 将根节点添加到队列中
  6. while queue: # 如果队列不为空,则开始循环
  7. cur_node = queue.pop(0) # 弹出队列中第一个节点
  8. print(cur_node.elem, end=" ") # 打印弹出的节点elem属性
  9. if cur_node.lchild is not None: # 如果弹出的节点的lchild属性不指向空,就把lchild指向的节点添加到队列中
  10. queue.append(cur_node.lchild)
  11. if cur_node.rchild is not None: # 如果弹出的节点的rchild属性不指向空,就把rchild指向的节点添加到队列中
  12. queue.append(cur_node.rchild)

完成代码(节点、树、添加节点、广度遍历、先前后序遍历)

  1. # coding:utf-8
  2. class Node(object):
  3. """节点"""
  4. def __init__(self, item):
  5. self.elem = item # 添加数据
  6. self.lchild = None # 左子节点
  7. self.rchild = None # 右子节点
  8. class Tree(object):
  9. """二叉树"""
  10. def __init__(self):
  11. self.root = None # 根节点
  12. def add(self, item):
  13. node = Node(item) # 实例化节点
  14. if self.root is None: # 判断根节点,如果为空则直接将根节点指向实例化节点
  15. self.root = node
  16. return
  17. queue = [self.root] # 创建一个队列,并将根节点传入
  18. while queue: # 如果queue不为空,则一直进行循环
  19. cur_node = queue.pop(0) # 弹出队列第一个节点,并在下面进行比较
  20. if cur_node.lchild is None: # 如果 cur_node 的左子节点为空,就添加到左子节点上,并返回
  21. cur_node.lchild = node
  22. return
  23. else:
  24. # 如果 cur_node 的左子节点不为空,把子节点添加到队列中
  25. queue.append(cur_node.lchild)
  26. if cur_node.rchild is None: # 如果 cur_node 的右子节点为空,就添加到右子节点上,并返回
  27. cur_node.rchild = node
  28. return
  29. else:
  30. # 如果 cur_node 的左子节点不为空,把子节点添加到队列中
  31. queue.append(cur_node.rchild)
  32. def breadth_travel(self):
  33. """广度遍历"""
  34. if self.root is None: # 如果根节点为空,则返回空
  35. return
  36. queue = [self.root] # 将根节点添加到队列中
  37. while queue: # 如果队列不为空,则开始循环
  38. cur_node = queue.pop(0) # 弹出队列中第一个节点
  39. print(cur_node.elem, end=" ") # 打印弹出的节点elem属性
  40. if cur_node.lchild is not None: # 如果弹出的节点的lchild属性不指向空,就把lchild指向的节点添加到队列中
  41. queue.append(cur_node.lchild)
  42. if cur_node.rchild is not None: # 如果弹出的节点的rchild属性不指向空,就把rchild指向的节点添加到队列中
  43. queue.append(cur_node.rchild)
  44. def preorder(self, node):
  45. """先序遍历"""
  46. if node is None: # 如果节点为空,直接返回
  47. return
  48. print(node.elem, end=" ") # 打印几点的elem属性
  49. self.preorder(node.lchild) # 递归调用从左子节点开始
  50. self.preorder(node.rchild)
  51. def inorder(self, node):
  52. """中序遍历"""
  53. if node is None:
  54. return
  55. self.inorder(node.lchild)
  56. print(node.elem, end=" ")
  57. self.inorder(node.rchild)
  58. def postorder(self, node):
  59. """后序遍历"""
  60. if node is None:
  61. return
  62. self.postorder(node.lchild)
  63. self.postorder(node.rchild)
  64. print(node.elem, end=" ")
  65. if __name__ == "__main__":
  66. tree = Tree()
  67. tree.add(0)
  68. tree.add(1)
  69. tree.add(2)
  70. tree.add(3)
  71. tree.add(4)
  72. tree.add(5)
  73. tree.add(6)
  74. tree.add(7)
  75. tree.add(8)
  76. tree.add(9)
  77. tree.breadth_travel() # 0 1 2 3 4 5 6 7 8 9
  78. print()
  79. tree.preorder(tree.root) # 0 1 3 7 8 4 9 2 5 6
  80. print()
  81. tree.inorder(tree.root) # 7 3 8 1 9 4 0 5 2 6
  82. print()
  83. tree.postorder(tree.root) # 7 8 3 9 4 1 5 6 2 0

Python树与树算法的更多相关文章

  1. python 树遍历

    使用python实现的树遍历,包括宽度优先和深度优先 ef dfs(): tree = { 'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['F', 'G'], 'D' ...

  2. python 树与二叉树的实现

    1.树的基本概念 1.树的定义 树的定义是递归的,树是一种递归的数据结构. 1)树的根结点没有前驱结点,除根结点之外所有结点有且只有一个前驱结点 2)树中所有结点可以有零个或多个后继结点 2.树的术语 ...

  3. Python 树表查找_千树万树梨花开,忽如一夜春风来(二叉排序树、平衡二叉树)

    什么是树表查询? 借助具有特殊性质的树数据结构进行关键字查找. 本文所涉及到的特殊结构性质的树包括: 二叉排序树. 平衡二叉树. 使用上述树结构存储数据时,因其本身对结点之间的关系以及顺序有特殊要求, ...

  4. python机器学习——决策树算法

    背景与原理: 决策树算法是在各种已知情况发生概率的基础上通过构成决策树来求某一事件发生概率的算法,由于这个过程画成图解之后很像一棵树形结构,因此我们把这个算法称为决策树. 而在机器学习中,决策树是一种 ...

  5. python数据挖掘决策树算法

    决策树是一个非参数的监督式学习方法,主要用于分类和回归.算法的目标是通过推断数据特征,学习决策规则从而创建一个预测目标变量的模型.如下如所示,决策树通过一系列if-then-else 决策规则 近似估 ...

  6. Python 数据结构和算法

    阅读目录 什么是算法 算法效率衡量 算法分析 常见时间复杂度 Python内置类型性能分析 数据结构 顺序表 链表 栈 队列 双端队列 排序与搜索 冒泡排序 选择排序 插入排序 希尔排序 快速排序 归 ...

  7. 传智 Python基础班+就业班+课件 【最新完整无加密视频课程】

    点击了解更多Python课程>>> 传智 Python基础班+就业班+课件 [最新完整无加密视频课程] 直接课程目录 python基础 linux操作系统基础) 1-Linux以及命 ...

  8. Python 常用算法记录

    一.递归 汉诺塔算法:把A柱的盘子,移动到C柱上,最少需要移动几次,大盘子只能在小盘子下面 1.当盘子的个数为n时,移动的次数应等于2^n – 1 2.描述盘子从A到C: (1)如果A只有一个圆盘,可 ...

  9. 数据结构(python)

    列表 list 在头部进行插入是个相当耗时的操作(需要把后边的元素一个一个挪个位置).假如你需要频繁在数组两头增删,list 就不太合适.数组是最常用到的一种线性结构,其实 python 内置了一个 ...

  10. 【Python】关于Python有意思的用法

    开一篇文章,记录关于Python有意思的用法,不断更新 1.Python树的遍历 def sum(t): tmp=0 for k in t: if not isinstance(k,list): tm ...

随机推荐

  1. ASP.NET Core 5.0 MVC 页面标记帮助程序的使用

    什么是标记帮助程序 标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素.标记帮助程序使用 C# 创建,基于元素名称.属性名称或父标记以 HTML 元素为目标. 创建标记 ...

  2. python常见面试题讲解(五)质数因子

    题目描述 功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 ) 最后一个数后面也要有空格 输入描述: 输入一个long型整数 输出描述 ...

  3. Go 疑难杂症汇总

    1. revision v0.0.0: unknown revision v0.0.0 go get -u github.com/uudashr/gopkgs/cmd/gopkgs 报错: [root ...

  4. 02-python简介

    python简介 python认知 1.python简介 89年开发的语言,创始人范罗苏姆(Guido van Rossum),别称:龟叔(Guido). python具有非常多并且强大的第三方库,使 ...

  5. Redis-键

  6. JVM大页内存的学习与使用

    JVM大页内存的学习与使用 原理和背景 操作系统是计算机的重要组成部分. 现代的操作系统一般都采用 段页式内存管理. 段一般是为了管理和权限 页主要是为了虚拟内存和物理内存的映射. 分页管理可以让物理 ...

  7. [转帖]prometheus和node_exporter中的磁盘监控

    https://www.ipcpu.com/2021/04/prometheus-node_exporter/ prometheus和node_exporter中的磁盘监控.md 对于磁盘问题,我们主 ...

  8. [转帖]TLB缓存是个神马鬼,如何查看TLB miss?

    https://zhuanlan.zhihu.com/p/79607142 介绍TLB之前,我们先来回顾一个操作系统里的基本概念,虚拟内存. 虚拟内存 在用户的视角里,每个进程都有自己独立的地址空间, ...

  9. [转帖]TiKV 内存参数性能调优

    https://docs.pingcap.com/zh/tidb/stable/tune-tikv-memory-performance 本文档用于描述如何根据机器配置情况来调整 TiKV 的参数,使 ...

  10. [转帖]Oracle客户端与Oracle数据库兼容矩阵

    https://www.cnblogs.com/kerrycode/p/17666025.html Oracle客户端与Oracle数据库之间是有兼容支持关系的,有些低版本的Oracle Client ...