题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:
三种解法:
对数组排序,则位于数组中间的数字可能是超过一半的数,对它进行判断即可。
数组中有一个数字出现的次数超过数组长度的一半,即这个数字出现的次数比其他所有数字出现的次数的和还要多。遍历数组的时候保存两个值:数组中元素的值、次数。在遍历下一个数字时,如果下一个数字和之前保存的数字相同,则次数加1;如果下一个数字和之前保存的数字不相同,则次数减1。如果次数为0,则保存下一个数字,并把次数设为1。由于要找的数字出现的次数比其他所有数字出现的次数之和还要多,则最后一次把次数设为1时对应的数字就是要找的数字。然后再进行判断是不是超过了数组的一半长度。
基于快速排序思想的解法。
①排序法:一个数字在数组中出现次数超过了一半,则排序后,位于数组中间的数字一定就是该出现次数超过了长度一半的数字(可以用反证法证明),也即是说,这个数字就是统计学上的中位数。最容易想到的办法是用快速排序对数组排序号后,直接取出中间的那个数字,这样的时间复杂度为O(nlogn),空间复杂度为O(1)。
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
Arrays.sort(array);
int result = array[array.length/2];
if(!checkMoreThanHalf(array,result))
return 0;
return result;
}
public 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;
}
}
②变量标识法:由于该数字的出现次数比所有其他数字出现次数的和还要多,因此可以考虑在遍历数组时保存两个值:一个是数组中的一个数字,一个是次数,。当遍历到下一个数字时,如果下一个数字与之前保存的数字相同,则次数加1,如果不同,则次数减1,如果次数为0,则需要保存下一个数字,并把次数设定为1。由于我们要找的数字出现的次数比其他所有数字的出现次数之和还要大,则要找的数字肯定是最后一次把次数设为1时对应的数字。该方法的时间复杂度为O(n),空间复杂度为O(1)。
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int result = array[0];
int count = 1;
for(int i=1;i<array.length;i++){
if(array[i] ==result)
count++;
else if(count==0){
result = array[i];
count =1;
}else{
count--;
}
}
if(!checkMoreThanHalf(array,result))
return 0;
return result;
}
public 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;
}
}
③基于快速排序思想的方法:数组中有一个数字出现的次数超过了数组长度的一半。如果把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一半的数字。也就是说,这个数字就是统计学上的中位数,即长度为 n 的数组中第 n/2 大的数字。
这种算法是受快速排序算法的启发。在随机快速排序算法中,我们先在数组中随机选择一个数字,然后调整数组中数字的顺序, 使得比选中的数字小数字都排在它的左边,比选中的数字大的数字都排在它的右边。如果这个选中的数字的下标刚好是 n/2,那么这个数字就是数组的中位数。如果它的下标大于 n/2 ,那么中位数应该位于它的左边,我们可以接着在它的左边部分的数组中查找。如果它的下标小于 n/2,那么中位数应该位于它的右边,我们可以接着在它的右边部分的数组中查找。这是一个典型的递归过程。
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length==0)
return 0;
int mid = array.length/2;
int low = 0;
int high = array.length-1;
int index = partition(array,low,high);
while(index!=mid){
if(index>mid){
high = index-1;
index = partition(array,low,high);
}
if(index<mid){
low = index+1;
index = partition(array,low,high);
}
}
boolean flag = checkMoreThanHalf(array,index);
if(flag)
return array[index];
else
return 0;
}
public int partition(int[] arr, int start, int end){
int left = start;
int right = end;
int base = arr[start];
while(left<right){
while(left<right && arr[right]>=base )
right--;
if(left<right){
arr[left++]=arr[right];
}
while(left<right && arr[left]<=base )
left++;
if(left<right){
arr[right--]=arr[left];
}
}
arr[left] = base;
return left;
}
public static boolean checkMoreThanHalf(int[] array,int result){
boolean flag = false;
int number = 0;
for(int i = 0;i<array.length;i++){
if(array[i]==array[result])
number++;
else
continue;
}
if(number*2>array.length)
flag = true;
return flag;
}
}