目录
一、需求
A:给定一个大小为n的数组,找到其中的多数元素;
a:多数元素是指在数组中出现次数大于[n/2]的元素;
B:假设数组非空,并且给定的数组总是存在多数元素;
二、暴力匹配法
2.1 思路分析
A:遍历数组,依次计算每个元素在数组中出现的次数;
B:若次数大于n/2,则返回该元素;
C:因为题目假定数组中含有多数元素,故最后的返回值随便返回一个数据就行;
2.2 代码实现
public int majorityElement(int[] nums) {
for(int i = 0; i < nums.length; i++) {
int count = 0;
for(int j = 0; j < nums.length; j++) {
if(nums[i] == nums[j]) {
count++;
}
}
if(count > nums.length/2) {
return nums[i];
}
}
return -1;
}
2.3 复杂度分析
A:时间复杂度为O(n^2);
B:空间复杂度为O(1);
三、HashMap实现
3.1 思路分析
A:创建HashMap对象,键表示数组值,值表示相同数组值的个数;
B:遍历数组,如果HashMap中不存在该数值,则将该数值加入HashMap中,对应的值为1;
C:如果HashMap中存在该数值,那该数值键对应的值加一;
D:等到当前键对应的值满足条件时,直接退出循环,经测试再加一个判断即可;
E:当然,也可以在遍历完数组后,开始遍历HashMap的键,得到每一个值,若该值大于n/2,则直接返回该键;
3.2 代码实现
public int majorityElement(int[] nums) {
//创建对象
HashMap<Integer,Integer> hm = new HashMap<Integer,Integer>();
//特殊情况
if(nums.length == 1) {
return nums[0];
}
//遍历数组
for(int i = 0; i < nums.length; i++) {
if(hm.containsKey(nums[i])) {
//map中直接修改键值,要重新覆盖
hm.put(nums[i],hm.get(nums[i])+1);
int count = hm.get(nums[i]);
if(count > nums.length/2) {
return nums[i];
}
} else {
hm.put(nums[i],1);
}
}
return -1;
}
3.3 复杂度分析
A:时间复杂度为O(n);
B:空间复杂度为O(n);
四、排序法
4.1 思路分析
A:将数组中的元素进行排序,多数元素个数必然大于n/2,所以直接返回中间的元素即可;
4.2 代码实现
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
4.3 复杂度分析
A:时间复杂度为O(nlogn);
B:空间复杂度为O(n)或者O(1),分别对应线性空间中拷贝数组排序和就地排序;
五、随机法
5.1 思路分析
A:创建Random对象,随机获取数组中的一个数据;
B:然后遍历数组,判断这个数据出现的次数是否大于n/2;
C:大于则返回该数据,否则继续循环;
5.2 代码实现
public int majorityElement(int[] nums) {
//创建Random对象
Random rand = new Random();
while(true) {
//随机获取数组的某一下标
int index = rand.nextInt(nums.length);
int count = 0;
//遍历数组
for(int i = 0; i < nums.length; i++) {
if(nums[i] == nums[index]) {
count++;
}
}
if(count > nums.length/2) {
return nums[index];
}
}
}
5.3 复杂度分析
A:时间复杂度理论上来说是O(∞),但是众数占据了一半以上,迭代的期望次数是个常数,期望运行的时间也是线性的;
B:空间复杂度为O(1);
六、分治算法
6.1 思路分析
6.2 代码实现
class Solution {
//创建计算元素个数的功能
private int number(int[] nums,int num,int left_index,int right_index) {
int count = 0;
for(int i = left_index; i <= right_index; i++) {
if(nums[i] == num) {
count++;
}
}
return count;
}
//递归函数
private int diGui(int[] nums,int left_index,int right_index) {
//递归结束条件
if(left_index == right_index) {
return nums[left_index];
}
//开始拆分数组为左子数组和右子数组
int mid = (left_index + right_index) >>> 1;
//定义左子数组众数left
int left = diGui(nums,left_index,mid);
//定义右子数组众数right
int right = diGui(nums,mid+1,right_index);
//左右众数相同
if(left == right) {
return left;
}
//左右众数不同
int left_Count = number(nums,left,left_index,right_index);
int right_Count = number(nums,right,left_index,right_index);
return left_Count > right_Count ? left:right;
}
public int majorityElement(int[] nums) {
return diGui(nums, 0, nums.length-1);
}
}
6.3 复杂度分析
A:时间复杂度O(nlogn);
B:空间复杂度O(logn);
七、摩尔投票法
7.1 思路分析
A:假设众数用+1来表示,非众数用-1来表示,所有数据加起来的结果必然大于0;
B:遍历数组,假定第一个数就是众数,则count+1,也就是有一个+1了;
C:然后继续遍历,若与当前众数相同,那么count+1,若不同,则count-1;
D:当count==0时,继续计算下一个众数的count,一定会有count>0,这时候返回当前的众数;
7.2 代码实现
public int majorityElement(int[] nums) {
int current_mode = 0;
int count = 0;
//遍历数组
for(int num : nums) {
if(count == 0) {
current_mode = num;
}
count += current_mode == num ? 1 : -1;
}
return current_mode;
}
7.3 复杂度分析
A:时间复杂度为O(n);
B:空间复杂度为O(1);
八、位运算
8.1 思路分析
8.2 代码实现
public int majorityElement(int[] nums) {
int majority = 0;
int n = nums.length;
//遍历32列
for(int i = 0, mask = 1; i < 32; i++, mask <<= 1) {
int count = 0;
for(int j = 0; j < n; j++) {
if((mask & nums[j]) == mask) {
count++;
}
}
if(count > n/2) {
majority |= mask;
}
}
return majority;
}
8.3 复杂度分析
A:时间复杂度为O(32n),也就是O(n);
B:空间复杂度为O(1);
九、参考地址
B:https://leetcode-cn.com/problems/majority-element/solution/qiu-zhong-shu-by-leetcode-2/