摩尔投票法——1比1火拼 2020-03-17(未经允许,禁止转载)

摩尔投票法的基本问题

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。假设该数组中必然存在多数元素
这是leetcode169. 多数元素

最常规的思路是,遍历+计数,但这样我们需要维护一个哈希表或者字典。于是我们看看摩尔投票法能发挥什么作用

摩尔投票法的原理

5个字:1比1火拼
我们形象地将数组中的不同元素视为不同国家,各国的人数等于对应元素的个数。如[1,2,0,2,4,2,3,2,1,2,2],视为国0,国1,国2(众数国),国3,国4,人数分别为1,2,6,1,1。我们在这些国家间燃起战火,让它们相互乱战1比1火拼,最坏的情况就是其他国合众连横合起来打你众数国,显然这就是看谁人多呗,人数最多的国2会获胜。这就是摩尔投票法
维护2个变量——存活人数和存活元素。依次扫描数组中的元素,如果当前扫描到的元素与上一扫描元素相同,说明属于同一势力,存活人数+1;如果当前扫描到的元素与上一扫描元素不同,则两两火拼,存活人数-1。当扫描结束后,存活元素存放的值就是这个多数元素

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        if nums:
            count = 0
            ele = 0
            for num in nums:
                if count == 0:
                    ele = num
                if num == ele:
                    count += 1
                else:
                    count -= 1
            return ele

进阶

思考:能用摩尔投票解决找出数组中出现次数大于1/3长度的数字吗?(假设一定存在至少一个这样的数,当然,至多不超过两个)
分析:
在上面使用摩尔投票法找出出现次数大于1/2长度的众数中,我们可以看到在最坏情况下,每一个众数都抱着一个其他的数同归于尽,最后才能够以量多取胜
以此类推,当试图使用摩尔投票法找出出现次数大于1/3长度的数时,需要3个不同的数一组同归于尽。最坏的情况是,一直都是国家X的一个人拼掉其他两个国家各一人,若国X最后有人剩下,则为所求,代码如下:

def vote(nums):
    m,n = 0,0
    cm,cn = 0,0
    for i in nums:
        # 先判断当前扫描到的数是否与m或n相等
        if i == m: cm += 1
        elif i == n: cn += 1
        # 不等,再判断有没有计数为0的
        elif cm == 0: m = i; cm += 1
        elif cn == 0: n = i; cn += 1                
        # 否则就是遇到了第三个数,计数都减1
        else: cm -= 1; cn -= 1
        print('当前遍历至 %s' % i)
        print('m is %s, m数量是%s; n is %s, n数量是%s\n' % (m, cm, n, cn))
    return m,n

那么这样就全对了吗?非也
诚然,返回的m, n中至少有一个确实满足要求,但另一个就不见得了。举个例子,nums = [1,2,3,1,2,3,2,4],执行上面的代码,返回是(4, 2),显然4是不对的。看一下打印日志:

当前遍历至 1
m is 1, m数量是1; n is 0, n数量是0

当前遍历至 2
m is 1, m数量是1; n is 2, n数量是1

当前遍历至 3
m is 1, m数量是0; n is 2, n数量是0

当前遍历至 1
m is 1, m数量是1; n is 2, n数量是0

当前遍历至 2
m is 1, m数量是1; n is 2, n数量是1

当前遍历至 3
m is 1, m数量是0; n is 2, n数量是0

当前遍历至 2
m is 1, m数量是0; n is 2, n数量是1

当前遍历至 4
m is 4, m数量是1; n is 2, n数量是1

我们可以看到,前面2组[1,2,3]都互相火拼掉了,最后剩下2和4
2属于一直都在战斗然后活下来的国家,就是我们要求的数;但4从头到尾没参加过一次战斗,纯粹就是坐收渔翁之利苟到最后

因此,1/2与1/3的差别就在于,是否“每个国家都加入了战斗”。只有确保每个国家都加入了战斗,活下来那个才是人多的,而不是苟活的!

因此,我们要检查得到的这两个数,再遍历一次,统计它们出现的次数。。。

class Solution:
    def majorityElement(self, nums: List[int]) -> List[int]:
        res = list()
        m, n = 0, 0
        cm, cn = 0, 0
        for i in nums:
            if i == m:
                cm += 1
            elif i == n:
                cn += 1
            elif cm == 0:
                m = i
                cm += 1
            elif cn == 0:
                n = i
                cn += 1
            else:
                cm -= 1
                cn -= 1
        # 再次遍历检验
        cm = cn = 0
        for i in nums:
            if m == i:
                cm += 1
            elif n == i:
                cn += 1
        # print(m, n)
        # print(cm, cn)
        if cm >= len(nums)//3 + 1:
            res.append(m)
        if cn >= len(nums)//3 + 1:
            res.append(n)
        return res
        
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容