java实现:剑指 offer--面试题39(数组中出现次数过半的数字)
第五章 优化时间和空间效率
面试题39
题目:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
方法一 切分法
由于数字超过数组长度的一半,故数组排序后,数组中间的数据必然是这个数字。
这样问题就转化为求数组的中位数,快速排序使用的切分算法可以方便的找出中位数,且时间负责度为O(n),找出中位数后,还需要再遍历一次数组,检查该数据是否出现次数超过数组长度的一半。
总结基于切分算法有如下两步骤:
- 切分找出中位数
- 检查中位数出现的次数
public class offer039 {
public int moreThanHalfNum(int[] array){
if(null == array || array.length == 0){
return 0;
}
int mid = find(array,array.length/2);
return checkMoreThanHalf(array,mid);
}
private int checkMoreThanHalf(int[] array, int number) {
int count = 0;
for(int a :array){
if(number == a){
count++;
}
}
return count>array.length/2?number:0;
}
/**
* 选择排名为k的元素,只是部分排序,时间复杂度为O(N)
* @param array
* @param k
* @return
*/
private int find(int[] array, int k) {
int low = 0;
int high= array.length -1;
while(high>low){
int j = partition(array,low,high);
if(j == k) {
return array[k];
}else if(j>k){
high = j-1;
}else {
low= j+1;
}
}
return array[k];
}
/**
* 快速排序的切分方法
* @param array
* @param start
* @param end
* @return
*/
private int partition(int[] array, int start, int end) {
int low = start;
int high = end;
//这里选择开始节点作为哨兵,也可以随机一个点
int soldier = array[start];
while (low<high){
while(low <high && soldier<= array[high]){
high--;
}
array[low] = array[high];
while (low<high && array[low]<=soldier){
low++;
}
array[high] = array[low];
}
array[low] = soldier;
return low;
}
}
方法二 基于数组特性
数组中有个数字出现次数超过一半,故这个数字出现的次数比其他所有数字次数的出现次数之和还要多。
于是可以考虑下面的方法:
使用两个变量:currentValue—表示当前的数字,count—表示当前数字的计数值;
情况1:count=0时,currentVaule = 新遍历的数字,count = 1;
情况2:count>0,新遍历的值和currentVaule 相同,count+1;不同,count-1
遍历完成后,最后currentValue可能就是我们要找的数字,
注意是可能,因为输入数组可能本身就不满足里面有某个数字出现次数超过一半。因此和上面的找中位数方法一样,得到的currentValue还需要进一步检验。
public int moreThanHalfNum(int[] array){
int currentValue =0;
int count=0;
for(int a : array){
if(count == 0){
currentValue = a;
count++;
}else {
if(a == currentValue){
count ++;
}else {
count --;
}
}
}
return checkMoreThanHalf(array,currentValue);
}
private int checkMoreThanHalf(int[] array, int number) {
int count = 0;
for(int a :array){
if(number == a){
count++;
}
}
return count>array.length/2?number:0;
}