
这道题最直观的解法就是使用排序算法将数组变为一个有序数组。显然,满足题意的数字必然是此时数组的中位数。但是,我们仍然清楚,即使是最快的排序算法,复杂度也不能低于O(nlogn)。不过,以下两个方法都可以在线性复杂度内解决此问题。
1.基于快速排序的轴点选择函数的算法(中位数)
快速排序的轴点选择函数会将序列中小于轴点值的数值放置到轴点左侧,大于等于轴点值的数值放置到轴点右侧。我们令每一次划分后都返回轴点所在的位置。如果说这个轴点的位置恰好为中位数,则直接输出。如果在轴点位置在中位数左侧则说明目标数值不可能出现在轴点位置左侧,需要对右侧部分重新进行划分。轴点位置在中位数右侧则同理对左侧部分重新进行划分。直至轴点位置恰好为中位数的位置。
具体代码如下:
class Solution {
public:
int partition(vector<int> &nums, int lo, int hi){
int i = lo, j = hi;
while(i < j){
while(nums[i] < nums[lo]) i++;
while(nums[j] >= nums[lo]) j--;
if(i < j) swap(nums[i], nums[j]);
}
swap(nums[i], nums[lo]);
return i;
}
int moreThanHalfNum_Solution(vector<int>& nums) {
int lo = 0, hi = nums.size() - 1;
int mid = nums.size() >> 1;
int index = partition(nums, lo, hi);
while(index != mid){
if(index > mid){
hi = index - 1;
index = partition(nums, lo, hi);
}
else{
lo = index + 1;
index = partition(nums, lo, hi);
}
}
return nums[mid];
}
};
此算法的时间复杂度为O(n),空间复杂度为O(1)。另外,此算法会改变原数组。
2.基于统计的算法(众数)
可以发现,满足条件的数字同时也是所有数字中的众数。那么我们可以统计数字的出现次数从而决定哪个数字是符合题意的。当然,这是最原始的方法。然而很多人不愿意使用的原因在于,为了做出相应的统计需要额外开辟空间用来记录。同时哈希表自身的查找也要耗费可观的时间。
不过,我们没有必要统计所有数的出现次数。因为我们要找的是出现次数最多的那个数,因此可以采取如下的方法。
首先我们维护两个变量,一个用来存放当前已遍历序列中出现次数最多的数,另一个用来记录这个数值相对于其他已遍历数值多出的出现次数。对于每一个当前扫描到的数,如果其值等于维护的数值则次数加1,反之则减1。当次数变为0时则将当前数字替换为新的维护数字。这样直至遍历结束,最后一个令次数记为1的数字就是最终结果。
具体代码如下:
class Solution {
public:
int moreThanHalfNum_Solution(vector<int>& nums) {
int ans = nums[0], count = 1;
for(int i = 0; i < nums.size(); i++){
if(ans == nums[i]) count++;
else{
count--;
if(!count){
ans = nums[i];
count = 1;
}
}
}
return ans;
}
};
此算法的时间复杂度为O(n),空间复杂度为O(1)。另外,此算法不会改变原数组。
本文介绍两种在线性复杂度内找到数组中出现超过一半的数字的算法,一种基于快速排序的轴点选择,另一种基于统计的众数算法。前者通过递归划分数组,后者通过遍历数组并维护出现次数最多的数。
5万+

被折叠的 条评论
为什么被折叠?



