数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
基于快排划分思想的时间复杂度为O(n)的解法
如果一个数字出现的次数超过一半,那么如果我们将数组排序后位于中间的就是那个出现次数超过一半的数字。 我们使用快速排序中划分的思想,在数组中随机选择一个数字,将小于该数的数放在它左边,大于该数的数字放在她右边。如果划分完毕后该数字的下标刚刚好为n/2,那么这个数字就是我们要找的结果;如果该数的下标大于n/2,那么结果数字在它的左边,否则在右边。我们依旧可以用这种方法进行划分,直至找到结果。使用递归实现这一解法:
private static int MoreThanHalfCountsNum(int arr[]){
if(arr == null){
System.out.println("Array is not invalid.");
return -1;
}
int left = 0;
int middle = arr.length>>1;
int right = arr.length-1;
int index = Partition(arr, left, right);
while(index!=middle){
if(index>middle){//大于n/2,结果在左边
index = Partition(arr, left, index-1);
}else{
index = Partition(arr, index+1, right);
}
}
int result = arr[middle];
//会出现数组中出现频率最高的数的出现次数都无法超过数组长度的一半的情况
//比如:{2,1,3,2,2,2,5,4,2,-1,-1,-1,-1,-1,-1,-1}
//出现次数最多的数字为-1,但是仍旧不及数组长度的一半,所有得出的result为2,我们需要再次检验
if(!isMoreThanHalfArrayLen(arr,result)){
System.out.println("不存在出现次数超过数组长度一半的数字!");
result=0;
}
return result;
}
//检查找到的结果是不是真的出现次数超过长度的一半
private static boolean isMoreThanHalfArrayLen(int[] arr, int result) {
int times=0;
for(int i=0;i<arr.length;i++){
if(arr[i]==result){
times++;
}
}
if(times*2<=arr.length){
return false;
}
return true;
}
//划分
private static int Partition(int arr[],int left,int right){
int x = arr[left];//将最左边的值作为主元
int i = left;
for(int j=left+1;j<=right;j++){
if(arr[j]<x){
i++;
swap(arr,i,j);
}
}
swap(arr,left,i);
return i;//返回划分位置
}
//交换
private static void swap(int arr[],int i,int j){
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
根据数组特点实现时间复杂度为O(n)的解法
数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现次数的和还要多。 我们遍历数组,同时保存两个值:一个存放数组中的数字,一个存放次数。当我们遍历到下一个数字时,如果下一个数字和保存的数字相同,次数加1;如果不同,次数减1;如果次数为0,我们保存下一个数字,并把次数该为1.由于我们要找的数字出现的次数比其他数字出现的次数都多,那么要找的数字肯定是最后一次把次数设为1的那个数字。实现代码:
private static int MoreThanHalfCountsNum(int[] arr) {
if(arr == null){
System.out.println("Array is not invalid.");
return -1;
}
int num=arr[0];
int counts=1;
for(int i=0;i<arr.length-1;i++){
if(num==arr[i+1]){
counts++;
}else{
counts--;
}
if(counts==0){
num = arr[i+1];
counts=1;
}
}
int result = num;
if(!isMoreThanHalfArrayLen(arr,result)){
System.out.println("不存在出现次数超过数组长度一半的数字!");
result=0;
}
return result;
}
这种方法依旧要进行检查是否真的出现次数超过长度的一半。
第一种解法改变了数组的内容,所以我们在面试过程中要询问是否可以更改数组内容。