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

70 阅读6分钟

1. 问题回顾

小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。

测试样例

样例1:

输入:array = [1, 3, 8, 2, 3, 1, 3, 3, 3]
输出:3

样例2:

输入:array = [5, 5, 5, 1, 2, 5, 5]
输出:5

样例3:

输入:array = [9, 9, 9, 9, 8, 9, 8, 8]
输出:9

2. 思考与题解

2.1 暴力法

使用暴力法来解决这个问题,我们可以通过两层循环来统计每个元素的出现次数,然后找到出现次数大于 ⌊ n/2 ⌋ 的元素。

详细步骤:

  1. 遍历数组:外层循环遍历数组中的每个元素。
  2. 统计出现次数:内层循环统计当前元素在数组中出现的次数。
  3. 检查多数元素:如果某个元素的出现次数大于 ⌊ n/2 ⌋,则返回该元素。
#include <vector>

int majorityElement(std::vector<int>& nums) {
    int n = nums.size();
    int majorityCount = n / 2;

    for (int i = 0; i < n; ++i) {
        int count = 0;
        for (int j = 0; j < n; ++j) {
            if (nums[j] == nums[i]) {
                count++;
            }
        }
        if (count > majorityCount) {
            return nums[i];
        }
    }

    // 由于题目保证存在多数元素,因此不会执行到这里
    return -1;
}

int main() {
    std::vector<int> nums1 = {3, 2, 3};
    std::vector<int> nums2 = {2, 2, 1, 1, 1, 2, 2};

    std::cout << "Example 1: " << majorityElement(nums1) << std::endl; // 输出 3
    std::cout << "Example 2: " << majorityElement(nums2) << std::endl; // 输出 2

    return 0;
}

复杂度分析:

时间复杂度
  1. 外层循环:外层循环的时间复杂度是 O(n),其中 n 是数组的长度。
  2. 内层循环:内层循环的时间复杂度也是 O(n)

因此,总的时间复杂度是 O(n^2)

空间复杂度
  1. 变量:除了几个变量(如 count 和 majorityCount)外,没有使用额外的空间。

因此,总的空间复杂度是 O(1)

2.2 排序法

基本思路:

  1. 排序数组:首先对数组进行排序。
  2. 找到多数元素:由于多数元素的出现次数大于 ⌊ n/2 ⌋,那么在排序后的数组中,位于中间位置的元素(即 nums[n/2])一定是多数元素。

详细步骤:

  1. 排序数组:使用 C++ 的 std::sort 函数对数组进行排序。
  2. 返回中间元素:排序后,数组的中间元素 nums[n/2] 就是多数元素。
#include <vector>
#include <algorithm>

int majorityElement(std::vector<int>& nums) {
    // 对数组进行排序
    std::sort(nums.begin(), nums.end());
    // 返回中间位置的元素
    return nums[nums.size() / 2];
}

int main() {
    std::vector<int> nums1 = {3, 2, 3};
    std::vector<int> nums2 = {2, 2, 1, 1, 1, 2, 2};

    std::cout << "Example 1: " << majorityElement(nums1) << std::endl; // 输出 3
    std::cout << "Example 2: " << majorityElement(nums2) << std::endl; // 输出 2

    return 0;
}

复杂度分析:

时间复杂度
  1. 排序:排序的时间复杂度是 O(n log n),其中 n 是数组的长度。这是因为在最坏情况下,排序算法需要对数组中的每个元素进行比较和交换操作。
  2. 访问中间元素:访问中间元素的时间复杂度是 O(1),因为直接通过索引访问数组元素是一个常数时间操作。

因此,总的时间复杂度是 O(n log n)

空间复杂度
  1. 排序:排序的空间复杂度取决于所使用的排序算法。对于 std::sort 来说,它通常是 O(log n) 的空间复杂度,因为它是基于快速排序、堆排序和插入排序的混合算法。快速排序的空间复杂度是 O(log n),而堆排序和插入排序的空间复杂度是 O(1)
  2. 其他变量:除了排序所需的空间外,没有使用额外的空间。

2.3 哈希表法

