介绍算法之前, 我们来看一个场景, 假设您有一个未排序的列表。您想知道列表中是否存在一个数量占列表的总数一半以上的元素, 我们称这样一个列表元素为 Majority 元素.如果有这样一个元素, 求出它?如果没有,你需要知道没有。你想要尽可能高效地完成这个工作。

这个问题的一个常见场景可能是容错计算。您执行多个冗余计算,然后验证大多数结果是否一致。

Boyer-Moore Majority Vote Algorithm

算法描述

Boyer-Moore 算法在 Boyer-Moore Majority Vote Algorithm 中提出。该算法使用 O(1)额外空间和 O(N)时间。它只需要遍历输入列表中2遍。实现这一点也很简单,虽然有点麻烦来了解它的工作原理。

第一次遍历列表,会生成 Majority 元素的候选值。 第二遍只是计算该元素的频数以确认是否是 Majority 元素。第一遍是有趣的部分。

在第一遍,我们需要2个值:

候选值 candidate,初始设置可以为任何值。
一个计数器 count,初始设置为0。
对于输入列表中的每个元素,我们首先检查计数值。如果计数等于0,我们将候选值设置为当前元素的值。接下来,首先将元素的值与当前候选值进行比较。如果它们相同,我们将计数加1.如果它们不同,我们计数减1。

在python:


candidate = 0
count = 0

//// 第 1 次遍历
for value in List:
  if count == 0:
    candidate = value
  if candidate == value:
    count += 1
  else:
    count -= 1

// 第 2 次遍历
Majority = num.count(candidate)
if (Majority > List.size/2):
  return Majority
    

第 1 遍遍历结束时,如果存在 Majority,候选值将是 Majority。第二次遍历可以验证候选值 candidate 是否是 Majority。

算法解析

为了看这是如何工作的,我们只需要考虑包含 Majority 的情况。如果该列表不包含 Majority 元素,则第二次遍历会轻易地拒绝该候选者。

首先,考虑第一个元素不是 Majority 元素的列表,例如,Majority为 0 的列表:

[5,5,0,0,0,5,0,0,5]

当处理第一个元素时,我们将 5 分配给候选值,计数器初始化为 1。由于 5 不是 Majority,在遍历到列表的最后一个元素之前的某个时刻,count将会下降到 0。在上面的例子中,这发生在第 4 个元素:

