6.2 多数元素【169】
6.2.1 题目描述
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
6.2.2 方法一:哈希表
思路
我们知道出现次数最多的元素大于 $\lfloor \dfrac{n}{2} \rfloor $次,所以可以用哈希表来快速统计每个元素出现的次数。
算法
我们使用哈希映射(HashMap)来存储每个元素以及出现的次数。对于哈希映射中的每个键值对,键表示一个元素,值表示该元素出现的次数。
我们用一个循环遍历数组 nums 并将数组中的每个元素加入哈希映射中。在这之后,我们遍历哈希映射中的所有键值对,返回值最大的键。我们同样也可以在遍历数组 nums 时候使用打擂台的方法,维护最大的值,这样省去了最后对哈希映射的遍历。
class Solution {
private Map<Integer, Integer> countNums(int[] nums) { // 统计nums数组中各元素数量
Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
for (int num : nums) {
if (!counts.containsKey(num)) {
counts.put(num, 1);
} else {
counts.put(num, counts.get(num) + 1);
}
}
return counts;
}
public int majorityElement(int[] nums) {
Map<Integer, Integer> counts = countNums(nums);
Map.Entry<Integer, Integer> majorityEntry = null;
for (Map.Entry<Integer, Integer> entry : counts.entrySet()) {
if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {
majorityEntry = entry;
}
}
return majorityEntry.getKey();
}
}
6.2.3 方法二:排序
思路
如果将数组 nums 中的所有元素按照单调递增或单调递减的顺序排序,那么下标为 ⌊ n 2 ⌋ \lfloor \dfrac{n}{2} \rfloor ⌊2n⌋的元素(下标从 0 开始)一定是众数。
算法
对于这种算法,我们先将 nums 数组排序,然后返回上文所说的下标对应的元素。下面的图中解释了为什么这种策略是有效的。在下图中,第一个例子是 n 为奇数的情况,第二个例子是 n 为偶数的情况。
对于每种情况,数组下面的线表示如果众数是数组中的最小值时覆盖的下标,数组下面的线表示如果众数是数组中的最大值时覆盖的下标。对于其他的情况,这条线会在这两种极端情况的中间。对于这两种极端情况,它们会在下标为 ⌊ n 2 ⌋ \lfloor \dfrac{n}{2} \rfloor ⌊2n⌋的地方有重叠。因此,无论众数是多少,返回 ⌊ n 2 ⌋ \lfloor \dfrac{n}{2} \rfloor ⌊2n⌋下标对应的值都是正确的。
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
复杂度分析
- 时间复杂度:O(nlogn)。将数组排序的时间复杂度为 O(nlogn)。
- 空间复杂度:O(logn)。如果使用语言自带的排序算法,需要使用 O(logn) 的栈空间。如果自己编写堆排序,则只需要使用 O(1) 的额外空间。
6.2.4 方法三:随机化
思路
因为超过$\lfloor \dfrac{n}{2} \rfloor $的数组下标被众数占据了,这样我们随机挑选一个下标对应的元素并验证,有很大的概率能找到众数。
算法
由于一个给定的下标对应的数字很有可能是众数,我们随机挑选一个下标,检查它是否是众数,如果是就返回,否则继续随机挑选。
class Solution {
private int randRange(Random rand, int min, int max) {
return rand.nextInt(max - min) + min;
}
private int countOccurences(int[] nums, int num) {
int count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == num) {
count++;
}
}
return count;
}
public int majorityElement(int[] nums) {
Random rand = new Random();
int majorityCount = nums.length / 2;
while (true) {
int candidate = nums[randRange(rand, 0, nums.length)];
if (countOccurences(nums, candidate) > majorityCount) {
return candidate;
}
}
}
}
复杂度分析
6.2.5 my answer—排序
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
int count = 1;
int n = nums.length;
for(int i = 1;i<n;i++){
if(nums[i]==nums[i-1]){
count++;
}else{
if(count > Math.floor(n/2.0)){
return nums[i-1];
}
count = 1;
}
}
if(count > Math.floor(n/2.0)){
return nums[n-1];
}
return 0;
}
}