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

38 阅读4分钟

问题描述

小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

这道题目要求我们找到一个数字,这个数字在给定的数组中出现的次数超过了数组总数的一半。也就是说,题目中的数字被称为 "多数元素" (Majority Element)。多数元素的出现次数大于数组元素总数的一半,因此它一定是唯一的。

题目分析

给定一个整数数组,数组中的某个数字出现的次数超过数组总数的一半,任务是找出这个数字。

  • 输入:一个整数数组 array
  • 输出:返回数组中出现次数超过一半的数字。

解法分析

这道题目可以通过 Boyer-Moore 投票算法 来高效解决。这个算法的核心思想是,通过在遍历数组时维护一个候选数字和一个计数器,最终确定出现次数超过一半的数字。

Boyer-Moore 投票算法

核心思想

  1. 选择候选数字:遍历数组时,我们不断更新候选数字。如果当前数字与候选数字相同,计数器加一;如果不同,计数器减一。如果计数器变为零,我们选择当前数字作为新的候选数字。
  2. 验证候选数字:由于题目保证有一个数字出现的次数超过数组总数的一半,所以在第一轮遍历后,我们得到的候选数字必定是多数元素。因此,我们只需要再次遍历数组,统计该数字的出现次数,验证它是否真的满足题目要求。

具体实现

public class Main {
    public static int solution(int[] array) {
        int count = 0;  // 计数器
        Integer candidate = null;  // 候选数字

        // 第一遍遍历,找出候选数字
        for (int num : array) {
            if (count == 0) {
                candidate = num;  // 选择一个新的候选数字
            }
            count += (num == candidate) ? 1 : -1;  // 如果当前数字与候选相同,计数器加1,否则减1
        }

        // 第二遍遍历,验证候选数字是否确实是多数元素
        count = 0;  // 重新计数
        for (int num : array) {
            if (num == candidate) {
                count++;  // 统计候选数字出现的次数
            }
        }

        // 判断候选数字出现的次数是否超过数组长度的一半
        if (count > array.length / 2) {
            return candidate;  // 如果是多数元素,返回该数字
        } else {
            return -1;  // 如果没有多数元素,返回-1
        }
    }

    public static void main(String[] args) {
        // 测试样例
        System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);  // 输出3
        System.out.println(solution(new int[]{5, 5, 5, 1, 2, 5, 5}) == 5);  // 输出5
        System.out.println(solution(new int[]{9, 9, 9, 9, 8, 9, 8, 8}) == 9);  // 输出9
    }
}

代码解释

  1. 候选数字的选择

    • 我们使用 count 来记录当前候选数字的计数。如果 count == 0,说明当前没有有效的候选数字,就将当前数字设置为新的候选数字。
    • 对于每一个数字,如果它与候选数字相同,count 加一;如果不同,count 减一。如果 count 为零,则需要重新选择候选数字。
  2. 验证候选数字

    • 一旦遍历完成,我们得到一个候选数字。接着,再次遍历数组统计该候选数字出现的次数,验证它是否真的出现超过数组长度的一半。如果是,则返回该数字;否则返回 -1

算法分析

  • 时间复杂度O(n)。我们需要遍历数组两次,第一遍找到候选数字,第二遍验证候选数字的出现次数。
  • 空间复杂度O(1)。只使用了常数空间来存储计数器和候选数字,因此空间复杂度是常数级别。

优点

  • 效率高:该算法只需要遍历数组两次,时间复杂度为 O(n),适合处理大规模数据。
  • 空间节省:只需要常数级别的额外空间,节省了内存。

总结

这道题的关键在于理解 Boyer-Moore 投票算法,它通过巧妙地维护一个候选数字和计数器,在遍历数组时可以高效地找出多数元素。这种方法不仅时间复杂度低,而且空间复杂度也极低,是解决这类问题的经典算法。