题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
以下算法中都最好加上
1、判断输入数组是否有效的checkinvalidarray函数
bool checkinvalidarray(int* numbers,int length)
{
bool input=false;
if(numbers==NULL||length==0)
input=true;
return input;
}
链接:https://blog.csdn.net/weixin_41413441/article/details/79180398
如果给出的数组不是一个vector数组,那么有两种基本解法:
1、基于哈希表的思想的枚举法,时间复杂度O(n)
与链接里相同(不修改原数组)
2、基于partition函数的算法(不是快排),时间复杂度为O(n)
但这种算法不适用于求出现次数<=n/2的情况(修改了原数组)
分析:数组中有一个数字出现的次数超过数组长度的一半。如果把这个数组排序,那么排序后位于数组中间的数字一定是要求的数字。
partition算法实现的效果:在数组中随机选择一个数字,然后调整数组中数字的顺序,使得比选中的数字小的都在他的左边,比选中的数字大的都在他的右边。那么partition之后
如果选中的数的下标刚好是n/2,这个数字就是数组的中位数,即要求的数。
如果其下标大于n/2,那么中位数应该位于它的左边,则可在左边部分继续查找。
如果其下标小于n/2,那么中位数应该位于它的右边,则可在右边部分继续查找。
代码:
int MoreThanHalfNum(int* numbers,int length)
{
if(checkinvalidarray(numbers,length))
return 0;
int middle=length;
int start=0;
int end=length-1;
int index=Partition(numbers,length,start,end);
while(index!=middle)
{
if(index>middle)
{end=index-1;
index=Partition(numbers,length,start,end);
}
else {start=index+1;
index=Partition(numbers,length,start,end);
}
int result=numbers[middle];
return result;
}
/*partition 函数*/
- void Swap(int A[], int i, int j)
- {
- int temp = A[i];
- A[i] = A[j];
- A[j] = temp;
- }
- int Partition(int A[], int left, int right) // 划分函数
- {
- int pivot = A[right]; // 这里每次都选择最后一个元素作为基准
- int tail = left - 1; // tail为小于基准的子数组最后一个元素的索引
- for (int i = left; i <right; i++) // 遍历基准以外的其他元素
- {
- if (A[i] <= pivot) // 把小于等于基准的元素放到前一个子数组末尾
- {
- tail++;
- if(tail!=i)
- Swap(A, tail, i);
- }
- }
- Swap(A, tail + 1, right); // 最后把基准放到前一个子数组的后边,剩下的子数组既是大于基准的子数组
- //或者for语句中(i<=right)则可省略最后一个Swap // 该操作很有可能把后面元素的稳定性打乱,所以快速排序是不稳定的排序算法
- return tail + 1; // 返回基准的索引
- }