给定一个长度为N的数组arr,和一个大于1的正整数K,如果有哪些数出现的次数大于N/K,就返回这些数
要求:时间复杂度O(N) 额外空间复杂度O(K)
思考:最多会有k-1个数
具体问题思考:当 k = 4时 哪些数出现的次数 大于 N / 4;
有哪些性质呢?
性质1:
次数大于 N/4的次数最多是3个,反证法:如果多余三个,那么就会出现,总数大于 N,所以假设不存在;
抵消阶段:
1.每次抵消k个不同的的数;如果数组中存在大于 N/K的数,那么一定会被保留下来,
保留下来的不一定是数,次数不一定大于N/K的数;
计数阶段:
1.对于每一个候选结果进行重新计数,判断每个结果是否满足要求;
代码:
private List<Integer> getSuperWaterExtension(int[] arr, int K) {
int N = arr.length;
int M = N / K;
//候选表,最大列表为K-1
Map<Integer, Integer> candis = new HashMap<>();
for (int value : arr) {
if (candis.containsKey(value)) {
candis.put(value, candis.get(value) + 1);
} else {
if (candis.size() == K - 1) {
allCandisMinusOne(candis);
} else {
candis.put(value, 1);
}
}
}
Map<Integer, Integer> reals = getReals(arr, candis);
List<Integer> res = new ArrayList<>();
reals.forEach((key,value) -> {
if(value > M) {
res.add(key);
}
});
return res;
}
/**
* 对候选的数据进行计数
*/
private Map<Integer,Integer> getReals(int[] arr,Map<Integer,Integer> candis){
Map<Integer,Integer> res = new HashMap<>();
for(int num : arr){
if(!candis.containsKey(num)) continue;
res.put(num,res.getOrDefault(num,0) + 1);
}
return res;
}
//所有的候选数减去一
private void allCandisMinusOne(Map<Integer, Integer> candis) {
List<Integer> removes = new ArrayList<>();
for (Integer key : candis.keySet()) {
if (candis.get(key) == 1) {
removes.add(key);
} else {
candis.put(key, candis.get(key) - 1);
}
}
// 删除无效的候选
for (Integer num : removes) {
candis.remove(num);
}
}