剑指offer. 40 数组中出现次数超过一半的数字
题目描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路:
解法1:在无序数组中找到第k位的数字,用快速选择算法(O(N))
题目要求是出现次数超过一半,那假如数组中有这样的数字,而数组长度为奇数,下标为k/2的数字必定是,假如长度为偶数 下标k/2-1 和k/2 必定是, 所以我们只需找到k/2的数。最后遍历数组,看这个数次数是不是超过一半就ok了。解法2: 充分利用超过了一半的这一特性。
因为如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。
所以在遍历数组时保存两个值:一是数组中一个数字result,一是次数 time。遍历一个数字时,先判断当前次数是否为0,假如是则把当前数字保存,并将次数置为1。假如次数不是0,而若它与之前保存的数字相同,则次数加1,否则次数减1。
遍历结束后,所保存的数字即为所求。最后遍历数组,看这个数次数是不是超过一半就ok了。
代码1:
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.empty())
return 0;
int len= numbers.size(),left=0,right=len-1;
int index = partition(numbers,left,right);
int aim=len>>1;
while(index!=aim ){
if(index > aim){
right = index-1;
index = partition(numbers,left,right);
}
else{
left = index+1;
index = partition(numbers,left,right);
}
}
int res = numbers[index];
int k=0;
for(int i=0;i!=len;++i){
if(numbers[i]==res) ++k;
}
if(k>aim)
return res;
return 0;
}
int partition(vector<int> &vec,int left,int right){
int pivot=vec[left];
int less=left;
for(int i=left+1;i<=right;++i){
if(vec[i]<pivot)
swap(vec[i],vec[++less]);
}
swap(vec[left],vec[less]);
return less;
}
};
代码2:
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers)
{
if(numbers.empty()) return 0;
// 遍历每个元素,并记录次数;若与前一个元素相同,则次数加1,否则次数减1
int result = numbers[0];
int times = 1; // 次数
for(int i=1;i<numbers.size();++i)
{
if(times == 0)
{
// 更新result的值为当前元素,并置次数为1
result = numbers[i];
times = 1;
}
else if(numbers[i] == result)
{
++times; // 相同则加1
}
else
{
--times; // 不同则减1
}
}
// 判断result是否符合条件,即出现次数大于数组长度的一半
times = 0;
for(int i=0;i<numbers.size();++i)
{
if(numbers[i] == result) ++times;
}
return (times > numbers.size()/2) ? result : 0;
}
};