前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >n种解法破DFS与BFS

n种解法破DFS与BFS

作者头像
公众号guangcity
发布2019-09-20 14:03:10
6160
发布2019-09-20 14:03:10
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

n种解法破DFS与BFS

0.说在前面1.二叉树的层次遍历I1.1 BFS非递归思路11.2 BFS非递归思路21.3 BFS双端队列1.4 BFS递归思路1.5 DFS递归思路2.二叉树的层次遍历II2.1 反转思路2.2 直接插入

0.说在前面

最近实在太忙了,但是算法我时刻不会忘记,每周都刷至少两道,比如我昨天上课无聊,拿手机刷了两道,感觉很easy,或许我刷的比较简答吧,然后今天回到实验室,看了之前给Datawhale编程实践定的任务,是一道层次遍历,于是这道题引发了我的思考,刚好晚上有时间,就花了时间来研究一波,结果真的很有趣。

一件事,简单而又直白;一件事,复杂而又晦涩;我宁愿选择后者,因为他可以激发你的潜能!

今天呢主要来介绍两道题,二叉树的层次遍历I与II,运用的思想为DFS与BFS,实现算法包含递归与非递归!

1.二叉树的层次遍历I

关于DFS与BFS这里不多做介绍,会在后面写出几篇简单文章让大家来看,如果有什么需求,可以留言!

问题

给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。

例如: 给定二叉树: [3,9,20,null,null,15,7],

代码语言:javascript
复制
    3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

代码语言:javascript
复制
[
  [3],
  [9,20],
  [15,7]
]

1.1 BFS非递归思路1

思路

采用传统的队列思路,先入先出思想,层次遍历大家都知道吧,就是一行访问完毕,再访问下一行,很有顺序,而对于二叉树BFS与层次遍历一致,那么这里就是采用BFS队列的思路来操作!

实现

代码实现思路为:首先对特殊情况处理,然后定义一个queue(用来存放每层节点)及结果list。然后进入循环,再次建立一个空的list用来存放每层的节点值,然后对queue循环出队,对出队的节点操作(让左孩子与右孩子入队),所以在代码中引入了同层节点个数的变量length,主要是queue要做修改,所以会发生改变,自然不能直接遍历原先的queue!每层访问完毕,让这层的节点值list添加到结果list中,返回即可!

代码语言:javascript
复制
def levelOrder(self, root):
    res = []
    if root is None:
        return res
    queue = [root]
    # 列表为空时,循环终止
    # 可以省去len(queue)!=0
    while queue and len(queue) != 0:
        # 使用列表存储同层节点
        level_val = []
        # 同层节点的个数
        length = len(queue)
        for i in range(length):
            # 将同层节点依次出队
            h = queue.pop(0)
            if h.left:
                # 左孩子入队
                queue.append(h.left)
            if h.right:
                # 右孩子入队
                queue.append(h.right)
            level_val.append(h.val)
        res.append(level_val)
    return res

1.2 BFS非递归思路2

思路

这个思路与上面类似,不同之处在于,这里在循环里面建立了两个list,而这两个list的作用分别为,一个用来存储每层节点值,一个用来存储每层的左右孩子节点,其实思路1与2基本一致,只是代码风格不一样,建议采用第一种思路写!

实现

代码语言:javascript
复制
def levelOrder(self, root):
    res = []
    if not root:
        return []
    res_list = []
    root_list = [root]
    while root_list and len(root_list)!=0:
        # 每层节点值
        val_list = []
        # 每层左右孩子节点
        level_list = []
        for node in root_list:
            val_list.append(node.val)
            if node.left:
                level_list.append(node.left)
            if node.right:
                level_list.append(node.right)
        # 修改循环条件
        root_list = level_list
        # 结果集中添加每层list
        res_list.append(val_list)
    return res_list

1.3 BFS双端队列

思路

思路同上1

实现

实现思路:采用了collections里面的双端队列deque!具体的用法注释标出了!

代码语言:javascript
复制
def levelOrder(self, root):
    res = []
    if not root:
        return res
    # 导包
    import collections
    # 双端队列定义
    queue = collections.deque()
    queue.append(root)
    # visited = set(root)
    while queue:
        length = len(queue)
        level_val = []
        for _ in range(length):
            # 弹出最左边的节点
            node = queue.popleft()
            level_val.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        res.append(level_val)
    return res

