删除重复项算法

Remove duplicates algorithm

本文关键字:算法 删除      更新时间:2023-10-16

我正在尝试编写一个算法来从vector<struct xxxx*>中删除重复项。

struct xxxx{
    int value;     // This is just to make you understand
    xxxx* one; 
    xxxx* two;
}

就像你看到的结构体,它就像一个树,但是指针不是有序的。指针可以指向其他指针中的任何一个(实际上不是任何一个,而是大多数)。并且vector不包含结构体而是包含指针,所以我也不能使用std算法来帮助我。

我试图删除具有完全相同的值和相同的两个指针的重复项,但同时,如果我有两个类似的结构体(让我们说AB)和C.oneC.two指向B。然后我需要将其更改为A,反之亦然。

换句话说:如果A == B,则删除B并将C.one更改为A

我想我可以写蛮力,所以如果没有更好的算法我就自己写了

昨天,我试图向一位同事解释一个非常类似的问题的合理方法,他使用了N方的解决方案来解决N log N的问题。

首先创建一个helper结构,它基本上是一个xxxx*的包装器,带有检查内容(不是指针值)的比较操作符,可能还有一些其他实用函数。与只使用xxxx*相比,这个包装器结构不是严格需要的,但从经验来看,我认为它使任务更简洁。

创建一个std::帮助结构的集合,你只能在其中插入唯一的元素,并可能在另一个集合中插入递归无法解析的元素。

循环遍历原始向量,并在每个位置递归遍历它的子向量。如果你命中了唯一集中已经存在的子指针,那就是那个子指针的最终值。如果你命中了匹配唯一元素的子元素,而不是它所匹配的元素,那么修复让你到达那里的指针。如果还存在空指针的可能性,它应该在递归底部,并且如果可能存在循环,则需要检测它们(使用递归未解析的集合)并决定如何处理循环。在某一时刻,你会遇到已解析的唯一元素,并将其添加到唯一集合中。

这个想法的表现,甚至可能是合理的,取决于循环的深度和复杂性,以及你想用循环做什么。在一些混乱的情况下,一个循环会映射到另一个循环,但检测这种情况可能非常棘手。如果你的阶段"像树一样"意味着"没有循环",那么递归就会干净而有效地结束,而不需要显式管理递归未解析元素的额外复杂性。

显然,我省略了一些繁琐的工作细节,比如当你退出递归时检测唯一/非唯一,以及当你在递归上面的主循环中碰到一个项时检测"在早期递归中已经做过了"。但是,在编写代码的相关部分时,所有这些细节都应该非常明显。

编辑:要理解尽管在顺序循环中嵌套了递归,但仍然有很少的节点访问,请从指针的角度考虑。我们最多跟踪每个指针一次(有些重复项在不跟踪指针的情况下被预先检测到)。对于N个节点,有N个顶层指针(如果我理解正确的话)和明显少于2N个内部指针(越像树,越接近N-1个内部指针,而不是2N个)。所以每个节点的平均访问次数少于3次,其中少数访问需要预查找和后递归查找,每次查找是log U, U是到该点为止找到的唯一项的数量。所以我们可以看到6 N log N的边界