JZ39 数组中出现次数超过一半的数字

279 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情

Day31 2023/02/06

题目链接

难度:简单

题目

给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

数据范围:n≤50000,数组中元素的值 0≤val≤10000

要求:空间复杂度:O(1),时间复杂度:O(n)

输入描述:

保证数组输入非空,且保证有解

示例

输入:[1,2,3,2,2,2,5,4,2]
返回值:2

思路一


使用哈希法,遍历一遍数组然后,用map记录每一个元素出现的次数,然后再遍历一遍map,找出众数(即出现次数大于数组长度的一半)。

思路二


使用Boyer-Moore 投票算法,也是本题的最优解,当数组中两数不相等就消除,由于本题要求数组中一定有出现次数大于数组长度一半的元素,所以最坏情况每次都消去一个众数和一个非众数,最后留下的就是众数。
具体步骤:

  1. 初始化:候选人condidate = numbers[0], 候选人的投票次数count = 1;
  2. 遍历数组,如果当前数=condidate,则count++,否则count--,如果count=0, 表示没有候选人,则选取下一个数为候选人
  3. 直到数组遍历完毕,返回candidate

关键点


  • 当元素出现次数记录完毕后,寻找众数的时候仍然是循环数组,因为数组中的元素是record的key值。

算法实现


c++代码实现-哈希法

#include <unordered_map>
class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        unordered_map<int, int> record; //记录
        for (int val : numbers) record[val]++; //记录次数
        for (int val : numbers) { //找出众数
            if (record[val] > numbers.size() >> 1) return val;
        }
        return 0;
    }
};
  • 时间复杂度 O(n)O(n) --- 遍历了一遍数组,n为数组长度
  • 空间复杂度 O(n)O(n) --- 最坏情况下数组中元素各不相同,这时record数组中存储了n个元素

c++代码实现-Boyer-Moore投票算法

class Solution {
  public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int candidate = numbers[0]; //初始化为数组第一个元素
        int count = 1; //计数器
        for (int i = 1; i < numbers.size(); i++) {
            if (numbers[i] == candidate) { //如果相同count++
                count++;
            } else { 否则count--
                count--;
            }
            if (count == 0) { //等于零就让candidate为下一个元素
                candidate = numbers[i + 1];
                count++;
            }
        }
        return candidate;
    }
};
  • 时间复杂度 O(n)O(n) --- 只需要遍历一遍数组,n为数组长度
  • 空间复杂度 O(1)O(1) ---无额外的辅助空间

总结

  • 我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法,但是哈希法是一种时间换空间的方法,使用的时候要考虑空间复杂的限制。