【题目描述】数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
【解题思路1】
先来看一种最简单的解法,算法复杂度为O(n*log(n))
//1.对数组进行排序,然后遍历
//2.遍历过程中,记录当前元素的出现次数,若超过数组长度的一半,即返回该值
//3.若遍历到最后也没有发现符合条件的元素,按照题目要求,返回 0
【源码1】
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length == 0){
return 0;
}
Arrays.sort(array);
boolean flag = false;
int mNum=array[0], sum=0, temp=array[0], len=array.length/2;
for(int a: array){
if(a == temp){
sum++;
mNum = a;
}
if(sum > len ){
flag = true;
break;
}
if(a != temp){
sum = 1;
temp = a;
}
}
if(flag == false){
mNum = 0;
}
return mNum;
}
}
a. sum用来记录当前值出现的次数。若出现当前值变化的情况,需要重置sum=1
, 注意此时sum为1,不为0。
b. 设置标志位flag,是针对遍历到最后并没有发现符合条件的值的情况。
【解题思路2】在 “思路1” 的基础上做一个小小的改进。
//1.对数组进行排序
//2.假设存在出现次数超过一半的数字target,则其位于中位数的位置上。此时,无论中位数为单个数值,还是为两个数值的平均数,都为该值。因为若数组长度为偶数,中位数取中间两个值的平均数,此时两个值应该是相同的,都等于最后要求的这个数字target。
//3.求得target后,对数组进行一次遍历,计算target出现的次数,若超过一半则返回该值。否则,不存在符合条件的值,返回0。
【源码2】
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
Arrays.sort(array);
int count=0, len=array.length;
for(int i=0;i<len;i++){
if(array[i]==array[len/2]){
count++;
}
}
if(count>len/2){
return array[len/2];
}else{
return 0;
}
}
}
【解题思路3】最简单直观的考虑。分别计算每个元素出现的次数,取最大值与len/2做比较。时间复杂度为O(n)。
//1.使用hashmap
链接:https://www.nowcoder.com/questionTerminal/e8a1b01a2df14cb2b228b30ee6a92163
来源:牛客网
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i=0;i<array.length;i++){
if(!map.containsKey(array[i])){
map.put(array[i],1);
}else{
int count = map.get(array[i]);
map.put(array[i],++count);
}
}
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
Integer key =(Integer)entry.getKey();
Integer val = (Integer)entry.getValue();
if(val>array.length/2){
return key;
}
}
return 0;
}
a. 使用迭代器iterator,加快遍历速度。
【解题思路4】该思路来自剑指offer的解法二,称为“打擂法”,也叫“守阵地思想”。若数组中有一个数字出现次数超过一半,则它出现的次数超过了其他所有数字出现次数的总和。算法复杂度为O(n)。
//1. 遍历数组,同时维护两个值:一个是数组中的一个元素e,另一个是次数count。
//2. 遍历过程中,若下一个元素和之前保存的e不相同,则次数count-1。若相同,则count+1。
//3. 若出现次数为零的情况,则e需要存储下一个数字,并把次数count=1。
//4. 若存在符合条件的元素,最后一次把次数置为1的数字即要找的目标值。否则,返回0。
//5. 验证最后维护的元素e,出现次数是否超过一半。
【源码4】
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int length=array.length;
if(array==null||length<=0){
return 0;
}
int result=array[0];
int times=1;
for(int i=1;i<length;i++){
if(times==0){
result=array[i];
times=1;
}else{
if(array[i]==result){
times++;
}else{
times--;
}
}
}
times=0;
for(int i=0;i<length;i++){
if(result==array[i]){
times++;
}
}
if(times*2<length){
result=0;
}
return result;
}
}
【解题思路5】该思路来自剑指offer第一种解法。该解法会改变原数组。
//1. 若有一个数字出现次数超过了一半,则排序之后位于数组中间的那个数字就是要求的值。
//2. 在随机快速排序算法中,先在数组中随机选一个数字,调整数组中数字的顺序。使得比选中的数字小的数位于其左边,比选中的数字大的数字位于其右边。
//3. 如果这个选中的数字的下标刚好是n/2,则这个数字就是数组的中位数。如果它的下标比n/2大,则中位数应该位于其左边,继续在左边查找。如果它的下标比n/2小,则中位数应位于其右边,继续在右边查找。
【源码5】
链接:https://www.nowcoder.com/questionTerminal/e8a1b01a2df14cb2b228b30ee6a92163
来源:牛客网
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null||array.length==0)
return 0;
int middle=array.length>>1;
int start=0;
int end=array.length-1;
int index=Partition(array,start,end);
while(index!=middle){
if(index>middle){
end=index-1;
ndex=Partition(array,start,end);
}else{
start=index+1;
index=Partition(array,start,end);
}
}
int result=array[middle];
if(!CheckMoreThanHalf(array,result))
result=0;
return result;
}
public static boolean CheckMoreThanHalf(int array[],int number){
int times=0;
for(int i=0;i<array.length;++i){
if(array[i]==number)
times++;
}
boolean isMoreThanHalf=true;
if(times*2<=array.length){
isMoreThanHalf=false;
}
return isMoreThanHalf;
}
public static int Partition(int array[],int start,int end){
int pivotkey=(int)start+(int)Math.random()*(end-start);
while(start<end){
while(start<end&&array[end]>=array[pivotkey])
end--;
int temp=array[start];
array[start]=array[end];
array[end]=temp;
while(start<end&&array[start]<=array[pivotkey])
start++;
temp=array[start];
array[start]=array[end];
array[end]=temp;
}
return start;
}
}