数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2
题解
-
摩尔投票法(不修改原数组)
- 算法思想
- 由于数组中众数超过数组的一半,让所有的数字投票,如果众数持有票面额为+1,非众数的持有的票面额为-1,则所有的数字的票的面额一定大于 0
- 复杂度分析
- 时间复杂度 O(n)
- 空间复杂度O(1)
class Solution { public int majorityElement(int[] nums) { int number = 0; int time = 0; for(int i = 0; i < nums.length; i++){ if(time == 0){ number = nums[i]; time++; }else if(nums[i] == number){ time++; }else{ time--; } } return number; } }
大佬写法
class Solution { public int majorityElement(int[] nums) { int x = 0, votes = 0; for(int num : nums){ if(votes == 0) x = num; votes += num == x ? 1 : -1; } return x; } }
- 算法思想
-
Partition法(会修改原数组)
-
算法思想
- 借鉴快速排序,当快速排序后pivot 的位置就是第 k 大元素的位置
- 当 k<n/2,那么中位数大于 k
- 否则相反
-
复杂度分析
-
时间复杂度 O(n):
-
第一次分区查找,我们需要对大小为 n 的数组执行分区操作,需要遍历 n 个元素。第二次分区查找,我们只需要对大小为 n/2 的数组执行分区操作,需要遍历 n/2 个元素。依次类推,分区遍历元素的个数分别为、n/2、n/4、n/8、n/16.……直到区间缩小为 1。如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。这是一个等比数列求和,最后的和等于 2n-1。所以,上述解决思路的时间复杂度就为 O(n)。
-
-
空间复杂度:O(1)
-
class Solution { public int majorityElement(int[] nums) { return quickSearch(nums,0,nums.length-1,nums.length/2); } int quickSearch(int[] nums,int low,int high,int target){ if(low > high)return 0; int k = partition(nums,low,high); if(k == target){ return nums[k]; }else if(k < target){ return quickSearch(nums,k+1,high,target); }else{ return quickSearch(nums,low,k-1,target); } } int partition(int[] nums,int start,int end){ int index = end; int small = start,pivot = nums[index]; for(int i = start; i < index;i++){ if(nums[i] < pivot){ if(small == i){ small++; }else{ int temp = nums[i]; nums[i] = nums[small]; nums[small] = temp; small++; } } } if(small < index ){ int temp = nums[small]; nums[small] = nums[index]; nums[index] = temp; } return small; } }
-
总结
- 快速排序应该多默写几次呀