1 树的基本知识

1.1 概念

树,结合了链表与图。

①单链表:一个数据域+一个指针域;树:一个数据域+多个指针域。

②树是无环连通图。

1.2 定义

树是N个节点的有限集合,N=0为空树,N>0时应当满足:

①有且仅有一个特定的称为根的节点;

②N>1时,其余节点可分为m个互不相交的有限集合,其中每个有限集合自身又是一棵树(递归定义)。

1.3 重要的树:二叉树(属于有序树)

满二叉树、完全二叉树、二叉搜索树(BST)、平衡二叉树(典型应用是平衡二叉搜索树)

2 树的存储结构

2.1顺序存储

[类似于邻接表的存储方法] 父子节点表示法、父节点表示法、子节点表示法

2.2链式存储

类似于链表:

#普通的树中的节点
class TreeNode:def __init__(self,val=0,children=None):self.val=valself.children=children if children is not None else []#二叉树中的节点
class TreeNode(object):def __init__(self,val=0,left=None,right=None):self.val=valself.left=leftself.right=right

3 树的基本操作

​ 关于树的增删改查,其核心是“查查查查”,即查找、搜索、遍历是树的核心操作。而对于树的查找、搜索、遍历,主要有两种思路,分别是深度优先搜索(DFS)和广度优先搜索(BFS)。

3.1 深度优先搜索(DFS)

​ 深度优先搜索是一种递归式的方法,因为它一定要按一定的规则规律向深处探索,而这种规律便分为三种,分别是前序、中序和后序。以前序为例,其顺序为“根、左、右”,那么对于每一个子树,都要按照这个顺序,先搜索根节点,然后搜索其左子树,再搜索其右子树,形成递归调用栈,触底后回溯。而中序和后序,只是几个递归句子和操作句子的顺序不太一样罢了。

这里我们假设操作句子为print,那么:

#1 前序
def dfs(TreeNode root):if not root:return Noneprint(root.val) #操作dfs(root.left)dfs(root.right)#2 中序
def dfs(TreeNode root):if not root:return Nonedfs(root.left)print(root.val)#操作dfs(root.right)#3 后序
def dfs(TreeNode root):if not root:return Nonedfs(root.left)dfs(root.right)print(root.val)#操作

​ 可以看到,深度优先搜索的关键点,一方面在于“深度优先”,也就是它先伸到底的这个特性;另一方面就是它带来的附加特性,即它必须要依照一定的规则、顺序来搜索,而这个规则要适用于任何子树,又因为树本身的定义就是递归的,所以自然也要递归地进行步骤,而一旦递归了,自然也就实现了“深度”,因为递归的终止条件是触底,触底后才会回溯,才能得到之前的结果,才能完成这一步,才能继续进行下去。

3.2 广度优先搜索(BFS)

​ 广度优先搜索是一种普通的迭代遍历方法,其规则是既定的,即从根节点开始,按层次从上到下,同层次内从左到右 “ 访问(/操作)” 每一个节点,每个节点只会经历一次(与DFS相对,DFS虽然也只操作一次,但会经历多次)。

​ 广度优先搜索的关键点是借助“队列”数据结构,对于队列的使用只需创建一个空列表,入队用append在队尾加入,出队用pop(0)在队头弹出。

#1 常规的BFS实现代码
def bfs(self,root):q=[]q.append(root)while q and q[0]: #这句and q[0]太重要了!!!因为有可能[none],也就是根节点为none#这里q[0]是为了避免root==None的情况,但它只会在初始条件时存在,因此也可改写成3cur=q.pop(0)if cur.left:q.append(cur.left)if cur.right:q.append(cur.right)return xxx#2 分层次的BFS实现代码
def bfs(self,root):q=[]q.append(root)#这里q[0]是为了避免root==None的情况,但它只会在初始条件时存在,因此也可改写成3while q and q[0]: #控制层次级别操作l=len(q)for i in range(l): #控制同一层内节点级别操作cur=q.pop(0)if cur.left:q.append(cur.left)if cur.right:q.append(cur.right)return xxx#3 改变初始空值处理方法
def bfs(self,root):if not root:returnq=[]q.append(root)while q: #控制层次级别操作l=len(q)for i in range(l): #控制同一层内节点级别操作cur=q.pop(0)if cur.left:q.append(cur.left)if cur.right:q.append(cur.right)return xxx
  • 分层次的合理性:并没有增加时间复杂度,虽然看似变成了双重循环,但是实际上内部循环占比和为1,相当于那种加权感,仍然实质上是一层循环。

  • 分层次的好处:将层次级操作与层内节点级操作分离开来,从最表象的优点来看,至少可以知道每一层节点的数目,以及节点所处的层数了。

