Python面试宝典第41题:数组中的众数

84 阅读5分钟

题目

众数,是指在数组中出现次数大于n/2的元素。现给定一个大小为n的数组,请找出其中的众数。我们可以假设数组是非空的,并且给定的数组总是存在众数。

示例 1:

输入:[3, 2, 3]
输出:3

示例 2:

输入:[2, 2, 1, 1, 1, 2, 2]
输出:2

哈希法

为了求解本题,我们可以使用哈希表来记录每个元素出现的次数。因为题目本身保证了众数的存在,且其出现次数超过了数组长度的一半,故在遍历数组的过程中,我们只需要找到出现次数最多的那个元素即可。使用哈希法求解本题的主要步骤如下。

1、初始化一个空的哈希表。

2、遍历数组,对于每个元素,在哈希表中更新其出现的次数。在遍历过程中,同时跟踪当前出现次数最多的元素及其出现次数。

3、遍历结束后,返回出现次数最多的元素。

根据上面的算法步骤,我们可以得出下面的示例代码。

def majority_element_of_array_by_hash(nums):
    count_map = {}
    majority_element = None
    max_count = 0
    
    for num in nums:
        # 更新元素的计数
        if num in count_map:
            count_map[num] += 1
        else:
            count_map[num] = 1
        
        # 更新当前出现次数最多的元素
        if count_map[num] > max_count:
            majority_element = num
            max_count = count_map[num]
    
    # 验证众数是否真的出现了超过n/2次
    if max_count > len(nums) // 2:
        return majority_element
    else:
        # 理论上不会执行到
        raise ValueError("No majority element found")

nums = [3, 2, 3]
print(majority_element_of_array_by_hash(nums))

nums = [2, 2, 1, 1, 1, 2, 2]
print(majority_element_of_array_by_hash(nums))

排序法

由于众数出现次数超过数组长度的一半,那么排序后,众数必定位于数组的中间位置,即索引n/2处。因此,我们只需要对数组进行排序,然后返回位于中间位置的元素即可。使用排序法求解本题的主要步骤如下。

1、使用任意一种排序算法对数组进行排序。

2、返回位于中间位置的元素。

根据上面的算法步骤,我们可以得出下面的示例代码。

def majority_element_of_array_by_sort(nums):
    # 对数组进行排序
    sorted_nums = sorted(nums)
    
    # 返回中间位置的元素
    mid_index = len(sorted_nums) // 2
    return sorted_nums[mid_index]

nums = [3, 2, 3]
print(majority_element_of_array_by_sort(nums))

nums = [2, 2, 1, 1, 1, 2, 2]
print(majority_element_of_array_by_sort(nums))

摩尔投票法

摩尔投票法,也被称为博耶-摩尔多数投票算法(Boyer-Moore Voting Algorithm),是由Robert S. Boyer和J Strother Moore在1981年提出的一种高效算法。该算法最初是在一篇名为“MJRTY—A Fast Majority Vote Algorithm”的论文中被描述的,介绍了如何在一个数组中快速找到出现次数超过数组长度一半的元素(如果存在的话)。

摩尔投票法的核心思想是:通过遍历数组并维护一个候选众数和一个计数器,当遇到相同的元素时增加计数器,遇到不同的元素时减少计数器。如果计数器减至零,则重置候选众数。最终,候选众数将是数组中的众数。使用摩尔投票法求解本题的主要步骤如下。

1、初始化两个变量:candidate和count,其中count = 0。

2、遍历数组中的每个元素,执行以下操作。

(1)如果count等于0,则设置candidate为当前元素。

(2)如果当前元素等于candidate,则count++。否则,count--。

3、第一遍遍历结束后,candidate将是可能的众数。

4、再次遍历数组,以确认candidate是否为众数,即检查它的出现次数是否超过n/2。

根据上面的算法步骤,我们可以得出下面的示例代码。

def majority_element_of_array_by_moore_voting(nums):
    # 初始化候选众数和计数器
    candidate = None
    count = 0
    
    # 找到候选众数
    for num in nums:
        if count == 0:
            candidate = num
        count += (1 if num == candidate else -1)
    
    # 再次遍历数组,计算候选众数出现的次数
    count = 0
    for num in nums:
        if num == candidate:
            count += 1
    
    # 检查候选众数是否出现次数超过n/2
    if count > len(nums) // 2:
        return candidate
    else:
        # 理论上不会执行到
        raise ValueError("No majority element found")

nums = [3, 2, 3]
print(majority_element_of_array_by_moore_voting(nums))

nums = [2, 2, 1, 1, 1, 2, 2]
print(majority_element_of_array_by_moore_voting(nums))

总结

哈希法的时间复杂度和空间复杂度均为O(n),因为需要遍历数组一次以填充哈希表,且最坏情况下需要存储数组中所有不同的元素。哈希法的实现简单直观,但需要额外的空间来存储哈希表。如果数组很大,可能会导致较高的内存消耗。

排序法的时间复杂度和空间复杂度取决于使用的排序算法,时间复杂度通常为O(n*logn),空间复杂度通常为O(1)或O(n)。排序法不需要额外的数据结构,但其时间复杂度较高,不适合大数据集。

摩尔投票法是最优选,特别是在处理大数据集时,因为它具有线性时间复杂度O(n)和常数级空间复杂度O(1)。摩尔投票法非常高效,实现也较为简单。缺点是需要遍历数组两次:第一次找出候选众数,第二次验证候选众数。