详细步骤:

  1. 创建哈希表:使用 std::unordered_map 来存储每个元素及其出现次数。
  2. 统计元素出现次数:遍历数组,将每个元素及其出现次数存储在哈希表中。
  3. 找到多数元素:遍历哈希表,找到出现次数大于 ⌊ n/2 ⌋ 的元素。
#include <vector>
#include <unordered_map>

int majorityElement(std::vector<int>& nums) {
    std::unordered_map<int, int> countMap;
    int majorityCount = nums.size() / 2;

    // 统计每个元素的出现次数
    for (int num : nums) {
        countMap[num]++;
        // 如果某个元素的出现次数大于 ⌊ n/2 ⌋,则返回该元素
        if (countMap[num] > majorityCount) {
            return num;
        }
    }

    // 由于题目保证存在多数元素,因此不会执行到这里
    return -1;
}

int main() {
    std::vector<int> nums1 = {3, 2, 3};
    std::vector<int> nums2 = {2, 2, 1, 1, 1, 2, 2};

    std::cout << "Example 1: " << majorityElement(nums1) << std::endl; // 输出 3
    std::cout << "Example 2: " << majorityElement(nums2) << std::endl; // 输出 2

    return 0;
}

复杂度分析:

时间复杂度
  1. 遍历数组:遍历数组的时间复杂度是 O(n),其中 n 是数组的长度。
  2. 哈希表操作:哈希表的插入和查找操作的平均时间复杂度是 O(1)

因此,总的时间复杂度是 O(n)

空间复杂度
  1. 哈希表:哈希表的空间复杂度是 O(n),因为最坏情况下,哈希表需要存储数组中的所有不同元素。

因此,总的空间复杂度是 O(n)

2.4 摩尔投票法

基本原理

摩尔投票法(Boyer-Moore Voting Algorithm)是一种用于在数组中找到出现次数大于 ⌊ n/2 ⌋ 的多数元素的高效算法。其核心思想是通过抵消不同元素的计数来找到多数元素。

  1. 多数元素的定义:在一个大小为 n 的数组中,多数元素是指出现次数大于 ⌊ n/2 ⌋ 的元素。由于多数元素的出现次数大于数组长度的一半,因此在数组中,多数元素的数量一定比其他所有元素的数量之和还要多。
  2. 抵消计数:摩尔投票法的核心思想是通过抵消不同元素的计数来找到多数元素。具体来说,算法维护一个票数计数器 vote 和一个候选元素 Mode。遍历数组时,如果当前元素与候选元素相同,则计数器加 1;如果不同,则计数器减 1。当计数器减到 0 时,说明当前候选元素的计数已经被抵消完,需要重新选择一个新的候选元素。
  3. 最终候选元素:由于多数元素的出现次数大于 ⌊ n/2 ⌋,因此在遍历结束后,候选元素 candidate 一定是多数元素。

实现步骤

详细步骤:

  1. 初始化计数器和候选元素:初始化一个计数器 vote 和一个候选元素 Mode

  2. 遍历数组:遍历数组中的每个元素。

    • 如果计数器 vote 为 0,则将当前元素设为候选元素,并将计数器设为 1。
    • 如果当前元素等于候选元素,则计数器加 1。
    • 如果当前元素不等于候选元素,则计数器减 1。
  3. 返回候选元素:遍历结束后,候选元素即为多数元素。

#include <iostream>
#include <vector>

using namespace std;

int solution(vector<int> array) {
    //Mole Vote
    int Mode = array[0];
    int vote = 0;
    for (int i = 0; i < array.size(); i++) {
        if (array[i] == Mode) {
            vote += 1;
        } else {
            vote -= 1;
        }

        if (vote == 0) {
            Mode = array[i + 1];
        }
    }
    
    return Mode;
}

复杂度分析:

时间复杂度
  1. 遍历数组:遍历数组的时间复杂度是 O(n),其中 n 是数组的长度。

因此,总的时间复杂度是 O(n)

空间复杂度
  1. 变量:除了几个变量(如 vote 和 Mode)外,没有使用额外的空间。

因此,总的空间复杂度是 O(1)