列表值:
[5,5,0,0,...

计数值:
[1,2,1,0,...

在计数返回到 0 的时候,我们已经消耗了和元素 5 相同数量的其他元素。如果所有其他元素都是这种情况下的 Majority 元素,那么我们已经消耗了2个Majority 元素和2个非Majority元素。这是我们可以消费的最多的Majority元素,但即使这样, Majority 元素仍然是输入列表剩余部分的大部分(在我们的示例中,余数为... 0,5,0,0, 5])。

我们可以看到,如果第一个元素是多数元素,并且在某个时间点计数器下降到 0,那么我们还可以看到,多数元素仍然是输入列表剩余部分的大部分,因为我们再次消耗了相等数的Majority元素和非Majority元素。

这反过来证明了当列表前面候选值计数器降到 0 时, 会被丢弃,而不会影响算法的第一遍的最终结果。我们可以一遍又一遍地重复地抛弃我们前面输入的范围,直到找到一个范围,该范围是我们输入的后缀,其中count不会下降到0。

给定一个输入列表后缀,其中 count 从不下降到 0,我们必须有更多的值等于第一个元素,而不是不值的值。因此,第一个元素(候选值)必须是该列表的 Majority,并且是输入列表中的 Majority 的唯一可能的候选值,尽管仍然可能没有 Majority。

Majority Element II

此题来自 leetcode 229 Majority Element II

问题描述:

Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorithm should run in linear time and in O(1) space.

给定一个大小为 n 的整数数组,找到所有出现超过 ⌊n / 3⌋次的元素。该算法应在 线性时间 O(n) 和 额外的 O(1)空间复杂度

思路: 首先就是看看列表中最多会有几个这样的 Majority 元素, 由于这里的 Majority 元素的为频数大于 ⌊n / 3⌋, 所以这样的元素最多有 2 个. 下面是 Most voted Solution, 灵活的使用的了 Boyer-Moore Voted Algorithm.

class Solution:

def majorityElement(self, nums):
    if not nums:
        return []
    count1, count2, candidate1, candidate2 = 0, 0, 0, 1

    // 第一轮循环
    for n in nums:
        if n == candidate1:
            count1 += 1
        elif n == candidate2:
            count2 += 1
        elif count1 == 0:
            candidate1, count1 = n, 1
        elif count2 == 0:
            candidate2, count2 = n, 1
        else:
            count1, count2 = count1 - 1, count2 - 1
    // 第二轮循环
    res = [n for n in (candidate1, candidate2) if nums.count(n) > len(nums) // 3]

    return res                    

上述代码第一轮循环之后, 会选出 2 个 Majority 候选值, 这会有 3 种情况:

  1. 列表没有 Majority 元素, 那么第一轮选出的候选值在第二轮都会被淘汰
  2. 列表只有 1 个 Majority 元素, 那么第一轮选出的 2 个候选值会有 1 个在第二轮都会被淘汰
  3. 列表有 2 个 Majority 元素, 那么第一轮选出的 2 个候选值都是 Majority, 第二轮自然没法淘汰任何一个

上面第 1 种情况中, 由于列表没有 Majority 元素, 自然没有元素的频数大于 ⌊n / 3⌋, 第二轮 2 个候选值都会被淘汰. 难缠的是第 2 种情况, 所以放在最后, 下面先说第 3 种情况

第 3 种情况, 列表中有 2 个 Majority 元素, 两个加起来的数量至少是列表元素的 2/3 以上, 按照 Boyer-Moore Voted Algorithm 中基本概念是 “让我们取消彼此的投票", 所以列表其他元素总数少于总数的 1/3, 所以不可能否决 2 个 Majority 元素中的任何一个, 自然选出来的元素就是 Majority 元素. 下面来说说第二种情况

第 2 种情况中, 列表只有 1 个 Majority 元素, 数量是超过列表元素的 1/3, 那么这时候列表中其他元素总数可能会大于 Majority 元素, 那我们选择的候选值中会包括 Majority 元素吗? 答案是肯定的, 那么我们看看为什么是这样.

为了便于说明, 我们基于一个观察将问题等价转化为一个便于描述的情况. 我们观察到, 给予你一个列表 A, 如果该列表有 Majority 元素, 那么改变列表中元素的排列顺序得到列表 B, 那么 列表 A 的 Majority 元素与列表 B 相同, 当然这是个显而易见的, 因为我们判断 Majority 元素的依据是元素出现的频数.

既然如此, 列表只有 1 个 Majority 元素, 我们假设列表的 Majority 元素都排在列表的开始位置, 那么非 Majority 元素想要投票否决 Majority 元素需要超过列表 1/3 的数量, 这乍看起来是有可能的, 不过实际上是不可能的, 由于我们选举了 2 个候选值, 因为上面 if/else 选择结构的逻辑是当输入元素不是2个候选值中的任何一个且2个候选值的计数器都不为1时, 才会将2个候选值的计数器减 1, 也就是说只有 1 个 Majority 元素的列表的另一个候选值是 非Majority 元素, 并且会被其他非Majority 元素投票否决. 我们可以假设 列表除去头部的 Majority 元素, 其他的元素都不相等, 当遍历列表元素到第一个非Majority 元素时, 元素别选举为候选值这时Majority 元素的计数器不变), 然后又被否决(这时Majority 元素的计数器也会减去1), 依次进行下去, 总数最多不超过 2n/3 - 2 的非Majority元素一般当选了候选值, 一半用来否决, 二者一半总数不会超过 n/3 -1, 没法否决掉总数超过 n/3 的Majority元素, 所以最终 Majority 元素会在候选值中, 第二轮循环被选出.


摘自 多数投票算法

Boyer-Moore Majority Vote Algorithm的更多相关文章

  1. Moore majority vote algorithm(摩尔投票算法)

    Boyer-Moore majority vote algorithm(摩尔投票算法) 简介 Boyer-Moore majority vote algorithm(摩尔投票算法)是一种在线性时间O( ...

  2. Boyer and Moore Fast majority vote algorithm(快速选举算法)

    问题来来自于leetcode上的一道题目,https://leetcode.com/problems/majority-element/,大意是是找出一个数组中,出现次数超过一个半的数字,要求是O(n ...

  3. LeetCode 169. Majority Element - majority vote algorithm (Java)

    1. 题目描述Description Link: https://leetcode.com/problems/majority-element/description/ Given an array ...

  4. A Linear Time Majority Vote Algorithm

    介绍一种算法,它可以在线性时间和常数空间内,在一个数组内找出出现次数超过一半的某个数字. 要解决这个问题并不难,可以使用排序或哈希,但是这两种算法都不能同时满足时间或空间的要求. 然而,该算法(A L ...

  5. 算法复习_线性时间求解Majority Vote Algorithm问题

    题目来源于Leecode上的Majority Element问题 Majority Element:在一个序列中出现了至少n/2的下界次 使用排序算法取中位数则需要Nlogn http://www.c ...

  6. leetcode 169. Majority Element 多数投票算法(Boyer-Moore Majority Vote algorithm)

    题目: Given an array of size n, find the majority element. The majority element is the element that ap ...

  7. Leetcode OJ : Implement strStr() [ Boyer–Moore string search algorithm ] python solution

    class Solution { public: int strStr(char *haystack, char *needle) { , skip[]; char *str = haystack, ...

  8. Boyer–Moore (BM)字符串搜索算法

    在计算机科学里,Boyer-Moore字符串搜索算法是一种非常高效的字符串搜索算法.它由Bob Boyer和J Strother Moore设计于1977年.此算法仅对搜索目标字符串(关键字)进行预处 ...

  9. Boyer Moore算法(字符串匹配)

    上一篇文章,我介绍了KMP算法. 但是,它并不是效率最高的算法,实际采用并不多.各种文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法. Boyer-Mo ...

随机推荐

  1. 本地jar上传到本地仓库

    转自:http://www.blogjava.net/fancydeepin/archive/2012/06/12/380605.html   thanks!! Maven 确确实实是个好东西,用来管 ...

  2. js实现类似iphone的秒表-添加平均数功能

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  3. 详解react/redux的服务端渲染:页面性能与SEO

        亟待解决的疑问 为什么服务端渲染首屏渲染快?(对比客户端首屏渲染)   react客户端渲染的一大痛点就是首屏渲染速度慢问题,因为react是一个单页面应用,大多数的资源需要在首次渲染前就加载 ...

  4. vijos1056题解

    题目: 桌面上放了N个平行于坐标轴的矩形,这N个矩形可能有互相覆盖的部分,求它们组成的图形的面积. 在翻题目时,偶然发现了这道标号为WA的题目. 原来,以前我把一中培训的代码发了上去,却WA了4个点, ...

  5. 面向对象设计模式——观察者(OBSERVER)模式

    定义 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新.  Observer模式描述了如何建立这种关系.这一模式中的关键对象是目标(subject ...

  6. Java之字符串String,StringBuffer,StringBuilder

    String类: String类即字符串类型,并不是Java的基本数据类型,但可以像基本数据类型一样使用,用双引号括起来进行声明.在Java中用String类的构造方法来创建字符串变量. 声明字符串: ...

  7. Java后端开发书架

    本人摘录于江南白衣文章,文章地址:http://calvin1978.blogcn.com/articles/javabookshelf.html 书架主要针对Java后端开发. 3.0版把一些后来买 ...

  8. 【微信小程序开发教程】如何显示群名称?

    今年 5 月份的时候,微信宣布:「为了更好的针对群场景提供个性化服务,当用户在群聊中点击小程序分享卡片时,小程序支持开发者获取群 ID 和群名称」.但随后没多久,发现小程序只返回了群 ID,并没有给我 ...

  9. Java 9 揭秘(15. 增强的弃用注解)

    Tips 做一个终身学习的人. 主要介绍以下内容: 如何弃用API @deprecate Javadoc标签和@Deprecation注解在弃用的API中的角色 用于生成弃用警告的详细规则 在JDK ...

  10. ASP.NET MVC Bundles 用法和说明(打包javascript和css)

    本文主要介绍了ASP.NET MVC中的新功能Bundles,利用Bundles可以将javascript和css文件打包压缩,并且可以区分调试和非调试,在调试时不进行压缩,以原始方式显示出来,以方便 ...