算法:多数元素
前言
柚子下面展示多数元素的几种解法,感兴趣的话,快向下看吧!
提示:以下是本篇文章正文内容,下面案例可供参考
一、解法一(哈希)
HashMap+分别两次循环
class Solution {
public int majorityElement(int[] nums) {
int middle = nums.length / 2;
//key是数组值,value是值出现的次数
Map<Integer, Integer> numMap = new HashMap<>();
for (int num : nums) {
if (!numMap.containsKey(num)) {
numMap.put(num, 1);
} else {
numMap.put(num, numMap.get(num) + 1);
}
}
int num = 0;
for (Map.Entry<Integer, Integer> entry : numMap.entrySet()) {
if (entry.getValue() > middle) {
num = entry.getKey();
}
}
return num;
}
}
二、解法二(哈希+打擂台遍历)
HashMap+打擂台遍历(维护最大的值)
class Solution {
private Map<Integer, Integer> countNums (int[] nums) {
Map<Integer, Integer> numMap = new HashMap<>();
for (int num : nums) {
if (!numMap.containsKey(num)) {
numMap.put(num, 1);
} else {
numMap.put(num, numMap.get(num) + 1);
}
}
return numMap;
}
//hashMap+打擂台遍历(维护最大的值)
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();
}
}
三、解法三(排序)
因为数组定存在n/2长度的多数元素,取排序后的n/2下标定为正确值
class Solution {
public int majorityElement(int[] nums) {
int middle = nums.length / 2;
Arrays.sort(nums);
return nums[middle];
}
}
四、解法四(随机)
随机化,因为众数出现的次数>n/2,所以随机取值有很大概率取到众数
class Solution {
//生成随机数
private int randRange(Random random, int min, int max) {
return random.nextInt(max - min) + min;
}
//计数发生次数
private int countOccurences(int[] nums, int num) {
int count = 0;
for (int num1 : nums) {
if (num1 == num) {
count++;
}
}
return count;
}
//返回众数
public int majorityElement(int[] nums) {
Random random = new Random();
//算出中间下标
int middle = nums.length/2;
while (true) {
//获取随机数
int candidate = nums[randRange(random, 0, nums.length)];
//遍历随机数的发生次数 和 中间下标比较
if (countOccurences(nums, candidate) > middle) {
return candidate;
}
}
}
}
五、解法五(分治)
如果数 a 是数组 nums 的众数,如果我们将 nums 分成两部分,那么 a 必定是至少一部分的众数。
我们可以使用反证法来证明这个结论。假设 a 既不是左半部分的众数,也不是右半部分的众数,那么 a 出现的次数少于 l/ 2 + r/2 次,
其中 l 和 r 分别是左半部分和右半部分的长度。由于 l/2 + r/2 <= (l + r) / 2,说明 a 也不是数组 nums 的众数,因此出现了矛盾。
所以这个结论是正确的。
这样以来,我们就可以使用分治法解决这个问题:将数组分成左右两部分,分别求出左半部分的众数 a1 以及右半部分的众数 a2,
随后在 a1 和 a2 中选出正确的众数。
算法:
我们使用经典的分治算法递归求解,直到所有的子问题都是长度为 1 的数组。长度为 1 的子数组中唯一的数显然是众数,直接返回即可。如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。
class Solution {
/**
* 统计众数的次数
*
* @param nums 数组
* @param num 众数
* @param lo 左边下标
* @param hi 右边下标
* @return 众数出现的次数
*/
private int countInRange(int[] nums, int num, int lo, int hi) {
int count = 0;
for (int i = lo; i <= hi; i++) {
if (nums[i] == num) {
count++;
}
}
return count;
}
/**
* 计算众数
*
* @param nums 数组
* @param lo 左边下标
* @param hi 右边下标
* @return 数组中的众数
*/
private int majorityElementRec(int[] nums, int lo, int hi) {
//大小为1的数组中唯一的元素是众数
if (lo == hi) {
return nums[lo];
}
//递归左边和右边部分
//中间数=(右边下标-左边下标)/2 + 左边下标,根据mid,将数组分为左右两部分
int mid = (hi - lo) / 2 + lo;
int left = majorityElementRec(nums, lo, mid);
int right = majorityElementRec(nums, mid + 1, hi);
//如果左半边众数和右半边的众数相同,则将其返回
if (left == right) {
return left;
}
//如果左边众数跟右边众数不相同,选出出现次数更多的众数
int leftCount = countInRange(nums, left, lo, hi);
int rightCount = countInRange(nums, right, lo, hi);
return leftCount > rightCount ? left : right;
}
public int majorityElement(int[] nums) {
return majorityElementRec(nums, 0, nums.length - 1);
}
}
六、解法六(Boyer-Moore 投票算法)
Boyer-Moore 投票算法(因为解析比较长,可以看另外一篇文章)
传送门: Boyer-Moore 投票算法解析
class Solution {
public int majorityElement(int[] nums) {
//初始count为0
int count = 0;
//候选众数为null
Integer candidate = null;
//遍历nums数组
for (int num : nums) {
//count为0时,就让候选众数为当前数组元素
if (count == 0) {
candidate = num;
}
//如果数组元素跟候选众数相等,count加1,反之count就减1
count += (num == candidate) ? 1 : -1;
}
//因为最后遍历结束后,count非负,且最后的候选众数就是数组的众数
return candidate;
}
}
题目来源:https://leetcode.cn/leetbook/read/top-interview-questions/xm77tm/
╭◜◝ ͡ ◜◝╮
( ˃̶͈◡˂ ̶͈ )感觉有用的话,欢迎点赞评论呀!
╰◟◞ ͜ ◟◞╯