当前位置: 首页 > 科技观察

如何从无序列表中删除重复项?有多少种方法?

时间:2023-03-14 13:21:25 科技观察

有小伙伴来谷歌问一道关于单链表运算的笔试题,问一共有多少解。今天就来说说吧。题目的大致意思是:假设有一个无序单向链表,去掉重复节点后,保留原来的顺序。去重前:1→3→1→5→5→7去重后:1→3→5→7顺序删除直接通过双循环对链表执行删除操作。外层循环使用一个指针从第一个节点开始遍历整个链表,然后内层循环使用另一个指针遍历其余节点。删除,如下图。假设外层循环从outerCur开始遍历,当内层循环指针innerCur遍历到上图实线所示位置(outerCur.data==innerCur.data)时,需要删除innerCur指向的节点这次。具体步骤如下:使用tmp记录要删除的节点地址。为了在删除tmp节点后继续遍历链表中的剩余节点,使innerCur指针指向其后继节点:innerCur=innerCur.next。从链表中删除tmp节点。实现代码如下:运行结果:算法性能分析由于该方法采用双循环遍历链表,时间复杂度为O(N^2)。其中,N为链表的长度。在遍历链表的过程中,使用常数个额外的指针变量来保存当前遍历的节点、前驱节点和被删除的节点,所以空间复杂度为O(1)。递归方法的主要思想是:对于节点cur,先递归删除以cur.next为首的子链表中重复的节点,然后从子链表中找到与cur具有相同数据字段的节点cur.next为首的链接列表点击删除。实现代码如下:算法性能分析该方法与方法一类似,本质上由于该方法需要对链表进行二次遍历,所以时间复杂度为O(N^2)。其中,N为链表的长度。由于递归方法增加了很多额外的函数调用,理论上这种方法的效率不如前面的方法。空间换时间通常,为了降低时间复杂度,往往在条件允许的情况下使用辅助空间来实现。具体来说,主要思路如下。创建一个HashSet,HashSet中的内容为已经遍历过的节点内容,初始化为空。从头遍历链表中的所有节点有两种可能:如果该节点的内容已经在HashSet中,则删除该节点,继续向后遍历。如果该节点的内容不在HashSet中,则保留该节点,将该节点的内容添加到HashSet中,继续向后遍历。“扩展:如何从有序链表中删除重复项?”例如链表:1,3,5,5,7,7,8,9去重后:1,3,5,7,8,9分析和上面介绍的方法同样适用于链表的情况list是有序的,但是由于上述方法没有充分利用链表的条件,所以算法的性能肯定不是最优的。这道题因为链表是有序的,所以不需要遍历链表两次。因此,思路是:用cur指向链表的第一个节点。这时候就需要分以下两种情况来讨论。如果cur.data==cur.next.data,则删除cur.next节点。如果cur.data!=cur.next.data,则cur=cur.next,继续遍历剩下的节点。小结对于一个无序列表的单向链表,想要删除重复节点(多个重复节点保留一个)。删除方式有顺序删除、递归删除、以空间换时间(HashSet中元素的唯一性)。最近有读者想要分布式项目,有的想要商城,有的想要springboot、springcloud、k8s等。直接分享,几乎涵盖了我们java程序员的大部分技术栈。可以说是真的很全面。强烈建议大家动手去做,以后一定会用到的。