题目描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
我的题解
第一想法是把每个元素出现的次数统计下,找出次数大于n/2的即可,使用hashmap实现:
1 public int majorityElement(int[] nums) { 2 3 Map<Integer,Integer> map =new HashMap<>(); 4 for(int n: nums){ 5 if (map.containsKey(n)){ 6 map.put(n,map.get(n)+1); //数量+1 7 } 8 else{ 9 map.put(n,1); //第一次出现,初始化为1 10 } 11 } 12 int size = nums.length/2; 13 for (Map.Entry<Integer,Integer> e: map.entrySet()){ 14 if (e.getValue()>size){ 15 return e.getKey(); 16 } 17 } 18 return -1; 19 }
时间复杂度是O(nlogn),空间复杂度是O(n)
性能实际上并不是很好。
其他解法
分析题意,可知‘多数元素’(众数)一定存在,且一定只有一个(两个总长度超出了n),数量大于n/2。
于是:对数组进行排序后,中间这个数一定是众数。
1 Arrays.sort(nums); 2 return nums[nums.length / 2];
代码简洁,时间/空间复杂度取决于使用的排序算法,时间复杂度不可能达到O(N).
还有更好的解法:
最优解 · 摩尔投票法
算法思想
核心思想是 对拼抵消,比如老师和学生干架,一个学生和一个老师能"同归于尽",由于学生数量比老师多,所以最后学生一定是赢家。
这里也是一样的,这个每次两个不同的数抵消,最后剩下的一定是众数。
1 public int majorityElement(int[] nums) { 2 int res= nums[0];//取一个数,不知道是众数还是其他数。 3 int count = 0; //统计这个数出现的次数。 4 for(int num : nums) {//遍历数组 5 if(num != res) { //如果取到的数和res不同,说明要让它们'同归于尽',即count-1,并移到下一个数。 6 count--; 7 if(count == 0) { //如果count为0,表示当前统计的这个数数目为0,不能在被抵消,这时必须换一个数,以能够被抵消 8 count = 1; 9 res = num; //num虽然被抵消了,但是赋给res并不影响,因为后面可以还有和num相同的值,如果不同就会一直过渡下去。 10 } 11 } 12 else 13 count++; //相同就数量+1, 14 } 15 return res; 16 17 }
时间复杂度O(N),空间复杂度O(1)