在没有任何适当解决方案的情况下,我一直在努力解决这个图表演示问题。也许有人可以解决问题。
我有一个连接的,无循环图的演示文稿,其形式如下:
下面是一个示例图表:
2 3
\ /
5 1
\ /
4
这就是演示文稿的形式:
2 3 3
\ / /
5 1 => 5 1 => 5 1 => 5 => 5
\ / \ / \ / \
4 4 4 4
1. Remove vertex two and mark one.
2. Remove vertex three and mark one.
3. Remove vertex one and mark four.
4. Remove vertex four and mark five.
因此该图表的演示文稿将是:
1 1 4 5
问题是,如何将此演示文稿转换为邻接矩阵或邻接列表? F.E. 1 1 4 5,邻接列表如下所示:
1: 2 3 4
2: 1
3: 1
4: 1 5
5: 4
谢谢!
答案 0 :(得分:1)
使用以下技术可以将“演示文稿”(在您的示例中为1 1 4 5)转换为图形(从上面的评论中,我认为您正在努力解决这个问题)。然后,您可以简单地生成邻接矩阵/列表。
该技术依赖于关键假设,图中的节点标记为1 - N(图中有N个节点)。如果不是这种情况,则根本不可能重建原始图,因为您永远无法确定第一个被删除节点的身份。
删除最后一个节点后,剩下的节点为5.因此,图表看起来像......
5 - ?
删除上一项时,标记为4。因此,原始问号实际上必须是节点4,并且我们有一个新的未知节点。
5 - 4 - ?
删除上一项时,标记为1。因此,?必须是1并且有一个新的未知节点。
5 - 4 - 1 - ?A
最后,当删除上一个项目时,标记为1。我们已经有了一个节点1,所以我们必须附上它。
5 - 4 - 1 +- ?A
|
+= ?B
我们已经完成了解析输入。现在我们只需要标记优秀的标签。我们知道值是2和3,因为上面假设节点标记为1 - N并且我们已经有1,2和1。 5.由于首先删除了最低值的节点(将图形转换为演示文稿时),因此在将演示文稿转换为图形时最后添加它们。那么?A = 3和?B = 2.(在这种情况下它没关系,但在一般情况下确实如此。)这样就得出了如下最终图。
5 - 4 - 1 +- 3
|
+= 2
......这很好,因为这与我们开始时的情况相同。
从此可以迭代节点并生成邻接矩阵。或者,也可以随着时间的推移产生邻接列表/矩阵(这可能更有效,但稍微混淆了实现)。
正如大卫在上面所指出的,这与Prüfer sequence非常相似(但不完全相同),当剩下2个节点(而不是1)时,{{3}}停止。链接的文章提供了一种有效的伪代码算法,可以通过跳过最后一步(将最后两个节点链接在一起)进行调整。
答案 1 :(得分:1)
这是python中的天真实现:
from collections import defaultdict
prufer_sequence = [1, 1, 4, 5]
all_vertices = range(1, len(prufer_sequence) + 2)
adjacency = defaultdict(list)
for vertex in prufer_sequence:
searched_vertex = filter(lambda v: v != vertex, all_vertices)[0]
all_vertices.remove(searched_vertex)
adjacency[vertex].append(searched_vertex)
adjacency[searched_vertex].append(vertex)
print adjacency
输出:
defaultdict(<type 'list'>, {1: [2, 3, 4], 2: [1], 3: [1], 4: [1, 5], 5: [4]})
答案 2 :(得分:1)
啊!由于原始问题中信息不足(特别是信息:树将有1 to n+1
个节点,其中n
是输入数组的长度),我试图以更加困难的方式解决它!无论如何,这是我的Prufer树生成实现,也许它会有所帮助: - ? :
#include <stdio.h>
#include <vector>
#include <memory.h>
using namespace std;
struct Node {
int N;
vector<int>list;
Node() {
N=-1;
list.clear();
}
};
vector<Node> convertPruferToTree(vector<int>& input) {
int n = input.size()+1;
vector<Node> T;
int *degree = new int[n+1];
for (int i=1; i<=n; i++) {
Node tmp;
tmp.N = i;
T.push_back(tmp);
degree[i]=1;
}
//printf("n: %d\n", n);
for (int i=0; i<input.size()-1; i++) {
degree[input[i]]++;
}
for (int i=0; i<input.size()-1; i++) {
for (int j=1; j<=n; j++) {
if (degree[j]==1) {
T[j-1].list.push_back(input[i]);
T[input[i]-1].list.push_back(j);
degree[input[i]]--;
degree[j]--;
break;
}
}
}
int u=0, v=0;
for (int i=1; i<=n; i++) {
if (degree[i]==1) {
if (u==0) u=i;
else {
v = i;
break;
}
}
}
//printf("u: %d v: %d\n", u, v);
T[u-1].list.push_back(v);
T[v-1].list.push_back(u);
delete []degree;
return T;
}
int main () {
vector <int> input;
int n,v;
scanf("%d", &n);
while(n--) {
scanf("%d", &v);
input.push_back(v);
}
vector<Node> adjList = convertPruferToTree(input);
Node tmp;
for (int i=0; i<adjList.size(); i++) {
tmp = adjList[i];
printf("%2d: ", tmp.N);
for (int j=0; j<tmp.list.size(); j++) {
printf("%2d ", tmp.list[j]);
}
printf("\n");
}
return 0;
}
答案 3 :(得分:0)
我想出了这个算法。这很像http://en.wikipedia.org/wiki/Pr%C3%BCfer_sequence,但就像安德鲁说的那样,我离开了最后一部分。 hashmap用于邻接列表,arraylist k用于表示。
public static HashMap<Integer, HashSet<Integer>> toGraph(ArrayList<Integer> k) {
HashMap<Integer, HashSet<Integer>> hm = new HashMap<Integer, HashSet<Integer>>();
for(int i=1; i<=k.size()+1; i++){
hm.put(i, new HashSet<Integer>());
}
int degree[] = new int[k.size()+1];
for(int i=0; i<degree.length; i++){
degree[i]=1;
}
for(int a : k){
degree[a-1]++;
}
for(int n : k){
for(int j : hm.keySet()){
if(degree[j-1]==1){
hm.get(j).add(n);
hm.get(n).add(j);
degree[n-1]--;
degree[j-1]--;
break;
}
}
}
return hm;
}
在某些情况下,一个顶点在返回的邻接列表中放错位置。 F.E.在16,1,19,9,19,18,17,10,13,13,1,19,5,19,18,4,19,19顶点3应该有17,19,13的边缘但在我的它有16,19,13的边缘。有人能发现一个缺陷吗?