算法 - 树中所有节点的最大距离

2024-05-27

所以......找到树中两个节点之间的最长路径相当容易。但我想要的是找到从节点出发的最长路径x到树中的另一个节点,对于所有x.

这个问题也可以用以下方式表达:计算从给定的树中可以生成的所有有根树的高度。

One way, of course, is to just do, for all nodes in the tree, a BFS/DFS and remember for each of them the furthest node found. However, this results in O(N2). Is it possible to do better?

EDIT: just to recap, my question is NOT how to find the longest path in the graph. It is how to find the longest path containing a given node x FOR ALL nodes x in BETTER THAN O(N2) time complexity, if possible.


是的,有一个 O(n) 算法。

将树视为无根树 - 只是一个图,其中每个节点都有不形成循环的双向边。

对于具有相邻节点的给定节点 p(例如 a_i),我们将计算高度 Hpa_i。 Height Hpa_i 是子树的高度带根 p(即,对于算法的这一部分,我们暂时考虑有根子树)通过将节点 a_i 视为 p 的父节点而获得。

如果您对从每个节点到叶子的最长路径感兴趣(您的问题加上其标题让人怀疑您实际要计算的内容),那么它只是 max{ Hpa_i for all i }。相应的 i 值给出了最长的路径本身。

另一方面,如果您对最长路径感兴趣throughp,这将是从 { len(p--a_i) + Ha_ip for all i } 中选择的最大对的总和,i 的两个相应值给出最长路径本身。

因此,如果我们有每个节点的高度,那么获得最终结果就是一个简单的 O(n) 工作。

剩下的只是计算所有节点的高度。为此,从特殊的深度优先搜索开始。它接受 2 个节点作为参数。第一个 p 是正在搜索的节点,第二个 q \in {a_i} 是当前被视为 p 的父节点的相邻节点。设 U 为将节点对映射到高度的映射: (p, q) -> Hpq

function search_and_label(p, q)
  if ((p, q) maps to height Hpq in U ) { return Hpq }
  if (p == null) { add (p, q) -> 0 to U and return 0 }
  let h = max(all x adjacent to p, not equal to q) {
            len(p--x) + search_and_label(x, p)
          }
  add (p, q) -> h to U
  return h
  

现在我们可以找到所有的高度。

 Add mappings (p, x)->null to U for all nodes p and adjacent nodes x
 Also add a mapping (p, z)->null to U for all nodes p having < 3 adjacent
 while (U contains a mapping of the form (p, x)->null) 
   search_and_label(p, x) // this replaces the null mapping with a height

这也将是一个 O(n) 计算,因为它在每条边上花费恒定的工作,并且树中的边数为 n-1。

Code

今天下雨了,所以这里有一些代码可以生成一棵随机树,并在 O(n) 时间内用最长的路径信息对其进行标记。首先,典型的输出。每个节点都标有自己的编号,然后是包含该节点的最长路径的长度,最后是该路径上相邻节点的编号。小边缘标签是高度信息。首先是相对子树的高度以及该子树中到叶子的最长路径的节点:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * An undirected graph. It has a builder that fills the graph with a random
 * unrooted tree. And it knows how to decorate itself with longest path
 * information when it's such a tree.
 */
class Graph {

  /**
   * Edge p--q is represented as edges[p][q]=dq and edges[q][p]=dp, where dq and
   * dp are node data. They describe the respective end of the edge:
   * <ul>
   * <li>dq.len == dp.len, the edge length
   * <li>dq.h is the height of subtree rooted at p with q as parent.
   * <li>dq.next is the child of p (with respect to parent q) rooting the max
   * height subtree.
   * </ul>
   */
  final Map<Node, Map<Node, EdgeData>> edges = new HashMap<>();

  /**
   * A node in the graph.
   */
  static class Node {

    final int id; // Unique node id.
    Node a, b;    // Adjacent nodes on longest path.
    double len;   // Length of longest path.

    Node(int i) {
      this.id = i;
    }
  }

  /**
   * Data associated with one end of an edge in the graph.
   */
  static class EdgeData {

    final double len;  // Edge length.
    Double h;          // Subtree height.
    Node next;         // Next node on max length path to leaf.

    EdgeData(double len) {
      this.len = len;
    }
  }

