题目描述
题目描述:
给定一个没有排序的链表,去掉其重复项,并保留原顺序,例如链表 1->3->1->5->5->7,去掉重复项后变为 1->3->5->7。
解答
解答
方法一:顺序删除
方法一思路
通过双重循环直接在链表上进行删除操作。外层循环用一个指针从第一个结点开始遍历整个链表,然后内层循环用另外一个指针遍历其余结点,将与外层循环遍历到的指针所指结点的数据域相同的结点删除。
假设外层循环从 outerCur 开始遍历,当内层循环指针 innerCur 遍历到上图实线所示的位置(outerCur.data= =innerCur.data)时,需要把 innerCur 指向的结点删除。具体步骤如下:
(1)用 tmp 记录待删除的结点的地址。
(2)为了能够在删除 tmp 结点后继续遍历链表中其余的结点,使 innerCur 指向它的后继结点:innerCur=innerCur.next。
(3)从链表中删除 tmp 结点。
方法一示例解答
# -*- coding: utf-8 -*-"""Created on Sat Nov 10 12:24:08 2018@author: Administrator"""class LNode: def __init__(self): self.data=None self.next=None """** 方法功能:对带头结点的无序单链表删除重复的结点 ** 输入参数:head:链表头结点"""def removeDup(head): if head == None or head.next == None: return outerCur = head.next # 用于外层循环,指向链表第一个结点 innerCur = None # 用于内层循环用来遍历outerCur后面的结点 innerPre = None # innerCur的前驱结点 while outerCur != None: innerCur = outerCur.next innerPre = outerCur while innerCur != None: # 找到重复的结点并删除 if outerCur.data == innerCur.data: innerPre.next = innerCur.next innerCur = innerCur.next else: innerPre = innerCur innerCur = innerCur.next outerCur = outerCur.nextif __name__=="__main__": i = 1 head =LNode() head.next = None cur = head while i<7: tmp =LNode() if i % 2 == 0: tmp.data = i + 1 elif i % 3 == 0: tmp.data = i - 2 else: tmp.data = i tmp.next = None cur.next = tmp cur = tmp i +=1 print ("删除重复结点前:",end = '') cur = head.next while cur != None: print (cur.data, '', end='') cur = cur.next removeDup(head) print ("\n删除重复结点后:",end = '') cur = head.next while cur != None: print (cur.data,'',end='') cur = cur.next
程序的运行结果为:
删除重复结点前:1 3 1 5 5 7删除重复结点后:1 3 5 7
方法一性能分析
由于这种方法采用双重循环对链表进行遍历,因此,时间复杂度为 O(N2),其中,N 为链表的长度,在遍历链表的过程中,使用了常量个额外的指针变量来保存当前遍历的结点、前驱结点和被删除的结点,因此,空间复杂度为 O(1)。
方法二:递归法
方法二思路
对于结点 cur,首先递归地删除以 cur.next 为首的子链表中重复的结点,接着从以 cur.next 为首的子链表中找出与 cur 有着相同数据域的结点并删除。
方法二示例解答
# -*- coding: utf-8 -*-"""Created on Sat Nov 10 12:24:43 2018@author: Administrator"""def removeDupRecursion(head): if head.next is None: return head pointer = None cur = head #对以head.next为首的子链表删除重复的结点 head.next = removeDupRecursion(head.next) pointer = head.next # 找出以head.next为首的子链表中与head结点相同的结点并删除 while pointer is not None: if head.data == pointer.data: cur.next = pointer.next pointer = cur.next else: pointer = pointer.next cur = cur.next return head """方法功能:对带头结点的单链删除重复结点 输入参数:head:链表头结点"""def removeDup(head): if head is None: return head.next = removeDupRecursion(head.next)
方法二性能分析
这种方法与方法一类似,从本质上而言,由于这种方法需要对链表进行双重遍历,因此,时间复杂度为 O(N2),其中,N 为链表的长度。由于递归法会增加许多额外的函数调用,因此,从理论上讲,该方法效率比方法一低。
方法三:空间换时间
方法三思路
通常情况下,为了降低时间复杂度,往往在条件允许的情况下,通过使用辅助空间来实现。具体而言,主要思路为:
(1)建立一个 HashSet,HashSet 中的内容为已经遍历过的结点内容,并将其初始化为空。
(2)从头开始遍历链表中的所有结点,存在以下两种可能性:
1)如果结点内容已经在 HashSet 中,那么删除此结点,继续向后遍历。
2)如果结点内容不在 HashSet 中,那么保留此结点,将此结点内容添加到 HashSet 中,继续向后遍历。
引申:如何从有序链表中移除重复项
上述介绍的方法也适用于链表有序的情况,但是由于以上方法没有充分利用到链表有序这个条件,因此,算法的性能肯定不是最优的。本题中,由于链表具有有序性,因此,不需要对链表进行两次遍历。所以,有如下思路:用 cur 指向链表第一个结点,此时需要分为以下两种情况讨论:
(1)如果 cur.data==cur.next.data,那么删除 cur.next 结点; (2)如果 cur.data!= cur.next.data,那么 cur=cur.next,继续遍历其余结点。感谢您的阅读,有任何问题欢迎评论区留言。
· END ·
本公众号拥有优而全的高效复习资料,旨在为您的面试求职保驾护航,提高您的职场竞争力,感谢您的支持!