📅  最后修改于: 2023-12-03 15:28:34.001000             🧑  作者: Mango
给定一个单链表,其中元素的数量可能很大,但是元素只能是非负整数,找到其中出现次数超过链表长度一半的元素。假定该链表非空,并且总是存在超过链表长度一半的元素。
例如,如果给定的链表是 [1,2,3,2,2]
,那么元素2出现了3次,占元素总数的3/5,因此,2是该链表中的多数元素。
最简单的解法是使用两个循环嵌套,外层循环枚举链表中的每个元素,内层循环统计该元素在链表中出现的次数,时间复杂度为 $O(n^2)$,空间复杂度为 $O(1)$。
如果使用哈希表可以减少时间复杂度。遍历链表的同时,统计每个元素出现的次数,将元素和次数存储到哈希表中。时间复杂度为 $O(n)$,空间复杂度为 $O(n)$。
因为多数元素占据了链表长度的一半以上,因此如果将链表分成两半,多数元素必定会出现在长度更长的一半中。因此,我们可以使用快慢指针,快指针每次移动2步,慢指针每次移动1步,快慢指针同时从头结点出发,当快指针到达链表末尾时,慢指针必定恰好到达链表的中间,此时计算慢指针左边元素的个数,如果该个数大于链表长度的一半,那么多数元素必定在左半部分,否则在右半部分。时间复杂度为 $O(n)$,空间复杂度为 $O(1)$。
摩尔投票法可以在 $O(n)$ 的时间复杂度内找到出现次数超过元素总数一半的元素。该算法基于一个事实,即一个最多有 $n$ 个元素的数组中,出现次数超过 $n/2$ 的元素最多只有一个。算法遍历数组,使用一个变量记录当前元素出现的次数,如果遇到相同的元素,则将计数器加1,否则将计数器减1。当计数器为0时,将当前元素设为候选元素。遍历完整个数组后,候选元素就是出现次数超过 $n/2$ 的元素。时间复杂度为 $O(n)$,空间复杂度为 $O(1)$。
class Solution:
def majorityElement(self, head: ListNode) -> int:
store = {}
while head:
store[head.val] = store.get(head.val, 0) + 1
if store[head.val] > len(l) // 2:
return head.val
head = head.next
class Solution:
def majorityElement(self, head: ListNode) -> int:
slow, fast = head, head
count = 0
while fast:
if fast.val == slow.val:
count += 1
else:
count -= 1
if not fast.next:
break
fast = fast.next.next
slow = slow.next
return slow.val
class Solution:
def majorityElement(self, head: ListNode) -> int:
count = 0
candidate = None
while head:
if count == 0:
candidate = head.val
count += (1 if head.val == candidate else -1)
head = head.next
return candidate
本题提供了多种解法。暴力解法虽然简单,但是时间复杂度高。哈希表和快慢指针的时间复杂度都是 $O(n)$,但是快慢指针需要遍历一遍链表,比哈希表慢。摩尔投票法是最优解,时间复杂度为 $O(n)$,空间复杂度为 $O(1)$,而且代码也比较简单。因此,摩尔投票法是解决该问题的最佳选择。