169.多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素
你可以假设数组是非空的,并且给定的数组总是存在多数元素
- 哈希表解法
key为不同的元素,value为出现的次数,遍历整个数组将key和value存入map中,再遍历每一个key的value,当value大于n/2时加入list中
public int majorityElement(int[] nums) {
Map<Integer,Integer> map = new HashMap<>();
for(int n : nums){
int val = map.get(n)==null ? 0 : map.get(n);
map.put(n,++val);
}
int ans = 0;
for(int i : map.keySet()){
if(map.get(i)>nums.length/2){
ans = i;
break;
}
}
return ans;
}
哈希表解法的时间复杂度为O(n),空间复杂度为O(n),力扣执行情况如下
- 摩尔投票算法
举个例子,有六只羊和三只老虎、两只狼关在一起,假设每只老虎和狼只能吃一只羊,并且它们一定会吃羊,吃掉这只羊就会睡觉,要从这些动物中选出羊,那么这个动物一定是活着的不睡觉的动物,这就是投票算法的核心思想,一个一个抵消掉,多于一半的一定会剩下
现在对这些动物进行处理,找出真正的羊的编号,假设把羊编号设为1,老虎设为2,狼设为3,设置一个空间,把这些动物一只一只扔进去,并设置计数器,当编号与之前不符的则让计数器减一,并且同时判断计数器值,当计数器为零时,设置此时扔进去的动物编号为目标编号,我们可以注意到,狼只有2只,假设前两次扔进去的都是狼,目标则为狼,值为2,计数器变成2,再扔进去老虎,计数器会在第三次扔老虎的时候变成零,导致目标变成老虎,值为3,再扔进去六只羊,同理计数器会再次清零,让目标变成羊,值为1。接下来我们来代码实现
首先取count计数,targer为目标值,将这两个变量都声明为0,遍历数组,如果count为0时target等于目前的元素,再对count进行计算,若target等于目前元素count加一,若target不等于目前元素count减一
public int majorityElement(int[] nums) {
int count = 0;
int target = 0;
for(int num : nums){
if(count == 0){
target = num;
}
count += target == num ? 1 : -1;
}
return target;
}
投票算法的时间复杂度为 O(n)、空间复杂度为 O(1) ,力扣执行情况如下
- 排序法
还有一种比较简单的算法,这里也罗列出来,对这些数据进行排序,取下标n/2的元素,若这个元素出现次数大于n/2,那么在下标n/2的位置上一定是该元素
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
229.求众数
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
- 哈希表解法
这道题也可以使用哈希表解法,解法类似上题,这里直接展示代码
public List<Integer> majorityElement(int[] nums) {
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
Integer val = map.get(nums[i]) == null ? 0 : map.get(nums[i]);
map.put(nums[i],++val);
}
List<Integer> list = new ArrayList<>();
for(Integer key : map.keySet()){
if(map.get(key)>nums.length/3){
list.add(key);
}
}
return list;
}
力扣执行情况如下
- 摩尔投票算法
首先,我们可以先想一下出现超过 ⌊ n/3 ⌋ 的元素有几个,假如有三个,那么这三个元素出现的次数总和就会超过数组的长度,与题意不符,所以只能有两个,以此类推,出现超过n/k的元素个数一定最多为k-1个
已知超过n/3的元素个数只有两个,那么我们可以声明两个计数器两个目标值,使用第一种的投票方法找到两个出现次数最多的元素,再对数组遍历计数,如果大于n/3则加入list返回
投票方法简述:
声明a,计数器为c1,声明b,计数器为c2,遍历所有的元素,并判断计数器的值,若a与元素相等,则c1加一,若b与元素相等,则c2加一,当计数器为零时替换元素,计数器等于1,若这个元素不等于a也不等于b,则两个计数器都减一
public List<Integer> majorityElement(int[] nums) {
int a = 0;
int b = 0;
int c1 = 0;
int c2 = 0;
for(int i : nums){
if(c1 != 0 && a == i){c1++;}
else if(c2 != 0 && b == i){c2++;}
else if(c1 == 0){
a = i;
c1++;}
else if(c2 == 0){
b = i;
c2++;
}else{
c1--;
c2--;
}
}
c1=0;
c2=0;
List<Integer> list = new ArrayList<>();
for(Integer n : nums){
if(a == n){
c1++;
}else if(b == n){
c2++;
}
}
if(c1 > nums.length/3){
list.add(a);
}
if(c2 > nums.length/3){
list.add(b);
}
return list;
}
以[1,1,1,3,3,2,2,2]为例记录数字变化情况
元素 | 1 | 1 | 1 | 3 | 3 | 2 | 2 | 2 |
---|---|---|---|---|---|---|---|---|
a | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
c1 | 1 | 2 | 3 | 3 | 3 | 2 | 1 | 0 |
b | 0 | 0 | 0 | 3 | 3 | 3 | 3 | 2 |
c2 | 0 | 0 | 0 | 1 | 2 | 1 | 0 | 1 |
虽然c1变成0了,但循环结束了,所以a的值不会再进行替换
时间复杂度为O(n),空间复杂度为O(1),力扣执行情况如下