📜  链表中的多数元素(1)

📅  最后修改于: 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)$,而且代码也比较简单。因此,摩尔投票法是解决该问题的最佳选择。