8.找出整型数组中占比超过一半的数 | 豆包MarsCode AI 刷题

128 阅读3分钟

多数元素查找问题详解

问题背景

多数元素问题是一个经典的算法挑战,要求在给定的数组中找出出现次数超过数组长度一半的元素。这个问题看似简单,但实际上蕴含了多种解题思路和算法设计的精妙之处。它不仅考验编程技巧,更是对算法复杂度和空间利用的深入考察。

算法解析

1. 算法思路概述

解决多数元素问题主要有两种核心方法:

  • 哈希计数法
  • 摩尔投票法(Moore Voting Algorithm)

这两种方法在时间复杂度上都达到了O(n),但在空间利用和实现复杂度上各有特色。

2. 详细算法解析

哈希计数法

哈希计数法的核心思路是通过哈希表统计每个元素出现的次数。其算法流程如下:

  1. 创建一个哈希表(unordered_map)
  2. 遍历数组,记录每个元素出现的次数
  3. 同时检查当前元素是否已经超过数组长度的一半
  4. 如果找到,立即返回该元素
  5. 遍历结束仍未找到,返回-1

时间复杂度分析

  • 最坏情况下需要遍历整个数组:O(n)
  • 哈希表的插入和查询操作:平均O(1)
  • 总体时间复杂度:O(n)

空间复杂度分析

  • 需要额外的哈希表存储元素计数
  • 空间复杂度:O(n)

摩尔投票法

摩尔投票法是解决这类问题的巧妙算法,其核心思想可以概括为"相互抵消"。算法流程分为两个阶段:

第一阶段:候选元素选择

  1. 初始化候选元素和计数器

  2. 遍历数组

    • 如果计数器为0,选择当前元素作为候选
    • 如果当前元素等于候选元素,计数器+1
    • 如果当前元素不等于候选元素,计数器-1

第二阶段:候选元素验证

  1. 重新遍历数组
  2. 统计候选元素出现次数
  3. 判断是否超过数组长度一半

时间复杂度分析

  • 两次遍历数组:O(n)
  • 总体时间复杂度:O(n)

空间复杂度分析

  • 仅使用有限的额外空间
  • 空间复杂度:O(1)

3. 算法选择与对比

推荐使用摩尔投票法的理由

  1. 空间效率极高
  2. 实现相对简单
  3. 不需要额外的数据结构
  4. 对大规模数据处理更加友好
#include <iostream>
#include <vector>

using namespace std;

int solution(vector<int> array) {
    // 第一轮遍历:找候选元素
        int candidate = array[0];
        int count = 1;
        
        for (int i = 1; i < array.size(); i++) {
            if (count == 0) {
                candidate = array[i];
                count = 1;
            } else if (array[i] == candidate) {
                count++;
            } else {
                count--;
            }
        }
        
        // 第二轮遍历:验证候选元素
        count = 0;
        for (int num : array) {
            if (num == candidate) {
                count++;
            }
        }
        
        // 返回多数元素,如果不存在则返回-1
        return (count > array.size() / 2) ? candidate : -1;
}

适用场景

  • 小规模数据:两种方法都可以
  • 大规模数据:推荐摩尔投票法
  • 内存受限环境:必须使用摩尔投票法

4. 算法局限性

  • 算法假设一定存在多数元素
  • 需要额外验证是否真的存在多数元素
  • 对输入数据的特殊要求

解题心得

多数元素问题不仅是一个简单的编程练习,更是算法设计的绝佳范例。它体现了:

  • 空间复杂度优化的重要性
  • 巧妙的算法思想
  • 在有限资源下高效解决问题的智慧