4 重点例题

4.1 二叉树的最大深度

①BFS
class Solution(object):def maxDepth(self, root):""":type root: TreeNode:rtype: int"""q=[]q.append(root)cnt=0while q and q[0]:l=len(q)cnt+=1for i in range(l):cur=q.pop(0)if cur.left:q.append(cur.left)if cur.right:q.append(cur.right)return cnt
②DFS(后序遍历)
class Solution(object):def maxDepth(self, root):""":type root: TreeNode:rtype: int"""if not root:return 0#递归结束条件if not root.left and not root.right:return 1#递归大致逻辑return max(self.maxDepth(root.left),self.maxDepth(root.right))+1
③DFS(先序遍历)
class Solution(object):def dfs(self,root,temp,ans):#递归终止条件if not root:self.ans=max(self.ans,temp) #每次触底,都要看看temp能否更新最大深度#递归主体temp+=1 #每调用一次自身,都记录一次(相当于先序遍历中对于自己的那一句操作)self.dfs(root.left,temp,self.ans)self.dfs(root.right,temp,self.ans)def maxDepth(self, root):""":type root: TreeNode:rtype: int"""temp=0self.ans=0self.dfs(root,temp,self.ans)return self.ans
4.2 二叉树的最小深度

​ 这里我一开始犯了个错误,就是没仔细想就直接莽撞地把求最大深度的地后序DFS套用了,但实际上它们是非常不一样的。最大深度的终止(触底)条件是它是叶节点,当然,最小深度的触底条件相同,但是有的时候它只有右子树没有左子树,这个时候不能莽撞地取了左子树作为min(第一次提交就是这么错的),也就是说,必须要么左右子树都有,才min地递归下去,要么左右子树都没有,就等待触底地递归下去,要是只有左子树或者只有右子树,则要单侧递归下去(当然,如果它的子树就恢复了,还是继续可以双min递归的。)

①DFS后序
#1 丑陋的敷衍版
class Solution(object):def minDepth(self, root):""":type root: TreeNode:rtype: int"""if not root:return 0if root.left and root.right:return min(self.minDepth(root.left),self.minDepth(root.right))+1if root.left and not root.right:return self.minDepth(root.left)+1elif root.right and not root.left:return self.minDepth(root.right)+1elif not root.left and not root.right:return 1#2 整理清爽版
class Solution(object):def minDepth(self, root):""":type root: TreeNode:rtype: int"""#递归终止条件if not root:return 0#if root.left==root.right: 这句不可以,因为好像是true and true这样是布尔型,但用==就是指相同的子树了if (root.left and root.right) or (not root.left and not root.right):return min(self.minDepth(root.left),self.minDepth(root.right))+1if root.left:return self.minDepth(root.left)+1else:return self.minDepth(root.right)+1
②DFS(先序)
class Solution(object):def dfs(self, root, temp, ans):#1 递归终止条件if not root: #由于min的特殊性,可能会导致self.ans无法增长,因此选择触底后再判断就可以解决这个问题if self.ans!=0:self.ans=min(self.ans,temp)else:self.ans=tempreturn #这里必须有个空return来终止递归temp+=1 #操作#对于左右的操作分情况讨论,就可以做到符合最小深度性质,避免之前说的那种问题if (root.left and root.right) or (not root.left and not root.right):self.dfs(root.left,temp,self.ans)self.dfs(root.right,temp,self.ans)return #这里用上return,才能避免下面的需要套一个大elseif root.left:self.dfs(root.left,temp,self.ans)else:self.dfs(root.right,temp,self.ans)returndef minDepth(self, root):""":type root: TreeNode:rtype: int"""temp=0self.ans=0self.dfs(root,temp,self.ans)return self.ans
③BFS
class Solution(object):def minDepth(self, root):""":type root: TreeNode:rtype: int"""q=[]q.append(root)cnt=0while q and q[0]:l=len(q)cnt+=1for i in range(l):cur=q.pop(0)if cur.left:q.append(cur.left)if cur.right:q.append(cur.right)if not cur.left and not cur.right:return cnt#break不可以的,因为break只能跳出一层循环,无法跳出嵌套,但是如果在外面硬加一个break就会变成强行无条件跳出了,所以这里只能returnreturn cnt #这个是为了解决[]要返回0的问题
4.3将有序数组转换为二叉搜索树