  /**
   * Add a new node to the graph and return it.
   */
  Node addNode() {
    Node node = new Node(currentNodeIndex++);
    edges.put(node, new HashMap<>());
    return node;
  }
  private int currentNodeIndex = 0;

  /**
   * Add an undirected edge between nodes x and y.
   */
  void addEdge(Node x, Node y, double len) {
    edges.get(x).put(y, new EdgeData(len));
    edges.get(y).put(x, new EdgeData(len));
  }

  /**
   * Decorate subtree rooted at p assuming adjacent node q is its parent.
   * Decorations are memos. No subtree is decorated twice.
   */
  EdgeData decorateSubtree(Node p, Node q) {
    Map<Node, EdgeData> adjacent = edges.get(p);
    EdgeData data = adjacent.get(q);
    if (data.h == null) {
      data.h = 0.0;
      for (Map.Entry<Node, EdgeData> x : adjacent.entrySet()) {
        if (x.getKey() != q) {
          double hNew = x.getValue().len + decorateSubtree(x.getKey(), p).h;
          if (hNew > data.h) {
            data.h = hNew;
            data.next = x.getKey();
          }
        }
      }
    }
    return data;
  }

  /**
   * Decorate node p with longest path information. Decorations are memos. No
   * node nor its associated subtrees are decorated twice.
   */
  Node decorateNode(Node p) {
    if (p.a == null) {
      double ha = 0.0, hb = 0.0;
      for (Map.Entry<Node, EdgeData> x : edges.get(p).entrySet()) {
        double hNew = x.getValue().len + decorateSubtree(x.getKey(), p).h;
        // Track the largest two heights and corresponding subtrees.
        if (hNew > ha) {
          p.b = p.a;
          hb = ha;
          p.a = x.getKey();
          ha = hNew;
        } else if (hNew > hb) {
          p.b = x.getKey();
          hb = hNew;
        }
      }
      p.len = ha + hb;
    }
    return p;
  }

  /**
   * Decorate the entire tree. This isn't necessary if the lazy decorators are
   * used as accessors.
   */
  void decorateAll() {
    for (Node p : edges.keySet()) {
      decorateNode(p);
    }
  }

  /**
   * Random tree builder. Parameters are a maximum edge length, tree radius in
   * number of edges, and partitions p[k] giving probabilities of branching with
   * degree k.
   */
  class RandomTreeBuilder {

    final Random gen = new Random();
    final long seed;
    final float[] partitions;
    final int maxLen;
    final int radius;

    RandomTreeBuilder(long seed, float[] partitions, int maxLen, int radius) {
      this.seed = seed;
      this.partitions = partitions;
      this.maxLen = maxLen;
      this.radius = radius;
    }

    private void growTree(Node p, int radius) {
      if (radius > 0) {
        float random = gen.nextFloat();
        float pSum = 0f;
        for (float partition : partitions) {
          pSum += partition;
          if (random < pSum) {
            return;
          }
          Node q = addNode();
          addEdge(p, q, 1 + gen.nextInt(maxLen));
          growTree(q, radius - 1);
        }
      }
    }

    /**
     * Build a tree in the graph. Any existing nodes and edges are erased.
     */
    void build() {
      if (seed != 0) {
        gen.setSeed(seed);
      }
      edges.clear();
      Node p = addNode();
      Node q = addNode();
      addEdge(p, q, 1 + gen.nextInt(maxLen));
      growTree(p, radius);
      growTree(q, radius);
    }
  }

  class TreePrinter {

    PrintStream stream;

    TreePrinter(PrintStream stream) {
      this.stream = stream;
    }

    /**
     * Print graph in the GraphViz DOT language.
     */
    void print() {
      stream.println("graph tree {");
      stream.println(" graph [layout = twopi overlap=false ranksep=1.7]");
      Node p = edges.keySet().iterator().next();
      Node q = edges.get(p).keySet().iterator().next();
      printEdge(p, q);
      print(p, q);
      print(q, p);
      for (Node x : edges.keySet()) {
        printNode(decorateNode(x));
      }
      stream.println("}");
    }

    /**
     * Print edge {@code p--q} in the GraphViz DOT language.
     */
    private void printEdge(Node p, Node q) {
      EdgeData dq = decorateSubtree(p, q);
      EdgeData dp = decorateSubtree(q, p);
      stream.format(" n%d--n%d [label=\"%.0f\" fontsize=8 "
          + "headlabel=\"%.0f:%s\" taillabel=\"%.0f:%s\"]\n",
          p.id, q.id, dq.len,
          dp.h, dp.next == null ? "-" : dp.next.id,
          dq.h, dq.next == null ? "-" : dq.next.id);
    }

