问题描述
给定一个数组,找出其中出现次数超过一半的元素。例如,给定数组[1, 2, 3, 2, 2, 2, 5, 4, 2],出现次数超过一半的元素是2。
解决方案
这个问题可以用摩尔投票法来解决。摩尔投票法是一种常用的技巧,它可以在O(n)的时间复杂度和O(1)的空间复杂度下,找出数组中的众数,即出现次数最多的元素。我们可以用两个变量来表示当前的候选元素和它的票数,初始都为0。然后,我们遍历数组,对于每个元素,如果它与候选元素相同,就给候选元素增加一票;如果它与候选元素不同,就给候选元素减少一票;如果候选元素的票数为0,就把当前元素作为新的候选元素,并给它一票。最后,候选元素就是数组中的众数。这个算法的原理是,如果一个元素出现次数超过一半,那么它的票数一定大于其他所有元素的票数之和,所以它一定能够成为最后的候选元素。当然,这个算法的前提是,数组中一定存在一个出现次数超过一半的元素,否则,候选元素可能不是正确的答案。为了验证候选元素是否真的是正确的答案,我们还需要再遍历一次数组,统计候选元素的出现次数,如果确实超过一半,就返回候选元素,否则,就返回-1,表示没有这样的元素。
代码
以下是用C语言实现的代码,假设数组的长度不超过100。
#include <stdio.h>
#include <stdlib.h>
int majority_element(int *nums, int nums_size) {
if (nums == NULL || nums_size == 0) {
return -1;
}
int candidate = 0;
int count = 0;
for (int i = 0; i < nums_size; i++) {
if (count == 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] == candidate) {
count++;
} else {
count--;
}
}
int frequency = 0;
for (int i = 0; i < nums_size; i++) {
if (nums[i] == candidate) {
frequency++;
}
}
if (frequency > nums_size / 2) {
return candidate;
} else {
return -1;
}
}
void print_array(int *array, int size) {
printf("[");
for (int i = 0; i < size; i++) {
printf("%d", array[i]);
if (i < size - 1) {
printf(", ");
}
}
printf("]\n");
}
int main() {
int nums[] = {1, 2, 3, 2, 2, 2, 5, 4, 2};
int nums_size = sizeof(nums) / sizeof(nums[0]);
int result = majority_element(nums, nums_size);
printf("数组:");
print_array(nums, nums_size);
printf("结果:%d\n", result);
return 0;
}
运行结果:
总结
这个问题的核心是如何在不使用额外的空间的情况下,找出数组中的众数,即出现次数最多的元素。如果用暴力的方法,就需要遍历每个元素,然后再遍历一次数组,统计它的出现次数,时间复杂度是O(n^2),效率很低。如果用摩尔投票法的方法,就可以在O(n)的时间复杂度和O(1)的空间复杂度下,完成这个任务。摩尔投票法的原理是,维护一个候选元素和它的票数,然后根据数组中的元素与候选元素的关系,增加或减少票数,最后得到一个可能的候选元素。然后,再验证候选元素是否真的是众数,如果是,就返回它,否则,就返回-1。摩尔投票法是一种非常实用的技巧,可以解决很多数组相关的问题,值得学习和掌握。