【发布时间】:2021-11-29 15:26:33
【问题描述】:
我知道标题可能具有误导性,常见的答案可能是“再次搜索”,但是我已经尽可能多地搜索堆栈溢出,但没有找到让我满意的答案。
简介
我需要创建一个算法来删除嵌套的交叉引用列表中的所有相似项。
一般规则:
- 项可以有子项并且可以引用其他项。
- 对象不能同时引用另一个对象和子对象。
- 一个列表中可以有很多对象(最多 100k),对于子对象的深度总是最多为 1 级,但每个对象都是未知的可变长度引用链。
- 引用链不是无限的,它们通常有多达 5-15 个元素,并且总是以已知 ClassType 字段的对象结束。
- 可能存在“死链” - 没有被任何对象引用但它们引用其他对象的项。
- 对象的子对象不是列表的一部分 - 它们可以引用其他元素,但不能单独引用。
- 为了比较两个对象,它们的属性以及它们的子对象(如果存在)和引用对象(如果存在)必须相同。
- 比较子对象或引用对象时,适用相同的规则。
- 为每个对象分配唯一 ID(这不是哈希码,而不是对象创建的顺序) - 尽管两个对象具有不同的 ID,但它们将是相同的。不会有具有相同 ID 的对象。
未指定内存要求和 CPU 占用率 - 首选高性能。我目前的方法(将每个对象与另一个对象进行比较的蛮力方法)速度很慢,可能需要长达 1 小时的运行时间。首选执行速度最多为 1 分钟。我的方式也是无限次地迭代集合,因为找到了相同的对象并且它只使用单线程。我很想获得多线程更具确定性的方法。
说明
考虑以下对象定义:
public enum ClassType
{
Type_Base,
Type_A,
Type_B,
Type_C,
};
public class MyObject
{
public string Name { get; }
public uint ID { get; }
public ClassType ClassType { get; }
public uint ReferencedID { get; }
public List<MyObject> Children { get; } = new List<MyObject>( );
}
还有一个以 MyObject 的 ID 属性为索引的集合,用于快速访问:
Dictionary<uint, MyObject> Preserved; // Top-most objects, has to be preserved
Dictionary<uint, MyObject> AllObjects; // Dictionary of all objects
另外,考虑以下比较方法:
public static bool AreObjectsSame( MyObject l, MyObject r, Dictionary<uint, MyObject> allObjects )
{
// Null-Check
if ( l is null ) throw new ArgumentNullException( nameof( l ) );
if ( r is null ) throw new ArgumentNullException( nameof( r ) );
if ( allObjects is null ) throw new ArgumentNullException( nameof( allObjects ) );
// Compare class type and name
if ( l.ClassType != r.ClassType ) return false;
if ( l.Name != null && r.Name != null && !l.Name.Equals( r.Name, StringComparison.InvariantCulture ) ) return false;
// Compare referenced ID objects
if ( l.ReferencedID != 0 && r.ReferencedID != 0 )
{
if ( !AreObjectsSame( allObjects[ l.ReferencedID ], allObjects[ r.ReferencedID ], allObjects ) ) return false;
}
// Compare children objects
if ( l.Children.Count != r.Children.Count ) return false;
for ( int i = 0; i < l.Children.Count; ++i )
{
if ( !AreObjectsSame( l.Children[ i ], r.Children[ i ], allObjects ) ) return false;
}
return true;
}
现在让我们检查一个对象链。有孩子的对象以类似的方式工作,因此为了解释简单,我将跳过它们。
MyObject ("Name_A", Type_A) -> MyObject ("Name_B", Type_B) -> MyObject ("Name_C", Type_A) -> MyObject ("Name_D", Type_Base)
MyObject ("Name_E", Type_C) -> MyObject ("Name_F", Type_C) -> MyObject ("Name_C", Type_A) -> MyObject ("Name_D", Type_Base)
MyObject ("Name_G", Type_A) -> MyObject ("Name_D", Type_Base)
如您所见,我们在 AllObjects 字典中放置了 10 个对象。在每个引用链中,我们都有对象 ("Name_C", Type_A) 和 ("Name_D", Type_Base)。由于对象 ("Name_D", Type_Base) 是相等的(它们具有相同的类型和名称),对象 ("Name_C", Type_A) 也是相等的(它们具有相同的类型和名称加上它们引用的对象是相同的)。
现在,我们可以将上面的对象链优化成这样:
MyObject ("Name_A", Type_A) -> MyObject ("Name_B", Type_B) -> MyObject ("Name_C", Type_A) -> MyObject ("Name_D", Type_Base)
MyObject ("Name_E", Type_C) -> MyObject ("Name_F", Type_C) ->-|
MyObject ("Name_G", Type_A) -------------------------------->-/
这导致我们删除 3 个重复的节点并重新排列其中两个的参考 ID。
可能的解决方案
一种可能的解决方案是为每个元素预先计算其自己的哈希码,并使用此哈希以蛮力方式比较元素。如果比较为真,则执行逐字段比较(以消除哈希码中可能的冲突)。
有没有更好的删除重复项的方法?我该怎么做?
【问题讨论】:
标签: c# reference nested duplicates cross-reference