    /**
     * Print node p in the GraphViz DOT language.
     */
    private void printNode(Node p) {
      stream.format(" n%d [ label=\"%d (%.0f:%s-%s)\" fontsize=10 ]\n",
          p.id, p.id, p.len,
          p.a == null ? "-" : p.a.id, p.b == null ? "-" : p.b.id);
    }

    /**
     * Print the sub-tree rooted at node p, treating node q as its parent.
     */
    private void print(Node p, Node q) {
      for (Node x : edges.get(p).keySet()) {
        if (x != q) {
          printEdge(p, x);
          print(x, p);
        }
      }
    }
  }

  public static void main(String[] args) throws FileNotFoundException {
    PrintStream stream = args.length > 0
        ? new PrintStream(new File(args[0]))
        : System.out;
    Graph graph = new Graph();
    graph.new RandomTreeBuilder(42L, new float[]{0.3f, 0.1f, 0.3f, 0.2f}, 10, 5)
        .build();
    graph.new TreePrinter(stream).print();
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

算法 - 树中所有节点的最大距离 的相关文章

  • 为什么《破解编码面试》这个例子的时间复杂度是O(k c^k)?

    该问题来自 破解编码面试 第 6 版 问题 V1 11 以下代码打印长度为 k 的所有字符串 其中字符 是按排序顺序排列的 它通过生成所有长度的字符串来做到这一点 k 然后检查每个是否已排序 什么是运行时间 package QVI 11 P
  • 直观地执行不同的排序算法[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Solr 阿拉伯语

    我正在使用 Solr 来索引 3 种语言 阿拉伯语 法语和英语 的文档 我使用了这个 fieldType
  • 如何在单向链表(一次遍历中)中从尾部获取第 n 个节点?

    所以我在一次考试中得到了这个问题 如何从单链表的尾部获取第 n 个节点 每个节点都有一个值和一个下一个 指向下一个值的指针 我们得到这个 getNodeFromTail Node head int x 所以我的做法是通过遍历一次来求出列表的
  • 使用 Numba 加速矢量距离计算

    以下是我为 3 D 环形几何中的距离 平方 计算编写的一些函数 用于该 3 D 空间中的粒子集合 import itertools import time import numpy as np import scipy import num
  • 在 VS Code 文件搜索中,我可以展开(或折叠)所有结果吗?

    在程序的 搜索 窗格中 按 Enter 键后 会列出所有文件 其中一些文件会展开以显示文件中的结果 而其他文件则会折叠 我首先想知道是什么决定了任何给定文件的扩展 其次我想知道如何一次性扩展所有文件 这个问题似乎最接近我的问题 但它是关于不
  • Bing 搜索 API 和 Azure

    我正在尝试以编程方式在 Microsoft Bing 搜索引擎上执行搜索 这是我的理解 有一个 Bing Search API 2 0 很快就会被替换 2012 年 8 月 1 日 新的 API 称为 Windows Azure Marke
  • 处理流星中的长服务器端计算

    我正在使用 jimp https www npmjs com package jimp https www npmjs com package jimp 在meteor JS中生成图像服务器端 换句话说 我正在使用递归算法 计算 图像的像素
  • 有效地合并两个数组 - 一个已排序,另一个未排序

    我正在解决一个问题 该问题有一个由 n 个元素组成的排序数组 后跟一个未排序的长度数组 O logn O 平方 n 如何最有效地对整个列表进行排序 在上述两种情况下我应该使用哪种排序 由于将单个元素插入数组并保持其排序是O n 你不可能变得
  • 自动跟踪算法

    我正在尝试写一个simple跟踪例程来跟踪电影中的某些点 本质上我有一系列 100 帧长的电影 在黑暗背景上显示一些亮点 我每帧有大约 100 150 个点 它们在电影的过程中移动 我想跟踪它们 所以我正在寻找一些有效的 但可能不会过度实施
  • 字符串插值搜索

    对于那些不熟悉插值搜索的人来说 这是一种在排序数组中搜索值的方法 可能比二分搜索更快 您查看第一个和最后一个元素 并 假设数组的内容均匀分布 线性插值以预测位置 例如 我们有一个长度为 100 的数组 其中 array 0 0 和 arra
  • 使用php表单更改href链接

    我正在制作一个带有搜索栏的网站 我想让搜索栏在 搜索 并显示结果后具有交互性 所以我希望 href 根据正在使用的 Id 进行更改 例如 有人搜索 Pinecones 如果它在数据库中 它将有一个 ID 在本例中是 4 一旦他们搜索它 它就
  • 是否有一种算法可以在线性时间内计算数组反转?

    我知道有多少倒转 en wikipedia org wiki Inversion 28discrete mathematics 29 in an n 元素数组可以在 O n log n 操作使用增强型归并排序 http www geeksf
  • 什么是“朴素”算法,什么是“封闭式”解决方案?

    我有一些关于描述算法时使用的术语语义的问题 首先 朴素 算法是什么意思 这与给定问题的其他解决方案有何不同 解决方案还可以采取哪些其他形式 其次 我听到很多人提到 封闭式 解决方案 我也不知道这意味着什么 但在尝试解决递归关系时经常会出现
  • 如何求两个地点的经纬度距离?

    我有一组位置的纬度和经度 怎么找distance从集合中的一个位置到另一个位置 有公式吗 半正矢公式假定地球是球形的 然而 地球的形状更为复杂 扁球体模型会给出更好的结果 如果需要这样的精度 你应该更好地使用文森特逆公式 See http
  • LRU算法,实现这个算法需要多少位?

    我有一个关于 LRU 算法的小问题 如果您有一个包含四个块的高速缓存 那么需要多少位来实现该算法 假设您指的是 4 路组关联缓存 完美 LRU 本质上是按照使用顺序为每一行分配一个精确的索引 您也可以将其视为 年龄 因此 4 个元素中的每一
  • 贝尔曼福特算法可以有任意的边顺序吗?

    我刚刚开始学习新算法 但当我阅读 极客为极客而写的贝尔曼福特算法 时 我陷入了困境 http www geeksforgeeks org dynamic programming set 23 bellman ford algorithm h
  • 在树结构的 Big-O 表示法中:为什么有些来源引用 O(logN),有些来源引用 O(h)?

    在研究遍历二叉搜索树的任何算法的复杂性时 我看到两种不同的方式来表达同一件事 版本 1 最坏情况下的遍历算法对树的每个高度进行一次比较 因此复杂度是O h 版本 2 最坏情况下的遍历算法对树的每个高度进行一次比较 因此复杂度是O logN
  • JavaScript 中的埃拉托斯特尼筛法对大量数据无限运行

    我一直在尝试写埃拉托斯特尼筛法 http en wikipedia org wiki Sieve of EratosthenesJavaScript 中的算法 基本上我只是按照以下步骤操作 创建从 2 到 n 1 的连续整数列表 令第一个素
  • 在一个区域中拟合二维多边形的算法?

    这有标准吗 算法名称 说 我有 10 个不同大小的多边形 我有一个特定大小的区域 我想知道如何填充该区域中的最多多边形 以及它们是如何拟合的 笔记 多边形可以根据限制集进行旋转 一个可能的名称是包装问题 http en wikipedia

随机推荐

  • Gluon 移动 iOS 音频播放器

    由于 JavaFx Media 尚未移植到移动平台 任何人都可以帮助我使用本机 iOS APi 来播放声音 mp3 文件 该文件将存储在我的 gluon 项目的 main resources 文件夹中 在 Android 上 我们可以轻松地
  • java中线程之间的通信:如果另一个线程完成则停止一个线程

    仅当另一个线程也在运行时 如何才能使一个线程运行 这意味着 如果我从一个线程中的运行返回 那么我希望另一个线程也停止运行 我的代码看起来像这样 ClientMessageHandler clientMessagehandler new Cl
  • 使用 Python Paramiko 进行端口转发和开放 SFTP

    我已经使用 ssh 在服务器上执行命令 现在我必须对不同的 IP 执行另一个 ssh 同时保持旧的 ssh 处于活动状态 这个新 IP 是端口转发 然后将用于执行 SFTP 我面临的问题是两个 ssh 连接都在同一端口上 因此无法进行第二次
  • 如何获取subprocess.run启动的进程的pid并杀死它

    我使用的是 Windows 10 和 Python 3 7 我运行了以下命令 import subprocess exeFilePath C Users test test exe subprocess run exeFilePath 使用
  • 计算 TCP 重传次数

    我想知道在LINUX中是否有一种方法可以计算一个流中发生的TCP重传的次数 无论是在客户端还是服务器端 好像netstat s解决了我的目的
  • 源生成器:有关引用项目的信息?

    我开始使用 C 源生成器 我想要的是开始一个git describe tags long处理并填充静态GitVersion具有当前标签和哈希码作为属性的类 问题是 我没有关于引用项目的目录的信息 所以我不知道在哪里运行 git 进程 我在其
  • 连接查询或子查询

    开发人员何时使用联接而不是子查询是否有经验规则 或者它们是否相同 第一个原则是 准确地陈述查询 第二个原则是 简单明了地陈述查询 这是你通常做出选择的地方 第三个是 陈述查询 以便它能够有效地处理 如果它是一个具有良好查询处理器的数据库管理
  • Base 64 编码的有效字符范围

    我对以下内容感兴趣 是否有一个字符列表never作为 Base 64 编码字符串的一部分出现 例如 我不确定这种情况是否会发生 如果原始输入实际上有 作为它的一部分 编码会有所不同吗 这是我可以发现的 RFC 4648 http www r
  • 从函数参数构建模板?

    template
  • 通过 VPN 容器路由 Docker 容器流量

    我在我的上安装了几个容器洛克Pro64 运行 openmediavault 的 ARMv8 处理器 rev 2 v8 版本 4 1 27 1 Arrakis 一切都运转良好 我使用的容器包括 Transmission Jellyfin Ra
  • IntelliSense:对象具有与成员函数不兼容的类型限定符

    我有一个名为 Person 的类 class Person string name long score public Person string name long score 0 void setName string name voi
  • ECS 上蓝/绿部署所需的 Cloudformation 脚本

    我正在尝试编写一个云形成模板具有蓝绿部署支持的 AWS ECS 这项蓝绿功能最近由 AWS 在 ECS 中添加 但在云形成模板中找不到任何更新它的参考 他们提供了有关如何通过 UI 而不是通过云形成来完成此操作的文档 我猜想 AWS 可能不
  • 集到子集点云匹配

    我有两个 3d 坐标的点云 一个是另一个的子集 包含更少的点 它们的规模相同 我需要做的是找到两者之间的平移和旋转 我看过点云库 迭代最近点 https en wikipedia org wiki Iterative closest poi
  • 如何防止 Firefox 缓存

    我尝试了很多可能的解决方案 但无法解决问题 这些不起作用 有人可以帮忙吗 我正在使用jsp servlet application 是websphere Portal 6 1 的一个portlet 切勿
  • 在跨平台的 npm 脚本中使用环境变量

    我正在构建一个 package json 并使用 npm run 来运行一些脚本 确切地说 https docs npmjs com misc scripts https docs npmjs com misc scripts 我的脚本需要
  • Kotlin 无法编译库

    There s this http github com theapache64 BugMailer我创建的库是为了通过电子邮件报告异常情况 它适用于 Android Java 项目 但不适用于 Android Kotlin 当我添加库的编
  • 如何对URL进行分类? URL 的特点是什么?如何从 URL 中选择和提取特征

    我刚刚开始研究分类问题 这是一个两类问题 我的训练模型 机器学习 必须决定 预测是允许 URL 还是阻止它 我的问题非常具体 如何对 URL 进行分类 我应该使用普通的文本分析方法吗 URL 的特点是什么 如何从URL中选择和提取特征 我假
  • 使用事件实现观察者模式

    我正在开发一个 Silverlight 应用程序 其中过度使用了观察者模式 在我的实现中 我创建了两个接口IObservable
  • 为什么某些 float < integer 比较比其他比较慢四倍?

    将浮点数与整数进行比较时 某些值对的计算时间比类似大小的其他值要长得多 例如 gt gt gt import timeit gt gt gt timeit timeit 562949953420000 7 lt 56294995342100
  • 算法 - 树中所有节点的最大距离

    所以 找到树中两个节点之间的最长路径相当容易 但我想要的是找到从节点出发的最长路径x到树中的另一个节点 对于所有x 这个问题也可以用以下方式表达 计算从给定的树中可以生成的所有有根树的高度 One way of course is to j