zoukankan      html  css  js  c++  java
  • 【Scala笔记——道】Scala Tree Fold深度优先遍历详解

    Tree 定义

    简化定义Scala Tree结构,包含两个部分: Branch和Tree。为了简化数据结构,Branch只包含 Tree类型的 左节点 和 右节点, Leaf包含具体 Value

    sealed trait Tree[+A]
    
    case class Leaf[A](value: A) extends Tree[A]
    case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]

    深度优先遍历 DFS

    树的遍历右两种方式:
    - 深度优先
    - 广度优先
    这里用DFS 实现,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。

    具体搜索顺序可以参考附图
    1. 搜索根节点 左子树
    2. 搜索当前树的左子树
    3. 搜索当前树的左子树
    4. 返回父节点,搜索父节点 右子树
    5. 搜索当前树的左子树
    6. 返回父节点,搜索父节点 右子树
    7. 返回父节点, 返回父节点,返回父节点,搜索右子树
    8. ….

    DFS

    我们从一道题来熟悉Scala遍历操作,求Scala树中节点总数
    按照DFS 思想实现代码如下

      def countNodes[A](tree: Tree[A]): Int = {
        def go[A](tree: Tree[A], sum: Int): Int = tree match {
          case Leaf(v) => sum + 1       //叶子节点 sum+1
          case Branch(left, right) => sum + 1 + go(left, 0) + go(right, 0)  //分支节点 sum = sum + 1 + 左子树节点总数 + 右子树节点总数
          case _ => 0
        }
        go(tree, 0) //递归
      }

    结合【Scala笔记——道】Scala List HOF foldLeft / foldRight 中讲到的List fold思想

    我们将countNode 方法的遍历进行抽象化
    ,首先一个函数最重要的就是输入 / 输出,参考List fold,不难理解对Tree的函数操作必然是将Tree[A]转化为 [B],我们这里实现的简化树模型中,Value的具体存储都在叶子节点中,因此

      def deepFirstSearch[A, B](tree: Tree[A])(f: A => B)...  = tree match {
          case Leaf(value) => f(value)
          ...
      }

    其次,将DFS 搜索的过程进行抽象。对每一个 枝点,首先搜索 枝点的左节点,得到左节点执行结果以后,再搜索右节点,得到右节点执行结果以后,执行 对左右子树 函数结果的 函数操作,因此

      def deepFirstSearch[A, B](tree: Tree[A])(f: A => B)(g: (B, B) => B) : B  = tree match {
          case Leaf(value) => f(value)
          case Branch(l, r) => g( deepFirstSearch(l), deepFirstSearch(r) )
      }

    使用

    通过几个小例子来实践deepFirstSearch

    获取Tree[Int]中最大值

    def maximum(tree: Tree[Int]): Int =
        deepFirstSearch(tree)(l => l)(_ max _)

    求树的最大深度

      def depth[A](tree: Tree[A]): Int =
        deepFirstSearch(tree)(_ => 1)(_.max(_) + 1)

    MAP函数 将A 转化为B

    def map[A, B](tree: Tree[A])(f: A => B): Tree[B] = {
        deepFirstSearch(tree)( x => (Leaf(f(x)): Tree[B]))( (a, b) => Branch(a, b))

    测试如下

      def main(args: Array[String]): Unit = {
    
        val tree = Branch(
                      Branch(
                        Branch
                          (Leaf(1),
                            Branch(
                              Leaf(7),
                              Branch(
                                Leaf(8),
                                Leaf(9)
                              ))),
                        Branch(
                          Leaf(34), Leaf(4))),
                      Branch(
                        Leaf(5), Leaf(6)))
    
        println("Max value :" + maximum(tree))
    
        println("Depth :" + depth(tree))
    
        println("Map :" + map(tree)(x => if(x%2 == 0) Branch(Leaf(1), Leaf(2)) else x))
      }

    结果如下

    Max value :34
    Depth :6
    Map :Branch(Branch(Branch(Leaf(1),Branch(Leaf(7),Branch(Leaf(Branch(Leaf(1),Leaf(2))),Leaf(9)))),Branch(Leaf(Branch(Leaf(1),Leaf(2))),Leaf(Branch(Leaf(1),Leaf(2))))),Branch(Leaf(5),Leaf(Branch(Leaf(1),Leaf(2)))))
  • 相关阅读:
    遇到的相关问题总结
    AI测试相关文章
    常用模块文档地址
    09-微服务接口:怎么用Mock解决混乱的调用关系
    03-思维方式:用一个案例彻底理解接口测试的关键逻辑
    1-基础:跳出细节看全局,接口测试到底是在做什么?
    21-Python并发编程之Futures
    使用原生php读写excel文件
    在for、foreach循环体中添加数组元素
    eval函数的坑
  • 原文地址:https://www.cnblogs.com/cunchen/p/9464096.html
Copyright © 2011-2022 走看看