- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
在我的项目中,我将给定的移动应用程序表示为有向图。移动应用程序的每个屏幕都充当图形节点和从一个屏幕到下一个屏幕的边链接。边缘存储在每个父屏幕内。还使用深度优先搜索将每条边分类为 TREE、FORWARD、CROSS 或 BACK 边。现在一切正常。
我的问题是我需要将两个图合并为一个图。这两个图可能代表移动应用程序的不同构建。一个版本可能有以下链:
Screen#1 -> Screen#2 -> Screen#3
下一个版本可能有如下链:
Screen#1 -> Screen#X -> Screen#2 -> Screen#3
我需要能够合并这两个图以得到下图:
Screen#1 -> Screen#X -+
| |
+------------> Screen#2 -> Screen#3
我可以测试每个节点与其他每个节点是否相等。但我似乎无法设计出一种算法来将这两个图合并为一个。
我在 Internet 上浏览了很多次,但一直找不到关于如何合并图形的站点、讲座或研究论文。我当前的实现(非常有问题)是这样的:
1. Identify all nodes in graph A that also exist in graph B (S#1, S#2, S#3)
2. Copy all of these nodes in the final tree..
3. For each of these common nodes, recursively explore their children..
4. If the child exists in the resultant tree, add an edge between the parent and the child. Otherwise, add the child and an edge between the parent and child and repeat the algorithm on the child itself.
5. Do this till there are no unvisited nodes left.
有人可以指出我在合并这些图表时应该采用什么方法的正确方向吗?请注意,这两个图可能是循环的。
谢谢,阿西姆
最佳答案
因为您还没有发布 Minimal, Complete, and Verifiable example ,这里的答案可能不完全适合您的用例。希望您无论如何都能利用它:
我建议您将图形表示为一组节点数据,其节点由字符串标签标识,同时还有一组节点标签对来表示边。像这样:
// T represents the data type, K represents the union of node labels
class Graph<K extends string, T> {
nodes: Record<K, T>;
edges: Array<{ start: K, end: K }>;
constructor(nodes: Record<K, T>, edges: Array<{ start: K, end: K }>) {
this.nodes = Object.assign({}, nodes);
this.edges = edges.slice();
}
}
// graphOne is a Graph<"a"|"b"|"c", string>
const graphOne = new Graph(
{
a: "NodeA", b: "NodeB", c: "NodeC"
}, [
{ start: "a", end: "b" },
{ start: "b", end: "c" }
]
);
在上面,graphOne
是一个简单的图,具有三个标记为 "a"
、"b"
和 "的节点c"
,从 "a"
到 "b"
以及从 "b"
到 “c”
。在这种情况下,存储在每个节点中的数据只是一个字符串
。在这里,让我们为 Graph
类创建一个简单的控制台日志记录方法:
print() {
console.log(this.nodes)
console.log(this.edges.map(e => e.start + "➡" + e.end));
}
并使用它:
graphOne.print();
// Object { a: "NodeA", b: "NodeB", c: "NodeC" }
// Array [ "a➡b", "b➡c" ]
在这一点上,您可能会反对这种表示并不能自然地表达您如何使用图形,其中每个节点都有数据和一些子节点,并且您可以通过递归遍历子节点来遍历图形(并且可能陷入循环)等。也就是说,您可能会想到这样的事情:
interface TraversableGraph<T> {
nodes: Array<TraversableNode<T>>;
}
interface TraversableNode<T> {
data: T,
children: Array<TraversableNode<T>>;
}
幸运的是,您可以相当轻松地将 Graph
转换为 TraversableGraph
。这是一种方法。让我们向 Graph
添加一个 asTraversableGraph()
方法:
asTraversableGraph(): TraversableGraph<T> {
return {
nodes: (Object.keys(this.nodes) as K[]).map(
k => this.asTraverableNode(k))
};
}
private asTraverableNode(k: K): TraversableNode<T> {
const graph = this;
return {
get data() {
return graph.nodes[k];
},
get children() {
return graph.edges.filter(e => e.start === k).map(
e => graph.asTraverableNode(e.end));
}
}
}
我选择使用 getters这样可遍历图将(可能,也许)反射(reflect)图的变化(例如,您向 Graph
添加一条新边,并且 TraversableGraph
也将具有该新边如果你穿过它,就进入它的边缘)。但您不必那样做。
让我们确保它的行为合理:
graphOne.asTraversableGraph().nodes[0].data; // "NodeA"
graphOne.asTraversableGraph().nodes[0].children[0].data; // "NodeB"
现在我们终于可以合并了。使用 Graph
表示,这不再是一些奇怪的毛茸茸的/循环的/递归的东西。您只需合并节点并合并边缘,然后回家。这是一种可能的方法,通过向 Graph
添加 merge()
方法:
merge<L extends string, U, V>(
otherGraph: Graph<L, U>,
dataMerge: (x: T | undefined, y: U | undefined) => V
): Graph<K | L, V> {
const thisGraph = this as Graph<K | L, T | undefined>;
const thatGraph = otherGraph as Graph<K | L, U | undefined>;
// merge nodes
const nodes = {} as Record<K | L, V>;
(Object.keys(thisGraph.nodes) as (K | L)[]).forEach(
k => nodes[k] = dataMerge(thisGraph.nodes[k], thatGraph.nodes[k]));
(Object.keys(thatGraph.nodes) as (K | L)[]).filter(k => !(k in nodes)).forEach(
k => nodes[k] = dataMerge(thisGraph.nodes[k], thatGraph.nodes[k]));
// merge edges
const edges: Record<string, { start: K | L, end: K | L }> = {};
thisGraph.edges.forEach(e => edges[JSON.stringify(e)] = e);
thatGraph.edges.forEach(e => edges[JSON.stringify(e)] = e);
return new Graph(nodes, Object.keys(edges).map(k => edges[k]));
}
请注意,merge()
需要两个参数:另一个图和一个 dataMerge()
回调,其工作是合并来自第一个图上的节点的数据(如果它存在)与来自第二个图上相同标记节点的数据(如果它存在)并产生你想在合并图中看到的数据。
通过遍历每个图的节点列表并在每个图的相同标记节点上运行 dataMerge()
来合并节点。通过合并两个边列表并确保没有重复项来合并边(我通过在边上使用 JSON.stringify()
作为唯一键来实现)。
让我们通过引入另一个图看看它是否有效:
// graphTwo is a Graph<"b" | "c" | "d", string>
const graphTwo = new Graph(
{
b: "NodeB", c: "Node C", d: "NodeD"
}, [
{ start: "b", end: "d" },
{ start: "b", end: "c" },
{ start: "d", end: "c" }
]
);
graphTwo.print();
// Object { b: "NodeB", c: "Node C", d: "NodeD" }
// Array(3) [ "b➡d", "b➡c", "d➡c" ]
并合并它,注意 dataMerge()
有点复杂,要处理 graphOne
中存储的字符串数据与 中存储的字符串数据之间可能发生的冲突图二
:
// graphMerged is a Graph<"a" | "b" | "c" | "d", string>
const graphMerged = graphOne.merge(graphTwo,
(x, y) => typeof x === 'undefined' ?
(typeof y === 'undefined' ? "??" : y) :
((x === y || typeof y === 'undefined') ? x : x + "/" + y)
);
graphMerged.print();
// Object { a: "NodeA", b: "NodeB", c: "NodeC/Node C", d: "NodeD" }
// Array(4) [ "a➡b", "b➡c", "b➡d", "d➡c" ]
而且有效!尽管 graphOne
和 graphTwo
具有相同的边,但合并后的图只有一条 b➡c
边。标记为 c
的节点名称在 graphOne
("NodeC"
) 和 graphTwo
( "Node C"
),所以 dataMerge()
加入了它们 ("NodeC/Node C"
)。
这是一个 Playground link以上所有代码。
希望对您有所帮助。祝你好运!
关于typescript - 将两个有向(可能是循环)图合并为一个,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55229917/
我想填充 3D 等高线图 (contour3(X,Y,Z)),就像 2D 等高线填充图 (contourf(X,Y,Z))。但我无法弄清楚如何实现这一目标。 contour3 和 surf 的组合不是
我有一个 c3.js 折线图,表示 2 个值的演变。我需要折线图的工具提示是饼图(工具提示 = 另一个 c3.js 图形)。 这是我成功的: http://jsfiddle.net/owhxgaqm/
我有具有结构的 Pandas 数据框: A B 0 1 1 1 2 1 2 3 4 3 3 7 4 6 8 如何生成 Seaborn Violin 图,每列作为其自己的单独
我正在使用 D3DXSPRITE 方法将我的 map 图 block 绘制到屏幕上,我刚刚添加了一个缩放功能,当您按住向上箭头时会放大,但注意到您现在可以看到图 block 之间的间隙,这是一些屏幕截
今天我们开始学习目前学习到的最难最复杂的数据结构图。 简单回顾一下之前学习的数据结构,数组、单链表、队列等线性表中数据元素是一对一关系,而树结构中数据元素是一对多关系,而图结构中数据元素则是多对
1、系统环境如下图: 2、为该系统添加一块新的虚拟硬盘,添加后需重启虚拟机,否则系统不识别;如下图,/dev/sdc 是新添加的硬盘; 3、fdisk /dev/sdc为新硬盘创建分区:
1、nagios简介 nagios是一款开源的电脑系统和网络监视工具,能有效监控windows、linux和unix的主机状态,交换机路由器等网络设置,打印机等。在系统或服务状态异常时发
越来越多人开始习惯用手机上网,浏览网页、查看邮件···移动化已经成为互联网发展必然趋势,包括facebook在内的很多互联网公司都将移动广告作为下一个淘金地
1.图片处理 1.圆角图片 复制代码 代码如下: /** * 转换成圆角 * &n
Microsoft SQL Server Management Studio是SQL SERVER的客户端工具,相信大家都知道。我不知道大伙使用导入数据的情况怎么样,反正我最近是遇到过。主要是因为没
debian6系统: 首先先安装mysql吧: 打开终端(root)用户登入 apt-get purge mysql-server-5.5 安装完成后: 默认情况下Mysql只允许本地登录
fedora16英文环境下支持中文输入法的方法 fedora16英文环境下支持FCITX的中文输入法: $ im-chooser 就会出现选择界面,选择第二个就行了。
Net预编译命令 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -? 显示说明 我们需要选择的命令为&n
有的时候电脑出现一些故障有的时候通过将其修改bios设置的方法来解决故障,那么在bios上设置能不能将电脑恢复出厂设置呢?其实也是可以的。方法也很简单的,只要会进入电脑的bios懂的上面英文的意思就
笔者曾介绍过Deepin 将对龙芯进行全面支持,打造最优美龙芯电脑桌面。现在Deepin团队移植工作取得了突破性的成果,Deepin桌面已经在龙芯3A和龙芯3B电脑上成功运行起来了。 以下为龙芯3
在安装一些软件之后,我们的电脑总是会发生一点小变化,不是桌面上多了几个网址图标,就是IE浏览器的默认主页被篡改成乱七八糟的网址。最可气的是,在IE设置中将默认主页改回来后,下次启动Win7后又变了回
“注册表编辑器怎么打开”虽说不是很难的问题,但是对于对电脑常识不是很擅长的网民来说,当电脑出现问题或需要更改设置时,着实还是件头疼的问题。因为需要打开注册表进行操作解决。那么如何打开注册表编辑器呢?
这篇文章重点介绍10个重要的WordPress安全插件和技巧,用来保护WordPress网站或者博客。 1. WP Security 人工帮助你修复被黑客入侵的网站,只要按照他们网站上的联系电话
其实运用object和javascript调用外部文件,也能实现不同栏目调用不同友情链接,即相当于调用不同栏目友情链接文件, {dede:field.typeid/}来获取当前栏目的ID。
我有一个复值矩阵。 如果我发出命令: plot(myMatrix) 然后它在图形设备上显示一种散点图,X 轴标记为 Re(myMatrix),Y 轴标记为 Im(myMatrix)。这显示了我正在寻找
我是一名优秀的程序员,十分优秀!