携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
169. 多数元素 [easy]
题目描述
给定一个大小为 n 的数组 nums ,返回其中的多数元素。
多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
提示:
n == nums.length1 <= n <= 5 * 104-109 <= nums[i] <= 109
**进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
解题思路——hashmap
使用一个 hashmap 来统计每个数字出现的次数,最后输出次数最多的那个。
这里除了对象外,我还使用了两个额外的变量 maxCnt 和 maxValue,用于标记当前出现次数最多的数字及其对应的下标。当出现次数 hashmap[number] > maxCnt 时,更新 maxCnt 和 maxValue 的值,这样最后输出的时候就不用再次遍历 hashmap 来找出现最多的数字了。
题解
var majorityElement = function(nums) {
const lens = nums.length;
if(lens == 1) return nums[0];
const hashmap = {};
let maxCnt = -Infinity, maxValue = -Infinity;
for(let v of nums) {
hashmap[v]++ || (hashmap[v] = 1);
if(hashmap[v] > maxCnt) {
maxCnt = hashmap[v];
maxValue = v;
}
}
return maxValue;
};
解题思路——摩尔投票算法
说实话第一次看的时候就觉得很巧妙了,不过没看懂。
写下今天的题解的时候,自己动手试了一遍,边写脑海里边过逻辑,噼里啪啦行云流水点击执行代码!没问题!点击提交!通过!
回到正题。
摩尔投票算法代入到本题,大概意思就是:把这每一个数字想象成一个士兵,那么出现的次数最多的数字代表的士兵数就最多,一换一的情况下,战争的最后赢家一定是人多的那边。
拿 [2,2,1,1,2,2] 来说:
- 初始化当前士兵为 2,士兵数量为1,遍历数组。
- 遇到2,还是当前士兵的阵营,那么士兵数量加1,此时士兵数量为2。
- 遇到2,还是当前士兵的阵营,那么士兵数量加1,此时士兵数量为3。
- 遇到1,非已阵营,一换一,那么2的士兵数量减1,此时2的士兵数量只剩1了。
- 遇到1,非已阵营,一换一,那么2的士兵数量减1,此时2的士兵数量为0了,gg,更换阵营,当前士兵为1,士兵数量1。
- 遇到2,非已阵营,一换一,那么1的士兵数量减1,此时1的士兵数量为0了,gg,更换阵营,当前士兵为2,士兵数量1。
- 遇到2,还是当前士兵的阵营,那么士兵数量加1,此时士兵数量为2。
遍历到最后,存活士兵数最多的数字就是所求的解。
题解
var majorityElement = function(nums) {
const lens = nums.length;
if(lens == 1) return nums[0];
let solider = nums[0], soliderCnt = 1;
for(let i=0; i<lens; ++i) {
if(solider === nums[i]) {
soliderCnt++;
} else {
soliderCnt--;
}
if(soliderCnt === 0) {
solider = nums[i];
soliderCnt = 1;
}
}
return solider;
};
解题思路——排序
官方题解有个娱乐解法,使用排序,那么 n/2 位置的一定是数量过半的。
229. 多数元素Ⅱ [middle]
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
示例 1:
输入:nums = [3,2,3]
输出:[3]
提示:
1 <= nums.length <= 5 * 104-109 <= nums[i] <= 109
**进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。
解题思路——Map
这道题和上一道题有点不一样了:所有出现次数超过 n/3 的元素。
也就是说结果是个数组。
这道题可以用 Map 来进行统计,随后通过遍历 Map 集合,找到所有出现次数大于 n/3 的元素输出就好了。
题解
var majorityElement = function(nums) {
const lens = nums.length, targetLens = lens / 3;
if(lens == 1) return [nums[0]];
const hashmap = new Map();
for(let v of nums) {
if(hashmap.has(v)) {
hashmap.set(v, hashmap.get(v) + 1);
} else {
hashmap.set(v, 1);
}
}
const res = []
for(let [k,v] of hashmap) {
v>targetLens && res.push(k);
}
return res;
};
解题思路——摩尔投票算法
对于 n 个数来说,如果我们要找到数量大于 n/2 的数字,那么这样的数字必然只存在一个,否则就超过 n 的范围了;同样的,找到数量大于 n/3 的数组,那么这样的数字必然只存在两个;由此推得,n 个数中,大于 n/k 的数字的个数不会超过 k-1 个。
那么对于上一题来说,需要找的是大于 n/2 的,结果只可能是一个数字,那么这道题需要找的是大于 n/3 的数字,答案至多有两个,因此我们的士兵数量增加到两个。
-
初始化两个变量
solider1和solider2代表接下来会遇到的两个阵营,数量都为0;开始遍历数组。 -
如果
cnt1 === 0时,说明solider1还有空位放一个士兵,此时给solider1赋值并让cnt1++。 -
如果
cnt2 === 0时,说明solider2还有空位放一个士兵,此时给solider2赋值并让cnt2++。 -
如果
cnt1 > 0,且当前遍历到的数字为solider1阵营时,士兵数cnt1++。 -
如果
cnt2 > 0,且当前遍历到的数字为solider2阵营时,士兵数cnt2++。 -
否则说明既不是
solider1阵营也不是solider2阵营的,三方抵消,cnt1--;cnt2--。 -
到最后
solider1和solider2的士兵数如果不为0,则说明它们的士兵在数量上是取胜的,大于其他所有的数字,仅仅知道这个还不够,这会我们只是知道它俩的数量是 top2,而不知道它们的数量具体是多少?有没有大过n/3? -
所以需要再遍历一次
nums数组,判断存活士兵数量大于0的阵营的士兵数,得到的结果存到数组中。
题解
var majorityElement = function(nums) {
const lens = nums.length, targetLens = lens / 3;
if(lens == 1) return [nums[0]];
let solider1 = 0, solider2 = 0, cnt1 = 0, cnt2 = 0;
for(let i=0; i<lens; ++i) {
if(cnt1 > 0 && solider1 === nums[i]){
cnt1++;
} else if(cnt2 > 0 && solider2 === nums[i]) {
cnt2++;
} else if(cnt1 === 0) {
solider1 = nums[i];
cnt1 = 1;
} else if(cnt2 === 0) {
solider2 = nums[i];
cnt2 = 1;
} else {
cnt2--;
cnt1--;
}
}
let count1 = 0, count2 = 0, res = [];
for(let v of nums) {
if(v === solider1 && cnt1) {
count1++;
} else if(v === solider2 && cnt2) {
count2++;
}
}
if(count1 > targetLens) res.push(solider1);
if(count2 > targetLens) res.push(solider2);
return res;
};