数组中次数出现超过一半的数字
题目
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路
思路1
重要前提:如果一个数字重复次数超过数组长度的一半,则数组中间数肯定是该重复的数字。
因此可以有如下思路:
- 利用快排的partition 函数不断找到 位于 数组中位数的 pivot。
- 如果partition返回的 index 在 mid 左边,则在 [index+1, right]继续找
- 如果partition返回的 index 在 mid 右边,则在[left, index-1]继续找
- index==mid,返回
- 如果该 pivot在数组中重复次数真的大于 一半,输出,
- 否则,输出无。
思路2
还有另一个思路:
相同数字抵消的思想。
维持一个当前数字 t 和 当前数字次数 n。
- 初始化时,t为 arr[0], n=1
- 继续往下走,如果arr[i] 与 t 相同,则 n+1, 如果不相同,n-1
- 如果n==0, 则 t 变为 该数字, n + 1
结束:
- 如果某个数字次数超过一半,最后剩下的肯定是它。
- 最后给出的数字要进行判断。 再遍历一遍验证。
实现
编程时 有两个错误:
- left = pIndex+1; 和 right = pIndex-1; ,当时没有加1和减1,造成不断循环,找到pIndex一直不变。
- if(times<=mid) 没有加等于,导致 size为9的数组,重复数字为4时,没返回不存在。最好的写法应该是times*2>length。
//思路1实现
class Solution {
public:
int partition(vector<int>& numbers, int left, int right)
{
if(numbers.empty() || left<0 || right>=numbers.size() || left>right)
return -1;
int pivot = numbers[left];
while(left<right)
{
while(left<right && numbers[right]>=pivot)
--right;
numbers[left] = numbers[right];
while(left<right && numbers[left]<=pivot)
++left;
numbers[right] = numbers[left];
}
numbers[left] = pivot;
return left;
}
int MoreThanHalfNum_Solution(vector<int> numbers) {
int length = numbers.size();
if(length==0) return 0;
int left = 0;
int right = length-1;
int mid = length/2;
int pIndex = partition(numbers, left, right);
while(pIndex!=mid)
{
if(pIndex<mid)
{
left = pIndex+1; //注意这里要加1
pIndex = partition(numbers, left, right);
}
else
{
right = pIndex-1; //注意这里要改变
pIndex = partition(numbers, left, right);
}
}
int targetNum = numbers[pIndex];
int times = 0;
for(auto t : numbers)
{
if(t == targetNum)
++times;
}
if(times<=mid) //注意临界值判断,其实 if(!(2*times>length))更直观
return 0;
return targetNum;
}
};
//思路2实现
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if(numbers.empty()) return 0;
int target = numbers.front();
int n = 1;
for(int i=1; i<numbers.size(); ++i)
{
if(n==0)
{
target = numbers[i];
++n;
}
else
{
if(target == numbers[i])
++n;
else
--n;
}
}
if(n<=0)
return 0;
int times = 0;
for(auto val:numbers)
{
if(val == target)
++times;
}
if(2*times>numbers.size())
return target;
else
return 0;
}
};