方法:二分递归
  1. 构造思路:

    ​ 首先,知道一个常识,即“二叉搜索树的中序遍历(左中右)是升序序列”,那么我们要从一个升序序列构造一棵二叉搜索树,则是把它反过来的操作,这个升序数组递归地符合“左边是左子树,中间是根节点,右边是右子树”,但是我们要构造一棵树,只能先构造根节点再构造其左子树右子树,因此要从中间开始递归地先构造根节点,再左右子树。

    ​ 其次,要求是高度平衡的二叉树,所以左右子树高度差不能超过1,使用二分法可以确保其平衡,即每次左右子树都最多差1,这样递归地进行下去,左右子树最多还是差1(仔细模拟着想一下就可以)。

  2. 关于唯一性:

    ​ 即使明确要求了它高度平衡,其仍然不唯一,因为在二分时如果有偶数,仍然有选左边和选右边的区别。因此即使是要求高度平衡,根据中序遍历序列构造出的二叉树也不是唯一的(不要求高度平衡的就更别说了)。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):def sortedArrayToBST(self, nums):""":type nums: List[int]:rtype: TreeNode"""if not nums:returnmid=len(nums)//2root=TreeNode(val=nums[mid])root.left=self.sortedArrayToBST(nums[:mid])root.right=self.sortedArrayToBST(nums[mid+1:])return root
4.4 从中序与后序遍历序列构造二叉树

①递归

​ 前面说了,仅由一个遍历序无法确定唯一的一个二叉树,但知道两个序的遍历就能确定一个了。整体思路,因为是树相关的题目,所以还是递归或者迭代,然后就是考虑中序和后序怎么充分发挥自身特性并结合起来了,其结合的思路大抵如下:

​ 中序遍历(左中右),特点是可以二分地递归构造出这棵树(上一个题已经充分展现和理解了这一点);而后序遍历(左右中),其特点是对于每一颗子树的部分,最后一位都是它的根节点(其实推广一下,如果知道的是前序遍历,也可以利用其第一位是根节点的特性)。分别理解了它们各自的特性之后,还有很关键的一点就是把它们衔接起来:①首先通过后序遍历的末尾得到根节点,然后在中序找到这个节点并记录下其位置,则是中序二分的分点;②因为后序想要找下一层的根节点需要跳几格,但不知道怎么找,这时候就要借助到中序上一步二分开左右的长度了,通过这样长度的跨越,就可以找到新的两个分点,再从中序序列里进行二分,自此递归地进行下去。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):def buildTree(self, inorder, postorder):""":type inorder: List[int]:type postorder: List[int]:rtype: TreeNode"""#递归终止条件if not inorder and not postorder:return#大体实现逻辑a=postorder[-1]p=inorder.index(a)leftin=inorder[:p]rightin=inorder[p+1:]ll=len(leftin)lr=len(rightin)next_left=postorder[ll-1]next_right=postorder[ll+lr-1]root=TreeNode(val=a)root.left=self.buildTree(leftin,postorder[:ll])root.right=self.buildTree(rightin,postorder[ll:ll+lr])return root
②迭代

……有点复杂…有时间再说吧

4.5 从前序与中序遍历序列构造二叉树


和上一题基本完全一样。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):def buildTree(self, preorder, inorder):""":type preorder: List[int]:type inorder: List[int]:rtype: TreeNode"""#递归终止条件if not preorder and not inorder:return#整体实现逻辑a=preorder[0]p=inorder.index(a)leftin=inorder[:p]rightin=inorder[p+1:]ll=len(leftin)root=TreeNode(val=a)root.left=self.buildTree(preorder[1:ll+1],inorder[:ll])root.right=self.buildTree(preorder[ll+1:],inorder[ll+1:])return root

