这两天想到一个冷笑话,迫不及待想和大家分享
在外面买鸡蛋,教大家如何区分土鸡蛋和洋鸡蛋
把鸡蛋打破,蛋清越透明说明这鸡蛋是土鸡蛋
。。。
不好笑也要笑<<
题目:
给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]
思路:
这题我刚开始是真的没思绪,因为空间复杂度的限制,我们只能定义变量。可是数组有n个元素,怎么用变量来找啊。看了网上的思路后才发觉自己有个概念没有理解,就是摩尔投票法。什么是摩尔投票法呢?定义如下:
一组数字序列中出现次数大于总数1/2的数字(并且假设这个数字一定存在),显然这个数字只可能有一个
本题是要我们找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素,那定义可以变化为:
一组数字序列中出现次数大于总数1/3的数字(并且假设这个数字一定存在),显然这个数字最多有2个
这个定义理解了,那前面我们提到的疑惑就解决了。只需要定义两个变量,分别记录众数即可。
当然了,光有定义可不行,还得知道如何找出这些众数才算数,这时候又不得不提到摩尔投票法的核心思想了,如下所示:
当一个数的重复次数超过数组长度的一半,每次将两个不相同的数删除,最终剩下的就是要找的数
上图就是查找出现次数大于总数1/2的数字过程,核心思路就是不断抵消对抗。那么本题中如何找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素呢?上面也提到了,我们可以设立两个cand1,cand2变量分别记录可能是众数的数字,num1和num2分别代表相应数字出现的次数。类比找出出现次数大于总数1/2的数字过程,我们此处是如果cand1,cand2与当前数字不一样,才会共同抵消,否则不会抵消。过程如下:
最后还有个特殊情况,就是比如:[A, B, C]的抵消阶段,最后得到的结果是 [C,1],C候选人的票数也未能超过一半的票数。所以,我们在代码的后面还需要加个计数操作,如果当前cand对应的num确实是超过了1/2或者1/3才能说,它真的是我们要找的众数。代码如下:
class Solution(object):
def majorityElement(self, nums):
"""
摩尔投票法
:type nums: List[int]
:rtype: List[int]
"""
if len(nums) == 0:
return []
cand1, cand2 = nums[0], nums[0]
num1, num2 = 0, 0
result = []
for i in nums:
if cand1 == i:
num1 += 1
continue
if cand2 == i:
num2 += 1
continue
if num1 == 0:
cand1 = i
num1 += 1
continue
if num2 == 0:
cand2 = i
num2 += 1
continue
num1 -= 1
num2 -= 1
print("can1:{0}, can2:{1}".format(cand1, cand2))
if cand1 == cand2:
return [cand1]
num1, num2 = 0, 0
for i in nums:
if cand1 == i:
num1 += 1
if cand2 == i:
num2 += 1
if num1 > len(nums)/3:
result.append(cand1)
if num2 > len(nums)/3:
result.append(cand2)
return result
if __name__ == "__main__":
nums = [1]
result = Solution().majorityElement(nums)
print(result)
执行效率在70%吧!