主题
题意:计算
尝试设计一个
想象一众武林高手比武,谁会笑到最后?
我用「擂台赛」打比方:
- 擂主登场:
成为初始擂主,生命值为 。 - 挑战者出现:遍历后续元素,作为挑战者。
- 比武:如果挑战者与擂主属于同一门派(值相同),那么擂主生命值加
,否则擂主生命值减 。 - 擂主更迭:如果比武后,擂主生命值降为
(同归于尽),那么下一个挑战者成为新的擂主,生命值为 。 - 最后在擂台上的那人,便是武林盟主(绝对众数)。
为什么这样做是对的?
设出现次数最多的元素的出现次数为
证明:上述过程中,每次擂主的生命值降为
- 对于除了最后一段的每一段(注意这些段的擂主不一定是绝对众数,比如绝对众数是
,这一段是 ),设绝对众数在其中出现了 次,其余元素的出现次数之和为 ,则必然有 。这可以用反证法证明,如果 ,那么绝对众数血多,不可能被其余元素同归于尽,绝对众数的生命值在这段结束时必然大于 ,矛盾。由此可得 ,意思是,把 减去 , 减去 ,所得到的 和 仍然满足 。依此类推,每一段结束时,在剩余元素(未遍历到的元素)中,设出现次数最多的元素的出现次数为 ,其余元素的出现次数之和为 ,那么 始终成立。 - 对于最后一段,由于
,绝对众数血多,不可能被其余元素同归于尽,绝对众数的生命值最终必然大于 ,所以最后在擂台上的是绝对众数。
python
class Solution:
def majorityElement(self, nums: List[int]) -> int:
ans = hp = 0
for x in nums:
if hp == 0: # x 是初始擂主,生命值为 1
ans, hp = x, 1
else: # 比武,同门加血,否则扣血
hp += 1 if x == ans else -1
return anscpp
// C++ 版待补充cpp
class Solution {
public:
int majorityElement(vector<int>& nums) {
int ans = 0, hp = 0;
for (int x : nums) {
if (hp == 0) { // x 是初始擂主,生命值为 1
ans = x;
hp = 1;
} else { // 比武,同门加血,否则扣血
hp += x == ans ? 1 : -1;
}
}
return ans;
}
};复杂度分析
- 时间复杂度:
,其中 是 的长度。 - 空间复杂度:
。
思考题
给定数组
欢迎在评论区分享你的思路/代码。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府