NC73 数组中出现次数超过一半的数字
题意
给你一个数组,找出出现次数超过一半的数字。(也叫主元素问题)
1. 暴力法
枚举每一个数,看他是不是在数组中出现了超过一半的次数。
- 时间复杂度: , 两重循环
- 空间复杂度: .
2. 排序法
既然是众数且超过了一半,那么肯定也是中位数,排序后取中点即为所求。
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { int n = numbers.size(); sort(numbers.begin(), numbers.end()); return numbers[n>>1]; } };
- 时间复杂度:. 依赖stl的sort方法。
- 空间复杂度:
3. 哈希表法
根据众数的定义,它出现的次数最多,所以用一个哈希表统计每个数字出现的次数,找最多的即可。
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { unordered_map<int, int> m; // 统计每个数字出现的次数 for (auto v : numbers) m[v]++; // 找出现次数超过一半的 for (auto [k, v] : m) { if (v > numbers.size()/2) return k; } // 兜底逻辑,没找到返回0 return 0; } };
- 时间复杂度:. 遍历一轮数组和一轮map。
- 空间复杂度:. 定义了一个大小为的map。
4. Boyer-Moore 投票算法(本题最佳算法)
这道题中,众数多了一个附加条件,出现的次数大于一半,那么说明该数比其他数字加起来出现的数都多。
Boyer-Moore 投票算法分为两步:
- 初始化两个变量,记录当前的候选值candidate和候选值出现的次数count。
- 第一轮遍历数组,如果count为0, 赋值当前元素予candidate,且count自加1; 如果元素与candidate相等,count自加1,否则自减1。
- 第二轮遍历数组,若candidate在数组出现次数大于数组size一半,则为多数元素,否则不存在多数元素。
对于本题,题目有一个已知条件,说明超过一半的众数一定存在,故第二轮遍历可省略。
如图所示:
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { int res = 0, cnt = 0; for (auto v : numbers) { if (v == res) cnt++; // 遇到同样的数,票数+1 else { cnt--; // 不同的数票数减一 if (cnt <= 0) res = v, cnt = 1; // 没有数了,则当前的数为候选人, 注意不能写=0, 否则第一个数的边界上会wa } } // 第二轮遍历省略。 // cnt = 0; // for (auto v : numbers) { // if (v == res) cnt++; // } // if (cnt < numbers.size()/2) return 0; return res; } };
- 时间复杂度:. 遍历一轮数组。
- 空间复杂度:. 没有额外的数组定义