一、题目描述
给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
输入描述:保证数组输入非空,且保证有解
二、示例
示例一:
输入:[1,2,3,2,2,2,5,4,2]
输出:2
示例二:
输入:[3,3,3,3,2,2,2]
输出:3
示例三:
输入:[1]
输出:1
三、主要思想
1.哈希思想
这道题可以用哈希思想来解决,也是比较常规的一种解法。我们可以建立数组元素的值与出现次数之间的映射关系,遍历整个数组,每个元素出现了一次出现次数就加一,最后遍历整个哈希表查看出现次数最多的值是哪一个即可。
class Solution
{
public:
int MoreThanHalfNum_Solution(vector<int> numbers)
{
unordered_map<int, int> map;
int half = numbers.size() / 2;
for (int i = 0; i < numbers.size(); i++)
{
auto it = map.find(numbers[i]);
// 如果已经在map中,进行自增,如果不在,插入,首次出现
if (it != map.end())
{
map[numbers[i]]++;
}
else
{
map.insert(make_pair(numbers[i], 1));
}
// 自增或者插入一个,直接进行判定。注意,这里要考虑测试用例为{1}的情况
// 走到这里,对应的key val一定存在
if (map[numbers[i]] > half)
{
return numbers[i];
}
}
// 走到这里,说明没有找到
return 0;
}
};
2.排序思想
由于题目在输入描述中明确了一定会有一个值出现的次数大于数组元素个数的一半,所以我们可以先对整个数组进行排序,无论排升序还是降序,相同的值一定会在一起,并且由于该元素出现的次数大于数组元素个数的一半,所以在有序数组里面的中间位置,一定是这个值,我们排好序以后直接返回中间位置的值即可。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size() == 0)
{
return 0;
}
sort(numbers.begin(), numbers.end());
return numbers[numbers.size() / 2];
}
};
3.抵消思想
由于我们要找的元素是出现个数超过数组元素个数的一半,那么就说明,除了这个元素以外的所有元素它们的出现次数加起来一定比这个元素小。
我们可以利用两个不同的元素相互抵消的思想,最后剩下来的就是出现次数超过数组元素个数一半的数字。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.size() == 0)
{
return 0;
}
// 让number从numbers的第一个元素开始
int number = numbers[0];
int times = 1;// 记录当前number的出现次数
for(int i = 1; i < numbers.size(); i++)
{
// 如果times==0说明前面已经将number抵消完了
if(times == 0)
{
number = numbers[i];
}
// 如果numbers当前的值和number相等,则无法抵消
if(numbers[i] == number)
{
times++;
}
// 如果numbers当前的值和number不相等,则抵消
else
{
times--;
}
}
return number;
}
};