思路
根据上面的算法思想,我们将当前票数最多的候选人与获得的票数(抵消后)分别存储在 major
和 count
中。
当我们遍历下一个选票时,判断当前 count
是否为零:
-
如果
count == 0
: 代表major
空缺,直接将当前候选人赋值给major
,并将count ++
-
若
count != 0
: 代表当前major
的票数未被完全抵消,令count --
,不同意见的斯巴达勇士去激情对撞。
初始值 count = 0, major = -1
详细图解
以 [2,2,1,3,1,2,2]
为例。
遍历数组第一个元素 2
,因 count == 0
,所以将 2
赋值给 major
,且票数 count = 1
第二个数组元素依旧是候选人 2
,得票数 count ++
第三个元素是 1
,与 major
冲突,因此发生激情对撞,当前 major
的票数抵消 1
票。
第四个元素是 3
, 与 major
冲突,产生激情对撞,当前 major
的票数抵消一票。
当遍历到第五个元素 1
时,此时 count = 0
,说明 marjor
位置空缺,所以令 majro = 1
,且 count = 1
第六个元素是 2
,与 major
冲突,产生激情对撞,当前 major
的票数抵消 1
票,此时 count
又变为 0
遍历最后一个元素 2
,当前 count = 0
,说明 marjor
位置空缺,所以令 majro = 1
,且 count = 1
遍历完毕,2
元素很有可能就是斯巴达新的领袖。
计数阶段: 统计 2
元素出现的次数是否大于一半,2
出现四次,大于一半,所以我宣布斯巴达共和国未来的领袖就是 2
了(怎么听起来怪怪的。。。)
真题一:主要元素
题目来源: 面试题 17.10. 主要元素
题目描述:数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。
如果没有最后一句要求,肯定咱们大脑中第一瞬间想到的就是哈希表,使用哈希表存储每个元素的出现次数。
但是咱们现在可不是凡人啊,连斯巴达共和国的领袖都可以选举出来了,肯定要上摩尔选举法啊。
这个题的思想与我举得图文讲解极度类似,我就不多做赘述了。
/**
-
@param {number[]} nums
-
@return {number}
*/
var majorityElement = function(nums) {
let major = -1;
let count = 0;
for (var i = 0; i<nums.length; i++) {
if (count === 0) {
major = nums[i];
}
if (nums[i] === major) {
count++;
} else {
// 票数抵消
count–;
}
}
count = 0;
const length = nums.length;
// 计数阶段
for (var i = 0; i<nums.length; i++) {
if (nums[i] === major) {
count++;
}
}
return count * 2 > length ? major : -1;
};
真题二:求众数 II
题目来源: 229. 求众数 II
题目描述:给定一个大小为 n
的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋
次的元素。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。
我当时刷这道题时苦思冥想,咋都想不出空间复杂度为 O(1)的解决方案,看官方题解醍醐灌顶,这也能摩尔投票法。咱们来一起分析一下:
做这个题之前,首先要理解:出现超过 ⌊ n/3 ⌋ 次的元素最多有 2 个。咱们可以稍微反证一下:如果出现了 2
个以上超过 ⌊ n/3 ⌋
次的元素,例如 3
个,哪意味着当前数组中会存在 3 * ⌊ n/3 ⌋ > n
,与现实冲突。
思路
我把这个题形象一下,其实就相当于斯巴达勇士们要选正副领袖,所以现在两个不同意见得勇士打架就不合理了,因为这两个勇士的候选人可能是正副领袖。
既然是要选两个领袖,那是不是也可以类比为三个不同意见得勇士打架抵消那,下面我们来具体操作一下,看看能不能实现:
如果数组中只有一个元素 x
超过了 ⌊ n/3 ⌋
,把数组分为两部分:一部分为 k
个 x
,另一部分为 (n-k)/3
组三个不同的元素,如果三个不同元素会被抵消,最终只会剩下 k
个 x
。
如果数组中有两个元素 x,y
超过了 ⌊ n/3 ⌋
,把数组分为三部分:第一部分为 k
个 x
,第二部分为 l
个 y
,第三部分为 (n-k-l) / 3
组三个不同的元素,如果三个不同元素会被抵消,最终会剩下 k
个 x
和 l
个 y
。
通过上面得分析,三个不同意见的勇士相抵消是可以实现的,算法流程为:
-
检查当前元素是否为第一个选中的元素或第二个选中的元素。如果存在相同,对应元素得票数加一。
-
如果与两个元素都不相同,则三个不同得元素抵消一次
-
抵消结束后,若存在最终选票大于 0 得元素,进行计数阶段,检查该元素次数是否大于
⌊ n/3 ⌋
上代码:
/**
-
@param {number[]} nums
-
@return {number[]}
*/
var majorityElement = function(nums) {
let major1 = 0,
major2 = 0,
count1 = 0,
count2 = 0;
const length = nums.length;
// 投票抵消阶段
for (let i = 0; i<length; i++) {
if (count1 > 0 && major1 === nums[i]) {
count1 ++;
}else if (count2 > 0 && major2 === nums[i]) {
count2 ++;
}else if (count1 === 0) {
count1 ++;
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
715824313934)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!