题目:
官方链接:
参考答案:
多数元素(Majority Element):Java 和 JavaScript 高效解题指南
目录
- 引言
- 题目理解
- 解题思路
- 摩尔投票算法
- 哈希表计数(了解即可,面试不用)
- 详细代码实现
- Java 版
- JavaScript 版
- 时代提升:掌握投票算法,轻松应对多数元素问题
1. 引言
在数组中找出现次数超过一半的元素(众数),是面试和练习中的常青题。理解并能够熟练应用摩尔投票算法,将极大提升你的算法水平。
2. 题目理解
题意简单明了:
- 给定数组
nums
- 寻找数组中出现次数大于
n/2
的元素 - 保证数组一定存在这样的元素
示例:
Input: [3,2,3]
Output: 3
Input: [2,2,1,1,1,2,2]
Output: 2
3. 解题思路
摩尔投票算法(投票法)
这是巧妙的线性时间、常数空间算法:
- 维护两个变量:候选元素
candidate
和计数器count
- 遍历数组:
- 若
count
为0,更新候选元素为当前元素 - 若当前元素等于候选元素,
count++
- 否则
count--
- 若
- 遍历结束后,候选元素即为众数
这种算法特别适合找数组中的多数元素,严格O(n),空间O(1)。
其他方式(了解即可)
- 哈希表计数(空间O(n))
- 排序后取中间元素(时间O(n log n))
4. 详细代码实现
Java 实现
public class Solution {
public int majorityElement(int[] nums) {
int candidate = 0, count = 0;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
JavaScript 实现
var majorityElement = function(nums) {
let candidate = null, count = 0;
for (let num of nums) {
if (count === 0) {
candidate = num;
}
count += (num === candidate) ? 1 : -1;
}
return candidate;
};
5. 小技巧和总结
- 投票策略:用数字代表“票数”,最终票数最大的数字即为众数
- 时间复杂度:O(n)
- 空间复杂度:O(1)
- 实用场景:选举、多数组合分析等
掌握投票算法,不仅帮你应对本题,更能应付一系列大数据分析题目。坚持练习,算法能力日益提升!
祝你学习顺利,代码成为你的得力武器!
多数元素问题详解
目录
问题描述
给定一个大小为 n
的数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例
-
示例 1:
- 输入:
nums = [3,2,3]
- 输出:
3
- 输入:
-
示例 2:
- 输入:
nums = [2,2,1,1,1,2,2]
- 输出:
2
- 输入:
解题思路
为了找到数组中的多数元素,我们可以使用 Boyer-Moore 投票算法。该算法的核心思想是通过维护一个候选元素和一个计数器来找到可能的多数元素。具体步骤如下:
- 初始化:设置一个候选元素
candidate
和一个计数器count
,初始值均为 0。 - 遍历数组:
- 如果
count
为 0,则将当前元素设为candidate
。 - 如果当前元素等于
candidate
,则count
增加 1。 - 如果当前元素不等于
candidate
,则count
减少 1。
- 如果
- 返回结果:遍历结束后,
candidate
即为多数元素。
该算法的时间复杂度为 O(n),空间复杂度为 O(1)。
Boyer-Moore 投票算法实现
Java实现
public class Solution {
public int majorityElement(int[] nums) {
int candidate = 0;
int count = 0;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
JavaScript实现
var majorityElement = function(nums) {
let candidate = 0;
let count = 0;
for (let num of nums) {
if (count === 0) {
candidate = num;
}
count += (num === candidate) ? 1 : -1;
}
return candidate;
};
复杂度分析
- 时间复杂度:O(n),其中 n 是数组
nums
的长度。我们只需遍历数组一次。 - 空间复杂度:O(1),我们只使用了常数空间来存储变量。
总结
多数元素问题是一个经典的数组问题,通过 Boyer-Moore 投票算法,我们可以高效地找到数组中的多数元素。希望本文能帮助你更好地理解这一问题及其解决方案,掌握算法的基本思想是解决许多类似问题的关键。
LeetCode 169:多数元素 - 摩尔投票法(Java & JavaScript)
目录
- 题目描述
- 解题思路
- 2.1 算法思想
- 2.2 算法步骤
- Java 代码实现
- 3.1 代码
- 3.2 代码详解
- JavaScript 代码实现
- 4.1 代码
- 4.2 代码详解
- 复杂度分析
- 总结与扩展
1. 题目描述
给定一个大小为 n
的数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
提示:
n == nums.length
1 <= n <= 5 * 10^4
-10^9 <= nums[i] <= 10^9
进阶: 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
2. 解题思路
2.1 算法思想
本题可以使用摩尔投票法(Boyer-Moore Voting Algorithm)来解决,该算法可以在 O(n) 的时间复杂度和 O(1) 的空间复杂度内找到数组中的多数元素。
摩尔投票法的核心思想是:在数组中,如果一个元素的出现次数超过数组长度的一半,那么在遍历数组时,我们可以将该元素视为“候选人”,并维护一个计数器。当我们遇到与候选人相同的元素时,计数器加 1;当我们遇到与候选人不同的元素时,计数器减 1。如果计数器减为 0,则更换候选人。最终,计数器不为 0 的候选人就是多数元素。
2.2 算法步骤
- 初始化候选人
candidate
为数组的第一个元素,计数器count
为 1。 - 从数组的第二个元素开始遍历数组。
- 如果当前元素与候选人相同,则计数器加 1。
- 如果当前元素与候选人不同,则计数器减 1。
- 如果计数器减为 0,则更换候选人为当前元素,并将计数器重置为 1。
- 遍历结束后,候选人
candidate
就是多数元素。
3. Java 代码实现
3.1 代码
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == candidate) {
count++;
} else {
count--;
if (count == 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
}
}
3.2 代码详解
majorityElement(int[] nums)
函数:candidate
:候选人,初始化为数组的第一个元素。count
:计数器,初始化为 1。for (int i = 1; i < nums.length; i++)
:从数组的第二个元素开始遍历数组。if (nums[i] == candidate)
:如果当前元素与候选人相同,则计数器加 1。else
:如果当前元素与候选人不同,则计数器减 1。if (count == 0)
:如果计数器减为 0,则更换候选人为当前元素,并将计数器重置为 1。
return candidate
:返回候选人,即多数元素。
4. JavaScript 代码实现
4.1 代码
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function(nums) {
let candidate = nums[0];
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (nums[i] === candidate) {
count++;
} else {
count--;
if (count === 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
};
4.2 代码详解
JavaScript 代码的逻辑与 Java 代码基本相同,只是在语法上有所差异。
5. 复杂度分析
- 时间复杂度: O(n),其中 n 是数组的长度。
- 空间复杂度: O(1),只使用了常数级别的额外空间。
6. 总结与扩展
本题可以使用摩尔投票法来解决,该算法可以在 O(n) 的时间复杂度和 O(1) 的空间复杂度内找到数组中的多数元素。
扩展:
- 可以尝试解决类似的投票问题,例如:
- LeetCode 229:求众数 II
希望这篇博客能够帮助你理解多数元素问题,并掌握解决 LeetCode 169 题的方法。 祝你刷题愉快!
【力扣169题】多数元素 - 摩尔投票法
目录:
-
问题描述
-
解决方案
2.1 Java 实现
2.2 JavaScript 实现 -
算法思路
-
复杂度分析
-
总结与拓展
-
问题描述
给定一个大小为 n 的数组nums
,返回其中的多数元素。多数元素是指在数组中出现次数大于⌊ n/2 ⌋
的元素。 -
解决方案
2.1 Java 实现
class Solution {
public int majorityElement(int[] nums) {
int candidate = 0;
int count = 0;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
2.2 JavaScript 实现
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function(nums) {
let candidate = 0;
let count = 0;
for (let num of nums) {
if (count === 0) {
candidate = num;
}
count += (num === candidate) ? 1 : -1;
}
return candidate;
};
- 算法思路
摩尔投票法是一种巧妙的算法,它利用了多数元素出现次数大于⌊ n/2 ⌋
的特性。
算法步骤如下:
- 初始化
candidate
为 0,count
为 0。 - 遍历数组
nums
,如果count
为 0,则将当前元素num
赋值给candidate
。 - 如果当前元素
num
等于candidate
,则count
加 1,否则count
减 1。 - 最后返回
candidate
即可。
这种算法的核心思想是,多数元素和非多数元素两两抵消,最后剩下的就是多数元素。
- 复杂度分析
- 时间复杂度: O(n),其中 n 是数组
nums
的长度。我们只需要遍历数组一次。 - 空间复杂度: O(1),我们只使用了常数个额外空间。
- 总结与拓展
摩尔投票法是一种非常巧妙的算法,它可以在 O(n) 时间和 O(1) 空间内解决这个问题。这种算法思路在很多其他问题中也有应用,比如 229. 求众数 II。
除了这种方法,还有其他的算法技巧,如哈希表、分治算法等,都值得我们去学习和掌握。通过不断练习和积累,我们可以逐步提高自己的算法能力,更好地解决各种编程问题。
力扣 169 题:多数元素新手入门攻略
一、题目概述
题目描述
本题是力扣第 169 题,难度为简单。给定一个大小为 n
的数组 nums
,需要返回其中的多数元素。多数元素的定义是在数组中出现次数大于 ⌊ n/2 ⌋
的元素。同时题目假设数组是非空的,并且给定的数组总是存在多数元素。
示例
- 输入:
nums = [3, 2, 3]
,输出:3
- 输入:
nums = [2, 2, 1, 1, 1, 2, 2]
,输出:2
提示
n == nums.length
1 <= n <= 5 * 10^4
-10^9 <= nums[i] <= 10^9
进阶要求
尝试设计时间复杂度为 O ( n ) O(n) O(n)、空间复杂度为 O ( 1 ) O(1) O(1) 的算法解决此问题。
二、解题方法分析
方法一:哈希表法
思路
使用哈希表来统计数组中每个元素出现的次数,然后遍历哈希表,找出出现次数大于 ⌊ n/2 ⌋
的元素。
Java 代码实现
import java.util.HashMap;
import java.util.Map;
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> countMap = new HashMap<>();
int n = nums.length;
// 统计每个元素出现的次数
for (int num : nums) {
countMap.put(num, countMap.getOrDefault(num, 0) + 1);
}
// 遍历哈希表,找出多数元素
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
if (entry.getValue() > n / 2) {
return entry.getKey();
}
}
return -1; // 按照题目假设,不会执行到这里
}
}
JavaScript 代码实现
var majorityElement = function(nums) {
const countMap = new Map();
const n = nums.length;
// 统计每个元素出现的次数
for (let num of nums) {
if (countMap.has(num)) {
countMap.set(num, countMap.get(num) + 1);
} else {
countMap.set(num, 1);
}
}
// 遍历哈希表,找出多数元素
for (let [key, value] of countMap) {
if (value > n / 2) {
return key;
}
}
};
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),需要遍历数组一次来统计元素出现次数,再遍历哈希表一次来找出多数元素。
- 空间复杂度: O ( n ) O(n) O(n),最坏情况下,哈希表需要存储数组中的所有元素。
方法二:摩尔投票法
思路
摩尔投票法的核心思想是在每一轮投票过程中,从数组中找出一对不同的元素并删除,直到数组为空或只有一种元素。由于多数元素的出现次数大于 ⌊ n/2 ⌋
,所以最后剩下的元素一定是多数元素。
Java 代码实现
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == candidate) {
count++;
} else {
count--;
if (count == 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
}
}
JavaScript 代码实现
var majorityElement = function(nums) {
let candidate = nums[0];
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (nums[i] === candidate) {
count++;
} else {
count--;
if (count === 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
};
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),只需要遍历数组一次。
- 空间复杂度: O ( 1 ) O(1) O(1),只使用了常数级的额外空间。
三、总结
本题主要介绍了两种解决多数元素问题的方法。哈希表法简单直观,但空间复杂度较高;摩尔投票法是一种更优的解法,满足进阶要求的时间和空间复杂度。对于新手来说,理解摩尔投票法的思想是重点。希望这篇博客能帮助大家更好地掌握本题的解题思路和方法。
寻找数组中的多数元素:Boys and Girls问题
目录
- 问题描述
- 输入输出格式
- 问题分析
- 解决方案
- Java实现
- JavaScript实现
- 总结
1. 问题描述
给定一个非空数组nums
,其中包含n
个元素,我们需要找出其中的多数元素。多数元素是指在数组中出现次数大于⌊n/2⌋
的元素。题目保证数组中总是存在这样的多数元素。
2. 输入输出格式
- 输入:一个整数数组
nums
。 - 输出:数组中的多数元素。
3. 问题分析
这个问题可以通过排序或者哈希表来解决,但是这些方法的时间复杂度和空间复杂度都不是最优的。题目要求我们设计一个时间复杂度为O(n)、空间复杂度为O(1)的算法。我们可以使用Boyer-Moore投票算法来解决这个问题。
4. 解决方案
4.1 Java实现
public class Solution {
public int majorityElement(int[] nums) {
int count = 0;
Integer candidate = null;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
4.2 JavaScript实现
var majorityElement = function(nums) {
let count = 0;
let candidate = null;
for (let num of nums) {
if (count === 0) {
candidate = num;
}
count += (num === candidate) ? 1 : -1;
}
return candidate;
};
5. 总结
本文介绍了如何寻找数组中的多数元素。通过Boyer-Moore投票算法,我们可以在O(n)的时间复杂度和O(1)的空间复杂度内解决这个问题。这种方法不仅适用于这个问题,还可以推广到其他需要寻找多数元素的场景。
169. 多数元素问题解析:Java与JavaScript实现
目录
- 引言
- 问题分析
- Java实现
- 算法思路
- 代码示例
- JavaScript实现
- 算法思路
- 代码示例
- 总结
- 进阶讨论
1. 引言
“169. 多数元素”是一个简单难度的数组问题,要求我们找出数组中出现次数大于数组长度一半的元素。由于多数元素的定义,我们可以使用高效的算法来解决这个问题。
2. 问题分析
为了解决这个问题,我们可以使用以下策略:
- Boyer-Moore 投票算法:这是一个线性时间复杂度且常数空间复杂度的算法,用于找出数组中的多数元素。
- 遍历数组,维护一个候选元素和一个计数器。
- 对于每个元素,如果计数器为0,则将当前元素作为候选元素。
- 如果当前元素与候选元素相同,则计数器加1;如果不同,则计数器减1。
3. Java实现
算法思路
- 初始化一个变量
candidate
来存储候选元素,以及一个变量count
来存储计数器。 - 遍历数组,根据上述投票算法的逻辑更新
candidate
和count
。 - 最后,
candidate
即为多数元素。
代码示例
public int majorityElement(int[] nums) {
int candidate = 0, count = 0;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
4. JavaScript实现
算法思路
JavaScript的实现与Java类似,主要区别在于语法。
代码示例
function majorityElement(nums) {
let candidate = 0, count = 0;
for (let num of nums) {
if (count === 0) {
candidate = num;
}
count += (num === candidate) ? 1 : -1;
}
return candidate;
}
5. 总结
本文介绍了如何使用Boyer-Moore投票算法解决“多数元素”问题。这种方法的时间复杂度为O(n),空间复杂度为O(1),其中n是数组的长度。
6. 进阶讨论
- 如果数组中存在多个多数元素,算法需要做哪些调整?
- 这个问题可以扩展到多维数组或矩阵中吗?例如,如何找到多维数组中的多数元素?
- 除了投票算法,还有其他方法可以解决这个问题吗?
多数元素问题:摩尔投票法与哈希表解法详解
目录
问题描述
给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数大于 ⌊n/2⌋ 的元素。题目保证数组非空且总是存在多数元素。
示例1:
输入:nums = [3,2,3]
输出:3
示例2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
问题分析
- 多数元素定义:出现次数超过数组长度一半的元素
- 题目保证:数组非空且一定存在多数元素
- 挑战:如何在O(n)时间复杂度和O(1)空间复杂度下解决问题
解题思路
方法一:哈希表统计
- 使用哈希表记录每个元素的出现次数
- 遍历哈希表找到出现次数超过n/2的元素
方法二:摩尔投票法
- 维护一个候选元素和计数器
- 遍历数组,相同元素计数器+1,不同元素计数器-1
- 计数器为0时更换候选元素
- 最终剩下的候选元素就是多数元素
哈希表解法
Java实现
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
if (map.get(num) > nums.length / 2) {
return num;
}
}
return -1; // 题目保证有解,这里不会执行
}
}
JavaScript实现
var majorityElement = function(nums) {
const map = new Map();
for (const num of nums) {
map.set(num, (map.get(num) || 0) + 1);
if (map.get(num) > nums.length / 2) {
return num;
}
}
return -1; // 题目保证有解,这里不会执行
};
摩尔投票法
Java实现
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (count == 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] == candidate) {
count++;
} else {
count--;
}
}
return candidate;
}
}
JavaScript实现
var majorityElement = function(nums) {
let candidate = nums[0];
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (count === 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] === candidate) {
count++;
} else {
count--;
}
}
return candidate;
};
算法复杂度分析
哈希表解法
- 时间复杂度:O(n),需要遍历数组一次
- 空间复杂度:O(n),需要存储元素出现次数
摩尔投票法
- 时间复杂度:O(n),只需要遍历数组一次
- 空间复杂度:O(1),只使用了常数级别的额外空间
常见错误
-
边界条件处理不当:
// 错误:没有处理空数组的情况(题目保证非空) if (nums.length === 0) return -1;
-
摩尔投票法初始化错误:
// 错误:初始候选元素和计数器设置错误 int candidate = 0; // 应该用数组第一个元素 int count = 0; // 应该初始化为1
-
哈希表解法空间浪费:
// 错误:没有提前返回,继续统计所有元素 for (const num of nums) { map.set(num, (map.get(num) || 0) + 1); } // 然后遍历map找多数元素,浪费了空间和时间
实际应用
- 选举系统:快速找出得票过半的候选人
- 数据分析:识别数据集中占主导地位的元素
- 系统监控:检测频繁出现的异常事件
- 基因组学:寻找DNA序列中的主要模式
总结
- 哈希表解法直观易懂,但需要额外空间
- 摩尔投票法是解决多数元素问题的最优解
- 算法选择取决于空间复杂度要求
- 摩尔投票法可以扩展到找出出现次数超过n/k的元素
掌握多数元素问题的解法不仅能够解决本题,还能帮助理解投票算法在数据处理中的应用。建议先实现哈希表解法,再学习摩尔投票法,并通过手动模拟算法过程来加深理解。
多数元素问题:摩尔投票算法详解
目录
问题描述
给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数大于 ⌊n/2⌋ 的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例1:
输入:nums = [3,2,3]
输出:3
示例2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
基本解法
1. 哈希表统计法
- 使用哈希表统计每个元素的出现次数
- 时间复杂度:O(n)
- 空间复杂度:O(n)
2. 排序法
- 将数组排序后取中间元素
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)或O(n)(取决于排序实现)
摩尔投票算法
核心思想
- 候选人(candidate)初始化为nums[0],票数count初始化为1
- 遍历数组:
- 当前元素==候选人:count++
- 当前元素!=候选人:count–
- 当count==0时:更换候选人为当前元素,count=1
- 最后的候选人就是多数元素
为什么有效?
多数元素的数量超过其他所有元素数量之和,所以"一对一对消"后剩下的必然是多数元素。
JavaScript实现
var majorityElement = function(nums) {
let candidate = nums[0];
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (count === 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] === candidate) {
count++;
} else {
count--;
}
}
return candidate;
};
Java实现
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (count == 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] == candidate) {
count++;
} else {
count--;
}
}
return candidate;
}
}
算法正确性证明
假设:
- 多数元素为x,出现次数为m(m > n/2)
- 其他元素总数为n-m
最坏情况下:
- 每次x与其他元素相消
- 最终剩余x的数量:m - (n - m) = 2m - n > 0
因此算法一定能找出多数元素。
复杂度分析
-
时间复杂度:O(n)
- 只需一次遍历数组
-
空间复杂度:O(1)
- 只使用了常数个额外变量
实际应用场景
- 选举系统:快速找出得票过半的候选人
- 数据分析:识别主要模式或趋势
- 系统监控:检测高频事件
- 压缩算法:寻找重复模式
- 容错系统:确定多数一致的状态
常见问题解答
Q1:如果不存在多数元素,算法会返回什么?
A1:题目假设一定存在多数元素。如果可能不存在,需要验证候选人的实际出现次数。
Q2:为什么摩尔投票算法空间复杂度是O(1)?
A2:因为它只维护了候选人和计数器两个变量,不随输入规模增长。
Q3:如何处理多个候选人的情况?
A3:摩尔投票算法可以扩展为找出出现次数超过n/k的元素,需要维护k-1个候选人。
总结与扩展
核心思想
- 对消思想:多数元素与其他元素相消后必然剩余
- 一次遍历:高效解决问题
- 常数空间:最优空间复杂度
扩展思考
- 多数元素II:找出所有出现次数超过n/3的元素
- 分布式计算:如何在分布式环境下找出多数元素
- 流式数据:处理无法全部存储的数据流
- 概率算法:近似计算多数元素
最佳实践建议
- 理解算法背后的数学原理
- 处理边界情况(如单元素数组)
- 考虑扩展问题的解法
- 比较不同解法的优缺点
掌握摩尔投票算法不仅能解决多数元素问题,还能培养对高效算法的设计思维,是算法学习中的重要里程碑。
LeetCode 169. 多数元素:摩尔投票法详解(Java/JS实现)
目录
- 题目描述
- 示例分析
- 解题思路
- 摩尔投票法详解
- Java代码实现
- JavaScript代码实现
- 复杂度分析
- 总结与扩展
1. 题目描述
给定一个大小为 n
的整数数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊n/2⌋
的元素。题目保证数组非空且一定存在多数元素。
示例:
输入:nums = [2,2,1,1,1,2,2]
输出:2
进阶要求:时间复杂度为 O(n),空间复杂度为 O(1)。
2. 示例分析
假设数组为 [2,2,1,1,1,2,2]
,其中元素 2
出现 4 次,超过半数(7/2 = 3.5),因此返回 2
。
3. 解题思路
方法一:哈希表统计(O(n)时间,O(n)空间)
- 遍历数组,用哈希表记录每个元素的出现次数。
- 遍历哈希表,找到次数超过
n/2
的元素。
方法二:排序法(O(n log n)时间,O(1)空间)
- 将数组排序后,中间位置的元素一定是多数元素。
方法三:摩尔投票法(O(n)时间,O(1)空间,最优解)
- 遍历数组,维护一个候选人和计数器。
- 遇到相同元素,计数器加1;否则计数器减1。
- 当计数器为0时,更换候选人为当前元素。
- 最终剩下的候选人即为多数元素。
4. 摩尔投票法详解
核心思想
多数元素的数量比其他所有元素的总和还要多,因此可以通过“抵消”的方式筛选出多数元素。
执行步骤
- 初始化:候选人
candidate
为nums[0]
,计数器count
为 1。 - 遍历数组:
- 如果当前元素等于候选人,
count++
。 - 否则,
count--
。 - 若
count == 0
,更换候选人为当前元素,并重置count = 1
。
- 如果当前元素等于候选人,
- 返回候选人。
正确性验证
- 假设多数元素为
x
,其出现次数为k
,且k > n/2
。 - 每次不同元素与
x
抵消后,剩余的x
仍然多于其他元素总和。
5. Java代码实现
public class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (count == 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] == candidate) {
count++;
} else {
count--;
}
}
return candidate;
}
}
代码解析
- 初始化:从第一个元素开始。
- 遍历:通过抵消逻辑更新候选人和计数器。
- 返回值:最终候选人即为多数元素。
6. JavaScript代码实现
var majorityElement = function(nums) {
let candidate = nums[0];
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (count === 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] === candidate) {
count++;
} else {
count--;
}
}
return candidate;
};
代码解析
- 逻辑与Java一致,语法调整为JavaScript形式。
7. 复杂度分析
- 时间复杂度:O(n),只需一次遍历。
- 空间复杂度:O(1),仅用两个变量存储候选人和计数器。
8. 总结与扩展
核心总结
- 摩尔投票法:通过抵消思想高效找到多数元素,满足时间和空间的最优要求。
- 适用场景:数组中存在出现次数超过半数的元素。
扩展问题
- 求众数 II(LeetCode 229):找出所有出现次数超过
n/3
的元素。 - 验证多数元素:如果题目不保证存在多数元素,需二次遍历验证候选人是否符合条件。
掌握摩尔投票法的思想,能高效解决类似问题!
力扣169. 多数元素题解:Java与JavaScript双解法详解
目录
- 问题描述
- 解题思路
- 方法一:哈希表统计(O(n)空间)
- 方法二:排序取中值(O(n logn)时间)
- 方法三:摩尔投票法(O(n)时间,O(1)空间)
- Java代码实现
- JavaScript代码实现
- 复杂度分析
- 关键点解析
- 示例验证
- 参考资料
1. 问题描述
给定一个大小为 n
的整数数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 严格大于 ⌊n/2⌋
的元素。题目保证数组非空且一定存在多数元素。
示例:
输入:nums = [2,2,1,1,1,2,2]
输出:2
2. 解题思路
方法一:哈希表统计
遍历数组,用哈希表记录每个元素的出现次数,当某个元素的次数超过 n/2
时返回。时间复杂度 O(n),空间复杂度 O(n)。
方法二:排序取中值
将数组排序,多数元素必定位于中间位置 nums[n/2]
。时间复杂度 O(n logn),空间复杂度 O(1)(原地排序)。
方法三:摩尔投票法
- 初始化:候选人
candidate
设为nums
,计数器count
初始为1
。 - 遍历数组:
- 若当前元素与
candidate
相同,count++
。 - 否则
count--
,若count
减至0
,则更换候选人为当前元素并重置count
为1
。
- 若当前元素与
- 返回结果:最终的
candidate
即为多数元素。时间复杂度 O(n),空间复杂度 O(1)。
3. Java代码实现
摩尔投票法实现
public class Solution {
public int majorityElement(int[] nums) {
int candidate = nums;
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == candidate) {
count++;
} else {
count--;
if (count == 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
}
}
排序法实现
import java.util.Arrays;
public class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
4. JavaScript代码实现
摩尔投票法实现
var majorityElement = function(nums) {
let candidate = nums;
let count = 1;
for (let i = 1; i < nums.length; i++) {
if (nums[i] === candidate) {
count++;
} else {
count--;
if (count === 0) {
candidate = nums[i];
count = 1;
}
}
}
return candidate;
};
排序法实现
var majorityElement = function(nums) {
nums.sort((a, b) => a - b);
return nums[Math.floor(nums.length / 2)];
};
5. 复杂度分析
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
哈希表统计 | O(n) | O(n) |
排序取中值 | O(n logn) | O(1) |
摩尔投票法 | O(n) | O(1) |
6. 关键点解析
- 摩尔投票法的核心:多数元素的数量比其他所有元素总和多,因此通过抵消后必然剩余。
- 排序法的正确性:多数元素超过半数,排序后中间位置必为该元素。
- 边界条件处理:即使数组全为同一元素,算法仍正确。
7. 示例验证
以 nums = [2,2,1,1,1,2,2]
为例:
- 摩尔投票法步骤:
candidate=2, count=1
→ 遍历第二个2,count=2
。- 遇到1,
count=1
→ 再遇到1,count=0
→ 更换candidate=1
,count=1
。 - 再遇到1,
count=2
→ 遇到2,count=1
→ 最后遇到2,count=0
→ 更换candidate=2
,最终返回2。
8. 参考资料
CSDN博客:投票法解析
博客园:三种解法对比
腾讯云开发者社区:摩尔投票法实现
力扣官方题解