数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
假设数组非空,并且一定存在满足条件的数字。
思考题:
- 假设要求只能使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢?
数据范围
数组长度 [1,1000]。
样例
输入:[1,2,1,1,3]
输出:1
思想:
思想:基于二分的减治法
1、要查找的键值一定是众数, 即,任意两个数不相同的数 成对删除,最后留下的数一定是众数
2、真正的众数的数量一定大于所有其他数的数量,当众数候选者的数量和其他数的数量相等时,就排除这堆数,不影响后续挑选真正的众数
3、基于二分思想,虽然通篇没有二分的代码,但是思想上与二分法有很多相似之处
传统的二分法,是通过在有序序列中将键值与mid比较大小来排除一半元素的。
而本题中,先设定一个众数候选者,通过判断当前值与众数候选者是否相等,来依次两两排除数字,再设定一个新的众数候选者,最后留下真正的众数候选者
代码实现:
例:1 1 2 3 1 众数是1
1、 数组中的数,我们可以假设初始为众数候选者val为num[0],即1
2、开始遍历 第一个数(num[1]==1) x ,如果等于val ,此时计数器 + 1, 即cont = 1
3、如果下一个数(num[2]==1)还是x, 等于val, 那么计数器 + 1 ,即 cont = 2,val还是x
4、假设第三个数(num[3]==2)为y, 不等于val,那么cont此时等于2 - 1 = 1,但 val还是x5、如果第四个数(num[4]==3)是z, 不等于val, 那么cont此时等于1 - 1 = 0,下面val要开始重新赋值了
(count +2再-2,就实现了前4个数的排除操作)
6、此时符合if(cout==0) 这个条件,那么val变成了z的值(num[4]==3),count重新赋值为17、下一步num[5]为1,不等于val(3),count--,为0
if(count==0)判断为真,val==num[5],结束遍历,return结果为val==num[5]==1,为真正的众数
8、就是这样通过计数来确定val的值,最后肯定留下了重复的那个数,因为他出现的次数过一半
代码:
acwing邓泽军大佬的代码,思想太高级了吧%%%
class Solution
{
public:
int moreThanHalfNum_Solution(vector<int>& nums)
{
int count = 1, val = nums[0];//将数组的第一个元素初始化为众数候选者
for (int i = 1; i < nums.size(); i++) //遍历
{
//count实际记录的是,前缀中,众数候选者与其他元素个数的差值
//当count==0时,众数候选者就一定不是众数了,舍弃这个数
if (nums[i] == val)
count++;
else
count--;
if (count == 0)
{
val = nums[i]; //假设为众数候选者
count = 1;
}
}
return val;//众数候选者终于脱颖而出,成为了真正的众数
}
};