当前位置:   article > 正文

二叉搜索树c++_树、二叉树、二叉搜索树[06]

二叉搜索树决策树

0630c5ad58cad082712c30461ad2a79d.png

一、引入:

数据结构的划分:​zhuanlan.zhihu.com
405c973ef3fc4fd302b674692eae81e9.png
  • 二维数据结构(可以想象成从一维泛化而来)
    • 基础:树 tree(一维的链表分叉有两个的时候)、图 graph
    • 高级(在树的基础上加了很多的特殊判断和约定条件):二叉搜索树 binary search tree(red-black tree,AVL)、堆 heap、并查集 disjoint set、字典树 Trie、etc

e52830eb8d09f96992599c7bac8b45bd.png
单链表结构

根据上图的单链表的数据结构,如果有next1、next2、next3指向多个结点的话,会变成什么?(见下图)

9ad6a421cbdd26bee7863f3e280af6b9.png
树(Tree)

现实中用的最多的就是二叉树

a0dc8b0e6c6815e8ba44caeacf0ddd02.png
二叉树结构

树于图最关键的差别是:有没有环?(有环为图,无环为树)

链表(Linked List)是特殊化的树(链表有两个next指针就是树) 树(Tree)是特殊话的图(也就是没有环的图就是树)

二、结点的定义

  1. Python
  1. class TreeNode:
  2. def __init__(self,val):
  3. self.val = val
  4. self.left, self.right = None, None

2. C++

  1. struct TreeNode{
  2. int val;
  3. TreeNode *left;
  4. TreeNode *right;
  5. TreeNode(int x): val(x),left(NULL),right(NULL){ }
  6. }

3. Java

  1. public classs TreeNode{
  2. public int val;
  3. public TreeNode left,right;
  4. public TreeNode (int val){
  5. this.val = val;
  6. this.left = null;
  7. this.right = null;
  8. }
  9. }

三、分析:

为什么会出现树形结构?

人类本身生活在一个三维的世界、思维的世界,人类本身的工程实践,其实就是在二维的基础上解决的,而树本身的话就是人经常会碰到的一种情况,就类似于飞机本身的话并不是人发明创造的,而是它看到鸟在飞之后想到的。

例1:斐波那契的数列,其实它扩散出去的结点是一个树状的结点。

f74235f8dd4f8cf1a513644792064827.png
递归树

例2:阿尔法狗,或者棋类的游戏

6721e02cd06be64fde9157d149fbf4e1.png

棋盘的状态就是向外扩散成不同的状态,每个不同的状态再往下面走,棋盘的状态本身的话也是一个树形结构,叶子结点就是棋盘的某一个终极形态,不同的棋的树的空间叫做状态树的空间,同时还有一个叫博弈的空间,也可以叫做决策树(decision tree)的空间,它们的复杂度不一样,决定了这个棋或者这个游戏它的复杂度。我们的人生状态(做不同的决定)也是类似于树。

四、树的遍历

二叉树的遍历:(记忆小技巧:遍历时,先左后右不变,根在前就是前序,根在中就是中序,根在后就是后序[1]

  1. 前序(Pre-order):根 - 左 - 右

a3694bdf998f519f4814aa41ae7438fc.gif

2. 中序(In-order):左 - 根 - 右

77d6b2d3d9c9edefac0f9d50949aff01.gif

3. 后序(Post-order):左 - 右 - 根

14f20bea827882d583d87c2aec0cd2ae.gif
  1. # 需要在心里做成机械化的记忆
  2. def preorder(self,root):
  3. if root:
  4. self.traverse_path.append(root.val)
  5. self.preorder(root.left)
  6. self.preorder(root.right)
  7. def inorder(self,root):
  8. if root:
  9. self.inorder(root.left)
  10. self.traverse_path.append(root.val)
  11. self.inorder(root.right)
  12. def postorder(self,root):
  13. if root:
  14. self.postorder(root.left)
  15. self.postorder(root.right)
  16. self.traverse_path.append(root.val)
为什么基于递归实现? 1. 树的定义本身,没法有效的进行所谓的循环,也可以强行循环(比如广度优先遍历),但是很多情况这种结构写循环相对比较麻烦,而写 递归调用相对比较简单,关于树的相关操作,不要怕递归,要拥抱递归。
2. 经常更好的一种方式: 直接对左结点调用相同的遍历函数
3. 递归本身不存在所谓的效率差、效率低的问题,只要程序本身的算法复杂度没有写残即可!
例如:斐波那契数列如果只是傻递归,没有把中间结果储存起来,导致本身线性可以解决的问题,需要指数级的时间复杂度才能解决,这是不合理的。锅并不在递归,而是没有使用缓存,
4. 递归与非递归: 递归是不是一定会慢一点?其实慢的地方就是它要多开一些栈,如果深度很深的话,的确可能有这样的问题,但是一般情况下,现在的计算机的存储方式和编译器对于递归,特别是尾递归的优化,我们就直接认为递归和循环的效率是一样的。不需要刻意的规避这个问题。

五、二叉搜索树(Binary Search Tree)

621a40ec9ef972bcbe85673a91938816.png

普通的树,查找结点,遍历一遍复杂度为O(n),实际与链表没有区别,没有实际意义。一般情况会把树的结构变得更加的有序:

af74aa5091c93cf055abb8af8c1820ec.png

二叉搜索树常见操作:

  1. 查询(复杂度不再是O(n),是logn)
  2. 插入新结点(创建),(复杂度不再是O(n),是logn)
  3. 删除

示例:

leetcode94 二叉树的中序遍历​leetcode-cn.com
  1. class Solution(object):
  2. def inorderTraversal(self, root):
  3. """
  4. :type root: TreeNode
  5. :rtype: List[int]
  6. """
  7. res =[]
  8. if root:
  9. res += self.inorderTraversal(root.left)
  10. res.append(root.val)
  11. res += self.inorderTraversal(root.right)
  12. return res

参考

  1. ^动态图片借鉴 https://www.pianshen.com/article/7106254596/
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/606781
推荐阅读
相关标签
  

闽ICP备14008679号