问题:数组中出现次数超过一半的数字
描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
注:此处修改了牛客的条件,不一定存在数组中出现次数超过一半的数字。
数据范围:n≤50000,数组中元素的值 :0≤val≤10000
要求:空间复杂度:O(1),时间复杂度 O(n)
解题方法:
1、投票算法
思路:
数组中有n个元素,如果有一个元素出现的次数超过数组长度的一半,那么它的元素个数一定大于其他元素个数的总和,那么该元素的个数减去其他元素的个数一定是大于1的,此时若将数组中元素值不同的数相互抵消(即使每次都是使用次数超过一半的元素与其他元素进行抵消,更别说可能会有其他元素间相互抵消),那么剩下的数一定是元素出现的次数超过数组长度的一半。
步骤:
1、选择输入数组中第一个元素作为候选元素candidate,并设置其出现次数为count=1。
2、遍历数组,当遇到与candidate相同的元素,count+1;遇到不同的元素,count-1。当count为0的时候,选择下一个元素为候选元素,并且置count=1。
3、遍历到数组的最后,剩下的candidate就可能是数组中出现次数超过一半的数字。
4、统计该candidate元素出现的次数,若超过一半则返回candidate;反之,返回0。
为什么还要判断元素出现的次数是否超过一半呢,不是留下的candidate一定是该元素吗?
原因如下:
如果存在某个元素个数超过数组一半的元素,那么candidate一定是该元素,前者是后者的充分条件,但是candidate存在,却不一定存在某个元素个数超过数组一半的元素,这只是充分不必要条件。如果数组为[1,3,6,3,4,3,3],[2,2,5,2,2,3,1,4,2,2],即数组中存在某个元素个数超过数组一半的数,那么剩下的candidate一定是该元素。但是如果现在的数组为[1,4,3,5,3,3],此时得到的candidate为3,但是我们可以看出元素3的个数刚好为数组的一半,并不满足条件,所以我们需要遍历一遍以此来统计元素个数,超过一半才满足。
代码:
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array == null || array.length < 1){
return 0;
}
//第一个候选元素
int candidate = array[0];
int count = 1;
//遍历数组
for (int i = 1; i < array.length; i++) {
//遇到相同元素,个数加1
if (array[i] == candidate) {
count++;
}
//遇到不同元素,个数减1
else {
count--;
}
//当候选元素个数为0时,选取下一个元素为候选元素,计数为1
if (count == 0) {
candidate = array[++i];
count++;
}
}
//统计该元素在数组中出现的次数
int num = 0;
for(int i=0; i < array.length; i++)
if(array[i] == candidate)
num++;
//超过一半则返回该元素,反之为0
return (num > array.length/2)? candidate:0;
}
}
2、哈希表计数法
思路:
首先我们想到的就是遍历一遍数组元素,并且在遍历过程中统计每一个元素出现的次数,然后再判断元素出现的次数是否大于数组长度的一半,如果超过,就返回该元素,如果未超过就继续遍历,直到数组遍历完全,此时还没有元素超过一半就返回0,过程中可以使用哈希表来存储。
代码:
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array == null || array.length < 1){
return 0;
}
//辅助空间,存储元素值及其出现次数
Map<Integer,Integer> mp = new HashMap<Integer,Integer>();
for(int i = 0; i < array.length; i++){
//元素存在map集合中,出现次数+1
if(mp.containsKey(array[i])){
mp.put(array[i],mp.get(array[i])+1);
}
//元素第一次出现,计数为1
else{
mp.put(array[i],1);
}
//判断当前元素出现的次数是否超过数组长度的一半
if(mp.get(array[i]) > array.length / 2){
return array[i];
}
}
return 0;
}
}
时间复杂度:O(n),遍历一遍长度为n的数组
空间复杂度:O(n),将长度为n的数组使用map集合进行存储