【发布时间】:2017-11-16 13:48:54
【问题描述】:
拥有原始和“最终”/结果树。我想比较这些树并“重现”这些步骤,以得到相同的结果。
真实示例:在数据库中有原始树。工作人员已准备好更改(在 App 中生成新的结果树),现在我们需要更新数据库。我们无法删除数据库并重新上传,因为可能存在尚未生成的数据。
类/表定义:
class TreeNode
{
public string Text { get; set; }
public TreeNode Parent { get; set; }
/* some other properties */
}
示例树:
Origin Result
|A |A
| -1 | -2
| -2 |C
|B | -3
| -5 |D
| --£ | -1
|C | --£
|F | -5
| -7 |E
|H | -6
|G
| -4
|H
我希望有一个算法,当对象被添加、删除或移动时,我可以通过该算法进行处理.
重要提示:具有其他父对象的对象不应删除并添加回来,而应仅在其他父对象下移动!删除会导致数据丢失。
例子:
Mark B as removed
Mark F as removed
Add D
Add E
Add G
Move 1 under D
Move 5 under D
Mark 7 as removed
Add 3 under C
Add 6 under E
Add 4 under G
Move £ under 1
Removed 7
Removed F
Removed B
自己的解决方案
我使用 Win-Forms 和 TreeView 创建了示例。我的算法仅适用于每个级别的基础(例如,将 1 从 A 移动到 D),但不能跨越。元素是第一个市场被删除,最后被删除。
代码:
//Recursive loop to find all nodes in Nth level
private IEnumerable<TreeNode> getNodesOnLevel(TreeNodeCollection aCollection, int aLevel)
{
var lResultTreeNodeCol = new List<TreeNode>();
if (aLevel == 1)
return aCollection.Cast<TreeNode>();
foreach(TreeNode nNode in aCollection)
{
lResultTreeNodeCol.AddRange(getNodesOnLevel(nNode.Nodes, aLevel - 1));
}
return lResultTreeNodeCol;
}
//Called once
public void UpdateTrees(TreeNodeCollection aCollectionA, TreeNodeCollection aCollectionB)
{
List<TreeNode> lRemoved = new List<TreeNode>();
for (int i = 1; UpdateWithLevel(aCollectionA, aCollectionB, i, ref lRemoved) > 0; i++)
{
}
var lRem = lRemoved.LastOrDefault();
do
{
W($"Removed {lRem.Text}");
lRemoved.Remove(lRem);
} while ((lRem = lRemoved.LastOrDefault()) != null);
}
//Called per level
private int UpdateWithLevel(TreeNodeCollection aCollectionA, TreeNodeCollection aCollectionB, int level, ref List<TreeNode> aRemoved)
{
int lNumOfUpdates = 0;
var colA = getNodesOnLevel(aCollectionA, level);
var colB = getNodesOnLevel(aCollectionB, level);
//Search Original collection, compare to Result collection
foreach (TreeNode nodeA in colA)
{
//Find nodeA in Result collection
var lNodeAinColB = colB.FirstOrDefault((a) => a.Text == nodeA.Text);
if(lNodeAinColB == null) //NodeA not found in result collection - delete
{
aRemoved.Add(nodeA);
W($"Mark {nodeA.Text} as removed");
lNumOfUpdates++;
}
else if((lNodeAinColB.Parent?.Text ?? "") != (nodeA.Parent?.Text ?? "")) //NodeA exists in Result collection, different parrent -> must be moved
{
W($"Move {nodeA.Text} under {lNodeAinColB.Parent.Text}");
lNumOfUpdates++;
}
}
//Search Result collection, if Original collection does not have nodeB, we must create it (add)
foreach (TreeNode nodeB in colB)
{
if (!colA.Contains(nodeB, new TestNodeEquality()))
{
W($"Add {nodeB.Text}" + ((nodeB.Parent != null)?$" under {nodeB.Parent.Text}":""));
lNumOfUpdates++;
}
}
return lNumOfUpdates;
}
我没有找到任何适合我的问题的主题,也没有找到有价值的资源,我真的很想避免重新发明轮子。
问题:
是否存在现有和有效的算法(名称/参考)?这种算法/动作叫什么(Tree Diff/Merge/Lookup/..)?
我可以以任何方式优化算法吗?
【问题讨论】:
-
@jdweng 你能指点我直接的文章吗?
-
如果每个节点都有一个唯一的身份,您可以轻松地逐个节点地比较它们的状态变化而不考虑级别,然后应用这些变化,我想?
-
@jdweng - 恐怕这些链接/文章是关于二叉树的,而不是多叶原因。
-
@AKX 这实际上是有道理的,我也在考虑这个实现。目前我什至没有看到任何缺点。
标签: c# .net algorithm linq tree