今天是小呆刷题的第 16 天,今天的题目是:力扣(LeetCode)的第 169 题,多数元素

题目要求

给定一个大小为n的数组nums,返回其中的多数元素。多数元素是指在数组中出现次数大于⌊ n/2 ⌋的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例:

1
2
3
4
5
输入:nums = [3,2,3]
输出:3

输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109

解题思路

这道题小呆的第一思路就是用哈希表来解决,因为跟返回数组中出现次数最多的题的思路是一样的,只不过这道题是返回次数大于n/2的那个元素。

哈希表-多数元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function (nums) {
let hasMap = new Map(),
half = nums.length / 2

for (let num of nums) {
if (hasMap.has(num)) {
hasMap.set(num, hasMap.get(num) + 1)
} else {
hasMap.set(num, 1)
}
if (hasMap.get(num) > half) {
return num
}
}
}

习惯性的做完去评论区开拓视野,然后发现了这道题至少有 4 种解法,除了上面的哈希表,还可以用排序摩尔投票分治来解决,分治目前小呆还没接触过,所以直接跳过,等到学会分治算法之后再回过头来看这道题。其他两种题解分享一下,首先是排序:

1
2
3
4
5
6
7
8
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function (nums) {
nums.sort((a, b) => a - b)
return Math.floor(nums.length / 2)
}

其次是摩尔投票,摩尔投票的核心就是对拼消耗。评论区你我山巅自相逢同学解释的挺好:

题目中我们需要查找的数字超过数组长度的一半,也就是说,要查找的数字 target 的个数会超过其他数字个数。

假设不同数字相互抵消,那么最后剩下的数字,就是我们要找的多数元素。

我们可以把这个过程打个比方,比如现在多军对峙,假设阵营 A 士兵人数比其他方的人数都多,阵营 A 士兵能以一杀一,那么只要阵营 A 士兵不杀自己人(相同数字),去杀不同阵营的人(不同数字),那么最后剩下的那些士兵,就是阵营 A 的士兵。

摩尔投票-多数元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function (nums) {
let count = 1
let majority = nums[0] // 先选择nums[0]所代表的队伍为对拼方
for (let i = 1; i < nums.length; i++) {
if (count === 0) {
// 这支队伍暂时没人了可以对拼了,那就先换只队伍
marjority = nums[i]
}
if (nums[i] === majority) {
// 友军,拉到我们的队伍里
count++
} else {
count-- // 非友军,对拼消耗
}
}
}

小结

今天又学到了一种新算法,摩尔投票法,收获满满!

引用

力扣 LeetCode 的第 169 题-多数元素

通俗易懂的摩尔投票法-作者:你我山巅自相逢