给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
方法一:哈希表
时间复杂度:O(n)
我们将 nums 迭代一次,哈希表的插入是常数时间的。所以总时间复杂度为 O(n)时间的。
空间复杂度:O(n)
方法二:排序
如果所有数字按升序或者降序进行排序,那么众数的下标为「
n
/
2
n / 2
n/2」(「
n
/
2
n / 2
n/2」+ 1)
对于每种情况,数组下面的线表示如果众数是数组中最小值的情况下覆盖的下标。数组上面的线是数组中最大值的情况。其他情况,这条线会在这两种极端情况的中间。但我们看到即使是这两种极端情况,它们也会在下标为「 n / 2 n / 2 n/2」 的地方有重叠。因此,无论众数是多少,返回 「 n / 2 n / 2 n/2」下标对应的值都是正确的。
func majorityElement(nums []int) int {
sort.Ints(nums)
return nums[len(nums) / 2]
}
时间复杂度:O(nlgn)
空间复杂度:O(1)或O(n)
方法三:分治法
使用经典的分治算法递归求解,直到所有的子问题都是长度为 1 的数组。由于传输子数组需要额外的时间和空间,所以我们实际上只传输子区间的左右指针 low 和 high 表示相应区间的左右下标。长度为 1 的子数组中唯一的数显然是众数,直接返回即可。如果回溯后某区间的长度大于 1 ,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数。原问题的答案就是下标为 0和 n 之间的众数这一子问题。
func majorityElement(nums []int) int {
return majorityElementExec(nums, 0, len(nums) - 1)
}
func majorityElementExec(nums []int, low int, high int) int {
if low == high {
return nums[low]
}
mid := (high - low) / 2 + low
leftVal := majorityElementExec(nums, low, mid)
rightVal := majorityElementExec(nums, mid + 1, high)
if leftVal == rightVal {
return leftVal
}
leftCount := countInrange(nums, leftVal, low, high)
rightCount := countInrange(nums, rightVal, low, high)
if leftCount > rightCount {
return leftVal
} else {
return rightVal
}
}
func countInrange(nums []int, val, low, high int) int {
count := 0
for i := low; i <= high; i++ {
if nums[i] == val {
count++
}
}
return count
}
复杂度分析
时间复杂度:O(nlgn)
空间复杂度:O(lgn)
方法四:Boyer-Moore 投票算法
如果我们把众数记为 +1 ,把其他数记为 -1 ,将它们全部加起来,显然和大于 0 ,从结果本身我们可以看出众数比其他数多。
func majorityElement(nums []int) int {
var candidate int
count := 0
for _, val := range nums {
if count == 0 {
candidate = val
}
if val == candidate {
count++
} else {
count--
}
}
return candidate
}
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(1)