问题描述:在一个数组里面找出出现次数最多的一个数,(出现次数最多在本题里面被定义成为:出现次数多于N / 2 次)
1.分析:
读完题目之后,会有几种解题方法浮现出来:
(1)直接使用数组的index来解决这个问题:
input : nums(arr of int)
let arr be a new arr:
for num in nums:
arr[num]++
return max_element_in_arr
这种方法很容易想到,但是不切实际,因为数组里面的数字不可能全部都是正数,而且不知道最大值是多少,如果最大值为INT_MAX,不可能在栈上或者堆上开这么大的数组来使用。
(2)使用红黑树数据结构:
input : nums(arr of int)
let map be a new map:
for num in nums:
map[nums]++;
return key_has_max_value_in_map
使用key-value数据结构可以很好地帮助我们解决这个问题,并且可以得到不错的时间复杂度(O(N)),但是使用了额外的空间,空间复杂度为O(N)。
对于这个题目,我们使用下面这个方法可以把空间复杂度下降到O(1):
我们先随机选取一个数组里面的元素:统计这个元素出现的次数,如果出现次数大于 N / 2,那么这个元素就是答案。:
input : nums(arr of int)
count = 0;
while 1:
count = 0;
let current be a random element in arr:
for num in arr:
if num == current:
count++
if count > N / 2
return current
这样我们就能够把空间复杂度下降到O(1),并且平均时间复杂度也是O(N)。
具体的C++代码:
int majorityElement(vector<int>& nums) {
int size = nums.size();
int count = 0;
while (1) {
count = 0;
int index = rand() % size;
int target = nums[index];
for (int i = 0; i < size; ++i) {
if (target == nums[i]) {
count++;
}
}
if (count > size / 2) {
return target;
}
}
}
(3)主元个数减掉不是主元的个数的值必然大于0:
int majorityElement(vector<int>& nums) {
int major = nums[0];
int count = 1;
int size = nums.size();
for (int i = 1; i < size; ++i) {
major == nums[i] ? count++ : count--;
if (count == 0) {
major = nums[i];
count = 1;
}
}
return major;
}
2.几个问题:
(1)为什么需要随机:
因为主元的个数大于 N / 2,在最坏情况下:前面的 N / 2个元素都不是主元,那么时间复杂度会上升到O(N^2)。
(2)为什么不直接用Map数据结构:
可以使用Map,并且很稳定(对于任何的输入都可以得到O(N)的时间复杂度),最后这种方法有点看脸,时间会有波动。最后一种方法只是在空间上面进行优化,使用随机来“随机保证”时间复杂度不变。