题目
春节期间小明使用微信收到很多个红包,非常开心。在查看领取红包记录时发现,某个红包金额出现的次数超过了红包总数的一半。请帮小明找到该红包金额。写出具体算法思路和代码实现,要求算法尽可能高效。
给定一个红包的金额数组gifts及它的大小n,请返回所求红包的金额。
若没有金额超过总数的一半,返回0。
数据范围:1 <= n <= 1000,红包金额满足1 <= gifti <= 100000。
示例1
输入 [1,2,3,2,2],5
输出 2
示例2
输入 [1,1,2,2,3,3],6
输出 0
思路
↑最简单思路:
分别算出每个红包金额出现次数,再找出出现次数过半的金额。
↑进阶思路:
考虑到时间/空间复杂度,是否可以优化为时间复杂度O(n),空间复杂度O(1)呢?那就是只在原数组遍历一次而不申请其他数组。
挖掘题目所给的重要信息:红包金额出现次数超过“一半”(而不是1/3或1/4),so求解方法与“一半”有关。是否只用求出该红包金额,而不用算出该红包出现次数呢~可以!在求解时次数可以加以利用并改变。
so~遍历原数组,只要是两个不同的金额,就直接抵消掉,最后剩哪个就是哪个过半。
具体解法:计数+验证
- 让key最初暂为数组第一个元素,而后在数组遍历中不断更新,用来保存最终的候选值。
- count最初是1,用来计数。
- 遍历数组(从数组第二个元素开始遍历)
- 若当前所遍历的数组元素值与当前key值相等,则count+1。
- 若当前所遍历的数组元素值与当前key值不等,则count-1。
- 若count为0,说明互相抵消完,前一个候选的值不是超过一半的数,则更新候选值,改变key值为此时遍历的数组下标所对应的数组元素值,还原count为1。
- 判断key出现的次数是否大于数组长度的一半,若大于,则return key;否则,return 0。
代码
public class WeChatRedEnvelope {
public int weChatRedEnvelope(int[] gifts, int n) {
//遍历原数组,只要是两个不同的金额,就直接抵消掉,最后剩哪个就是哪个过半
int key = gifts[0];
int count = 1;
for (int i = 1; i < n; i++) {
if(gifts[i] == key) {
count++;
} else {
count--;
}
if(count == 0) {
key = gifts[i];
count = 1;
}
}
//判断key出现的次数是否大于数组长度的一半
int num = 0;
for (int i = 0; i < n; i++) {
if(gifts[i] == key) {
num++;
}
}
//此时num为key重复的次数
if(num > (n / 2)) {
return key;
} else {
return 0;
}
}
}