题目链接
题目描述与示例
给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
输入描述
保证数组输入非空,且保证有解
示例1
输入:
[1,2,3,2,2,2,5,4,2]
输出:
2
示例2
输入:
[3,3,3,3,2,2,2]
输出:
3
示例3
输入:
[1]
输出:
1
解题思路
题目分析
题目简单易懂,就是看哪个数多,然后输出它就行,再简单点就是求众数。
个人思路
- 用count记录次数,tmp记录值
- 用当前值和tmp比较,如果相等则count++,不相等则count–
- count为0时,修改tmp值为当前值重复运行
- 最后保留的值即为出现次数最多的
这个描述的好像不太好,我也不太清楚怎么去描述这个思路,还是简单说一下原理吧,然后再画个图理解下。原理大概就是因为所求的一定是出现次数最多的,一一抵消之后所保留下来的数据就是所求(大概率,后面解释),拿一组数举例:1 2 3 2 2:
简单理解原理:众数存在的话,众数出现的次数肯定是最多的,拿其他非众数出现的次数和众数抵消掉,剩下的一定是众数,好比上述示例2出现3次,1和3加起来才2次,抵消两次后剩下的还是2。
至于为什么说抵消下来的大概率是众数这一点也是我纠结了很久才悟了,就是只有当众数存在的情况下剩下的才一定是它,好比上述示例我们前面都不变,最后的数字修改为其他数字的话就会出现错误,所以拿到最后一个数字后还需要遍历数据统计该数字出现的次数,超过个数的一半才能确定下来。
至于判空的问题虽然这个题目已经提到输入是非空的,所以可以不写判断条件,但是自己一定要想到。
代码展示
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int count=0,tmp=0;
for(int i=0;i<numbers.size();i++)
{
if(count != 0)
{
if(numbers[i]==tmp)
count++;
else
count--;
}
else
{
tmp=numbers[i];
count++;
}
}
count=0;
//统计出现的次数
for(int i=0;i<numbers.size();i++)
{
if(tmp==numbers[i])
count++;
}
//大于一般就输出,不然就没有
if(count>numbers.size()/2)
return tmp;
else
return 0;
}
};
题解
题解大概主要分两种思路,我和思路一基本一致,但是讲道理思路二应该才是更普遍的,但是我没想到。。。。
思路二:
- 排序
- 取中
- 统计次数
是的,思路二就是这样,因为题目要求数字超过个数一般,那么排序后的该数字就一定会出现在数组的中间,这里需要注意的就是它一定会出现在数组中间,但是出现在数组中间的不一定就是它,就好比1 2 3 4 5中间的是3,但是它并不是,所以也是需要统计次数验证的。
代码如下:
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
sort(numbers.begin(), numbers.end());
int count=0;
for(int i=0;i<numbers.size();i++)
{
if(numbers[i]==numbers[numbers.size()/2])
count++;
}
if(count>numbers.size()/2)
return numbers[numbers.size()/2];
else
return 0;
}
};
除了上述两种题解,我还发现了一个结合hash和map的解决方法好像也挺简单的(由于目前还没接触到,先只给出代码,后续学到了看情况再补充)
int MoreThanHalfNum_Solution(vector<int> numbers) {
map<int, int> table;
for (auto num:numbers) {
table[num]++;
}
for (auto i:table) {
if (i.second > numbers.size() / 2) {
return i.first;
}
}
return 0;
}
盲猜就是map第一个参数放数据,第二个参数放数据出现的次数,然后遍历map如果第二个参数大于一般直接输出存放在第一个参数里面的数据即可。