题目:
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:
输入:[2,2,1,1,1,2,2]
输出:2
进阶:
- 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
解法1:排序
/**
* 思路:
* 排序后中间的数就是众数
*/
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
时间复杂度:Onlogn
空间复杂度:O1
解法2:随机数
/**
* 思路:
* 随机挑一个数,大概率是众数
*/
public int majorityElement(int[] nums) {
while (true) {
int random_num = randomGetNum(nums);
boolean result = isMajority(random_num,nums);
if (result) return random_num;
}
}
private boolean isMajority(int random_num, int[] nums) {
int count=0;
for (int num:nums){
if (num==random_num)count++;
}
return count>nums.length/2;
}
private int randomGetNum(int[] nums) {
Random random = new Random();
int index = random.nextInt(nums.length);
return nums[index];
}
时间复杂度:On^2
空间复杂度:O1
解法3:位运算
/**
* 思路:
* 核心思路:众数超过数组的一半,也就意味着其二进制位上的每一个数都超过数组的一半
*/
public int majorityElement(int[] nums) {
int result = 0, k = nums.length >> 1;
//符号位所以只能到31
for (int i = 0; i < 32; i++) {
int count = 0;
for (int num : nums) {
//num右移i后其二进制是否为1。也就是第i为二进制是否为1
count += num >> i & 1;
if (count > k) {
//众数通过不断的累加其二进制位的十进制值,最后就组成一个完整的十进制众数
result += 1 << i;
break;
}
}
}
return result;
}
时间复杂度:On
空间复杂度:O1
或者这样写
public int majorityElement(int[] nums) {
int[] bits = new int[32];
for (int num : nums){
for (int i=0;i<bits.length;i++){
if (((num>>i)&1)==1)bits[i]++;
}
}
int result=0;
for (int i=0;i<bits.length;i++){
if (bits[i]>nums.length/2)result+=(1<<i);
}
return result;
}
时间复杂度:On^2
空间复杂度:On
解法4:分治
/**
* 思路:
* 分治:查整个数组的众数,可以分解为子问题:比较左右两边的众数
* 切分数组,比较左右两边的众数
* 如果众数一样就返回
* 不一样就比较左右众数的个数,多的那个就是众数
*/
public int majorityElement(int[] nums) {
return recursive(0,nums.length-1,nums);
}
private int recursive(int left, int right, int[] nums) {
if (left==right)return nums[left];
int mid=(left+right)/2;
int l_majority = recursive(left, mid, nums);
int r_majority = recursive(mid + 1, right, nums);
int l_m_count=getCount(l_majority,left,right,nums);
int r_m_count=getCount(r_majority, left, right, nums);
return l_m_count>r_m_count?l_majority:r_majority;
}
private int getCount(int majority, int left, int right, int[] nums) {
int count=0;
for (int i=left;i<=right;i++){
if (nums[i]==majority)count++;
}
return count;
}
时间复杂度:On
空间复杂度:On
解法5:MorreVoting
/**
* 思路:
* 这个思路很巧妙,众数一定比其他数的总和多
* 把第一个数记作众数,如果是第一个数count++,不是count--
* 如果count==0说明这个数不是众数,把其他的数作为众数
* 继续循环直到所有的数走完
*/
public int majorityElement(int[] nums) {
int count=0,majority=0;
for (int num:nums){
if (count==0)majority=num;
if (num==majority)count++;
else count--;
}
return majority;
}
时间复杂度:On
空间复杂度:O1
解法6:map
/**
* 思路:
* map中k存数,v存次数
* 遍历数组,不断的更新map,如果v>n/2返回
*/
public int majorityElement(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
if(map.get(num)>nums.length/2)return num;
}
return -1;
}
时间复杂度:On
空间复杂度:On