1.4 BFS递归思路

思路

思路就是递归+BFS,具体思路看实现!

实现

首先在结果list中每层插入一个空list,然后循环每一层所有节点,将当前节点直接加入对应层当中,然后更新下一层节点list(更新方法为:将当前及节点的左右孩子入list即可),然后不断递归,直到下一层没有节点了,结束递归!

所以这里是一层层递归,一层层实现,所以名字为BFS递归!

代码语言:javascript
复制
def levelOrder(self, root):
    if root is None:
        return []
    self.res = []
    self.bfs([root], 0)
    return self.res

def bfs(self, level_nodes, level):
    if not level_nodes: 
        return
    self.res.insert(level, [])
    temp = []
    for node in level_nodes:
        self.res[level].append(node.val)
        if node.left: 
            temp.append(node.left)
        if node.right: 
            temp.append(node.right)
    self.bfs(temp, level+1)

1.5 DFS递归思路

思路

思路就是采用深度优先搜索,先选择一个分支不断往下遍历,标记访问过的节点,在去继续往下,如果已经到达终点,回退各个分支节点,进入下一分支,重复前面步骤,直到所有节点访问完毕!

实现

在代码中体现深度优先搜索的为:

代码语言:javascript
复制
self.result[level].append(root.val)

这一行表示为当前层添加节点值!

具体的解释放在注释中!

代码语言:javascript
复制
def levelOrder(self, root):
    # 特殊情况处理
    if not root:
        return []
    # 定义结果list
    self.result = []
    # 调用函数
    self.dfs(root, 0)
    # 返回结果
    return self.result
# dfs函数
def dfs(self, root, level):
    # 这一行很关键,主要是用来为访问下一层的节点添加一个空的list
    if len(self.result) < level + 1:
        self.result.append([])
    # 访问对应level的list,并添加数据
    self.result[level].append(root.val)
    # 左孩子递归
    if root.left:
        self.dfs(root.left, level + 1)
    # 右孩子递归
    if root.right:
        self.dfs(root.right, level + 1)

对于上述的dfs递归的终止条件就是,没有左右孩子了,就结束了,为了更加好看,可以写成如下代码:

改写变动为终止条件变动及左右孩子访问判断条件变动!

代码语言:javascript
复制
def dfs(self, root, level):
    # 递归终止条件
    if not root:
            return
    # 这一行很关键,主要是用来为访问下一层的节点添加一个空的list
    if len(self.result) < level + 1:
        self.result.append([])
    # 访问对应level的list,并添加数据
    self.result[level].append(root.val)
    # 左孩子递归
    self.dfs(root.left, level + 1)
    # 右孩子递归
    self.dfs(root.right, level + 1)

2.二叉树的层次遍历II

上面介绍了5种方法解决二叉树的层次遍历!那么下面又有一道题是对上面的简单处理,下面接着来看!

问题

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如: 给定二叉树 [3,9,20,null,null,15,7],

代码语言:javascript
复制
    3
   / \
  9  20
    /  \
   15   7

返回其自底向上的层次遍历为:

代码语言:javascript
复制
[
  [15,7],
  [9,20],
  [3]
]

2.1 反转思路

反转思路为将一个list直接做反转即可,实现为:

list[::-1]

所以将上面的所有方法最后返回结果直接跟个[::-1],那么这道题就解决了!

2.2 直接插入

上面最后需要做反转,也就是切片,这样的话时间复杂度会变高,有没有更高效率了,当然有了,那就是直接插入了!

怎么改呢?

直接将上面的结果list的append改为insert(0,xxx),就是在每次数据的前面插入,这样就保证的先入的在栈底!也就满足了题意!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • n种解法破DFS与BFS
    • 0.说在前面
      • 1.二叉树的层次遍历I
        • 1.1 BFS非递归思路1
        • 1.2 BFS非递归思路2
        • 1.3 BFS双端队列
        • 1.4 BFS递归思路
        • 1.5 DFS递归思路
      • 2.二叉树的层次遍历II
        • 2.1 反转思路
        • 2.2 直接插入
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档