主要元素 17.10
问题描述
数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1。
示例 1:
输入:[1,2,5,9,5,9,5,5,5]
输出:5
示例 2:
输入:[3,2]
输出:-1
求解时间、空间消耗
一、摩尔投票法(Boyer–Moore majority vote algorithm)
1、算法概述
形象化理解:在有众多候选人的场景下,选出拥有选票数超过总票数一半的那个人
- 数组中的每个元素都代表着一张候选人的选票
- 数组中值不相同的两元素会相互进行"抵消"
- 如果数组中某个元素值出现的次数比其他元素出现的次数加起来都要多的话,这个元素值代表的候选人将赢得选举,当两两抵消结束,最后剩下的那个元素值代表(可能会有多个)人数多的一方
- 但如果所有元素值(候选人的选票)都不是主要元素(即占比都未超过一半),那后剩下的这个元素就并不能代表人数多的一方
- 因此,当某个元素留到最后时,需要统计它是否占比过半,来判断投票结果是否有效
2、步骤
-
抵消阶段:两个不同的元素进行抵消,当剩下的元素值都相同时,结束
-
计数阶段:对最后剩下的元素值进行统计,统计该元素在数组中占比是否超过一半,进而判断投票结果是否有效
(1)设票数 vote(用于计数) 初始值为 0, 候选人 初始值为 0
(2)遍历数组,先判断 vote 是否为 0,如果 vote 为 0,就将 candidate 更新为当前遍历到的元素值,并将 vote 值更新为 1
(4)若 vote 不为 0,判断 candidate 和数组中当前遍历到的元素值是否相等,若相同,则将 vote 值 +1,否则将 vote 值 -1
(5)最后 candidate 占比可能超过半数,需要统计来决定是否有效 -
Java:摩尔投票法(T(n) = O(n),S(n) = O(1))
class Solution {
public int majorityElement(int[] nums) {
//数组为空,直接返回-1(题中未说数组不为空,判断下更严谨,提容错率)
if(nums.length == 0){
return -1;
}
//数组长度为1,直接返回元素值
if(nums.length == 1){
return nums[0];
}
int vote = 0;
int candidate = 0;
// 成对抵消阶段,nums看作候选人
for(int num : nums){
if(vote == 0){
candidate = num;
vote = 1;
}else{
if(candidate == num){
vote++;
}else{
vote--;
}
}
}
// 计数阶段,判断cand的vote数是否超过总票数的一半
vote = 0;
for(int num:nums){
if(candidate == num){
vote++;
}
}
if(vote <= nums.length / 2){
candidate = -1;
}
return candidate;
}
}
二、Arrays.sort()排序
- Array.sort()方法,默认按照ASC排序,即可以遍历的时候对元素进行两两判断,统计相同的元素出现的次数
- 时间复杂度:T(n) = O(n)
- 对Array.sort()原理感兴趣的可以自己在进行深入了解,也可以看下这篇:浅谈Arrays.sort()原理
class Solution {
public int majorityElement(int[] nums) {
//数组为空,直接返回-1(题中未说数组不为空,判断下更严谨,提容错率)
if(nums.length == 0){
return -1;
}
//数组长度为1,直接返回元素值
if(nums.length == 1){
return nums[0];
}
//用Array.sort 排序,将重复的数据放到一起
//相邻数据相同的,标识记录+1,判断标识记录是否超过数组长度一半
Arrays.sort(nums);
int numFlag = 1;
for(int i = 1;i < nums.length;i++){
if(nums[i-1] == nums[i]){
numFlag++;
}else{
numFlag = 1;
}
if(numFlag > nums.length/2){
return nums[i-1];
}
}
return -1;
}
}
三、HashMap(map.getOrDefault()方法)
1、步骤
- new map
- 遍历数组,用map.getOrDefault()将nums中每个元素出现的次数进行统计,并向map中添加元素(元素值做key,次数做value)
- 遍历map,若某个数字的出现次数大于nums.length / 2,
- 返回该元素,否则返回-1
- 时间复杂度:T(n) = O(n)
2、涉及到的知识点 map.getOrDefault()
- map.getOrDefault(Object key,V defaultValue) 方法的作用是:
- 当Map集合中存在这个key时,就使用这个key值,返回这个key值在集合中对应的value
- 如果这个key不存在,就使用方法中的默认值defaultValue
- map.getOrDefault() 举例
HashMap<String, String> map = new HashMap<>();
map.put("A", "AA");
map.put("B", "BB");
map.put("C", "CC");
String A = map.getOrDefault("A", "random");
int num = map.getOrDefault("num", 100);
// AA,map中存在A,获得A对应的value
System.out.println(A);
// 100,map中不存在num,使用默认值100
System.out.println(num);
3、Code
class Solution {
public int majorityElement(int[] nums) {
//数组为空,直接返回-1(题中未说数组不为空,判断下更严谨,提容错率)
if(nums.length == 0){
return -1;
}
//数组长度为1,直接返回元素值
if(nums.length == 1){
return nums[0];
}
HashMap<Integer,Integer> map = new HashMap<>();
for(int num:nums){
int count = map.getOrDefault(num,0)+1;
map.put(num,count);
}
for (Map.Entry<Integer,Integer> entry:map.entrySet()){
if(entry.getValue() > nums.length / 2){
return entry.getKey();
}
}
return -1;
}
}
四、暴力解法-双重for循环
- 两个for循环去统计元素值出现的次数,没什么好具体讲的
- 时间开销很大 T(n) = O(n^2)
class Solution {
public int majorityElement(int[] nums) {
//数组为空,直接返回-1(题中未说数组不为空,判断下更严谨,提容错率)
if(nums.length == 0){
return -1;
}
//数组长度为1,直接返回元素值
if(nums.length == 1){
return nums[0];
}
int num = -1;
for(int i = 0;i < nums.length;i++){
int count = 0;
for(int j = 0;j < nums.length;j++){
if(nums[i] == nums[j]){
count++;
}
}
if(count > nums.length / 2){
return nums[i];
}
}
return num;
}
}
- 参考:详细了解摩尔投票法可参见这篇文章:摩尔投票法(Boyer–Moore majority vote algorithm)