datawhale组队学习笔记(3)树相关推荐

  1. DQN相关知识总结及演员-评论员算法介绍(DataWhale组队学习笔记)

    DQN基本概念及相关技巧 DQN(Deep Q-network)即深度Q网络,通过结合神经网络技术和价值函数近似,采用目标网络和经历回放的方法来进行网络的训练. 价值函数近似 在面对现实中的强化学习任 ...

  2. datawhale组队学习笔记(2)链表

    链表基础知识: 结构: ①逻辑结构:集合.线性结构(一对一).树形结构(一对多).图结构(多对多): ②存储结构:顺序存储(顺序表).链式存储(链式表).索引存储.散列存储. 2.链表分类: ①单链表 ...

  3. Datawhale组队学习周报(第038周)

    本周报总结了从 11月01日至11月07日,Datawhale组队学习的运行情况,我们一直秉承"与学习者一起成长的理念",希望这个活动能够让更多的学习者受益. 第 30 期组队学习 ...

  4. Datawhale组队学习周报(第035周)

    希望开设的开源内容 目前Datawhale的开源内容分为两种:第一种是已经囊括在我们的学习路线图内的Datawhale精品课,第二种是暂未囊括在我们的学习路线图内的Datawhale打磨课.我们根据您 ...

  5. Datawhale组队学习周报(第032周)

    希望开设的开源内容 目前Datawhale的开源内容分为两种:第一种是已经囊括在我们的学习路线图内的Datawhale精品课,第二种是暂未囊括在我们的学习路线图内的Datawhale打磨课.我们根据您 ...

  6. 第8期Datawhale组队学习计划

    第8期Datawhale组队学习计划马上就要开始啦 这次共组织15个组队学习,涵盖了AI领域从理论知识到动手实践的内容 按照下面给出的最完备学习路线分类,难度系数分为低.中.高三档,可以按照需要参加 ...

  7. 组队学习笔记Task1:论文数据统计

    数据分析第一次组队学习笔记--Lizzy @Datawhale Task1:论文数据统计 学习主题:论文数量统计(数据统计任务),统计2019年全年,计算机各个方向论文数量: 学习内容:赛题理解.Pa ...

  8. Datawhale组队学习之集成学习——Task 6 Boosting

    Datawhale组队学习之集成学习--Task 6 Boosting 一.Boosting方法的基本思路 二.Adaboost算法 1.Adaboost基本原理 2.使用sklearn对Adaboo ...

  9. Datawhale组队学习周报(第047周)

    本周报总结了从 2021年01月03日至2022年01月09日,Datawhale组队学习的运行情况,我们一直秉承"与学习者一起成长的理念",希望这个活动能够让更多的学习者受益. ...

最新文章

  1. 使用intellij idea制作可执行jar文件
  2. 给批量用户设磁盘配额
  3. Socket编程基本流程实践
  4. git merge最简洁用法
  5. 双数据源其中一个数据源的dao文件报BindingException
  6. Boost:基于Boost的posix聊天的客户端测试程序
  7. Vue+stylus实现自定义文字的loading组件
  8. 第一、第二、第三范式之间的理解和比较(转载)
  9. .NET Core 3.0 中间件 Middleware
  10. linux bash命令_Ultimate Linux命令行指南-Full Bash教程
  11. Java和web前端,IT新人该如何选择?
  12. 明年起Polkascan不再为Kulupu提供服务
  13. python networkx教程_Python社交网络——NetworkX入门
  14. ETL学习之八:添加日志记录
  15. Listview刷新数据
  16. matlab神经网络工具箱简介
  17. 沟通的艺术I:什么是沟通
  18. linux启动lighttpd服务,Linux下Lighttpd的安装配置
  19. 网络字节序与主机字节序的转换 - HEN_MAN的专栏 - 博客频道 - CSDN.NET
  20. Abbirb120型工业机器人_工控产品-ABB IRB120工业机器人-IRB120

热门文章

  1. traceroute命令---Linux学习笔记
  2. 机房管理系列之工作站
  3. SQLserver语句命令
  4. 《R语言数据挖掘》----1.15 结果可视化
  5. 代码添加ProgressBar 进度条
  6. SGU 117 Counting
  7. squid代理服务器详解
  8. Markdown基本语法总结
  9. 有关 MyEclipse-export runnable jar file选项 launch configuration里面没有可以选择的东西的解决方法...
  10. windows 下 配置 github