超级水王数:
描述:
在一个数组中,如果存在一个数,在该数组中出现的次数大于数组长度的一半,那么这个数就是超级水王数
暴力解决:
使用HashMap来存储每个元素出现的次数,最后判断出现次数大于数组长度一半的元素并返回。
代码如下:
public static void Hash(int[] arr){
if(arr == null || arr.length == 0){
System.out.println("数组为空,没有水王数");
}
int n = arr.length;
HashMap<Integer,Integer> map = new HashMap<>();
for(int i : arr){
if(map.containsKey(i)){
map.put(i,(map.get(i) + 1));
}else{
map.put(i, 1);
}
}
for(Map.Entry<Integer,Integer> record : map.entrySet()){
if(record.getValue() > (n >> 1)){
System.out.println("水王数:" + record.getKey());
}
}
}
但使用HashMap的空间复杂度是O(n)。可不可以得到一个空间复杂度为O(1)的算法呢?
答案是有的。
思路:
从数组第一个元素开始,依次删掉两个不同的数,最后如果有水王数的话,它会剩下来(反过来不成立,也就是剩下来的数不一定是水王数)。
所以我们需要第二次遍历数组,来判断剩下的数的数量超过数组的一半没。
如何实现呢?
将问题转换为以下两步:
1、依次删除数组中两个不同的数
2、 判断是否有数剩余。如果没有数剩余,则不存在水王;如果有数剩余,我们再遍历一次数组,计算该数的真实的出现次数与数组长度的一半进行比较判断是否为水王数
我们需要定义两个变量:变量1:king,来决定当前的水王数候选者。变量2:hp。水王数的生命值,也就是水王数的数量。
如果生命值为0,那么遍历的当前的元素就为水王数。如果生命值不为0,并且当前的元素和水王数候选者并不相同,该元素就会和水王数PK,使HP减一。如果当前的元素和水王数相同,就会增强水王数的实力,使HP+1。
遍历完数组后,如果HP不为0,就再进行一次遍历。统计该水王数候选者在数组中出现的次数,如果总次数大于数组长度的一半,那么就返回该水王数。
否则返回-1.
代码实现:
//候选者法:
public static void WaterKing(int[] arr){
if(arr == null || arr.length == 0){
System.out.println("数组为空,没有水王数");
}
int king = 0; //水王数候选者
int HP = 0; //代表生命值,每当数组中有相同的数时,生命值加一
for(int num : arr){ //遍历数组
if(HP == 0){ //如果生命值为0,候选者为当期的数
king = num;
HP = 1; //生命值为1
}else if(num != king){ //如果有候选者,且当前值和候选者的值不一样,生命值减一
HP--;
}else{ //如果遇到了相同的数,生命值加一
HP++;
}
}
if(HP == 0){ //如果生命值为0,则表示没有水王数
System.out.println("没有水王数");
}
//再次遍历
int count = 0;
int n = arr.length;;
for(int i : arr){
if(i == king){
count++;
}
}
if(count > (n >> 1)){
System.out.println("水王数为:" + king);
}else{
System.out.println("没有水王数");
}
}