题目描述:数组中有一个数出现的次数超过了数组长度的一半,找出这个数。(数组是无序的)。
思路一:先排序。我们如果选择最快的排序算法,时间复杂度是O(log(N))。排序完了之后,一般思路是遍历,然后统计次数,但其实没有必要这么做。因为如果某个数在数组中出现的次数超过了一半,那么在已经排好序的数组索引的n/2处就一定是要找的这个数。那么总的时间复杂度其实就是排序的时间复杂度,O(log(N))。
代码比较简单,不再展示,就是一个排序,然后输出。
思路二:散列表,以空间换取时间。把数组中的数当做散列表中的键,值为该键出现的次数。那么,利用散列表完成统计之后,直接遍历整个散列表,直接输出即可。
遍历一次需要的时间是O(N),而构造散列表需要O(N)的空间开销,而且还要设计散列函数。有没有更好的方法呢?
思路三:每次删除两个不同的数。我们每次删除数组中两个不同的数,不管是不是我们要查找的那个数,那么在剩下的数中,我们要查找的那个数仍然会超过剩余总数的一半。通过不断重复这个过程,最终会找到那个出现次数超过一半的数。这样一算,时间复杂度是O(N),而空间复杂度是O(1)。
那么我们如何能够最高效的删除两个数,完成遍历呢?
方法就是遍历数组的时候保存两个值,一个是temp,用来保存数组中遍历到的某个数,另一个是count,用来保存当前数的出现次数,初始化为1。当遍历到下一个数的时候,可能有如下情况:
1.如果下一个数与之前的temp中保存的数相同 那么count++
2.如果下一个数与之前的temp中保存的数不同 那么count—
3.每当count=0时,用temp保存下一个数,并把count设置为1。
4.直到遍历完所有的数组。
public class MainB {
/**
* 通过不断地删除两个数,找到出现次数超过一半的数
* 假设输入的是有效的数字
* @param args
*/
private static int findOneNumberByDel(int []a,int len){
int temp =0;
int count = 0;//用来计数
for(int i=0;i<a.length;i++){
if(count ==0 ){ //等于0代表前面的 所有数据恰好已经抵消
temp = a[i]; //需要重来选择一个元素
count++;
}else{
if(temp == a[i]){ //碰到的一样的
count++;
}else{ //碰到不一样的
count--;
}
}
}
return temp;
}
public static void main(String[] args) {
int [] a = {0,1,2,1,1};
System.out.println(findOneNumberByDel(a, a.length));
}
}