• Majority Element


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

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

    Majority Element

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

    算法描述

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

    在第一次遍历列表时,我们需要记录 2 个值:

    • candidate, Majority候选值, 可初始化为任何值。
    • count, 候选值净获取的投票计数(总支持-总反对票), 初始化为0。

    算法流程
    对于输入列表中的每个元素,我们首先判断当前元素是不是等于候选值。

    1. 如果当前元素等于候选值, 计数器加1, continue
    2. 如果当前元素不等与候选值, 检查计数器
      • 如果计数器等于0, 令当前元素为候选值, 计数器设置为1, continue
      • 如果计数器不为0, 计数器减一, continue
    def majority(nums):
        candidate = 0
        cnt = 0
        
        # 第 1 次遍历
        for value in nums:
            if candidate == value:
                cnt += 1
            elif cnt != 0:
                cnt -= 1
            else:
                candidate, cnt = value, 1
                
    
        # 第 2 次遍历
        maj = nums.count(candidate)
    
        if maj > len(nums) / 2:
            return maj
    

    第 1 次遍历结束时,如果存在 Majority,候选值将是 Majority。
    第 2 次历可以验证候选值 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])。

    (color{red}{关键思想})

    如果第一个元素是 Majority 元素,并且在某个时间点计数器下降到 0,那么我们还可以看到,多数元素仍然是输入列表剩余部分的大部分,因为我们消耗了相等数的 Majority 元素和非 Majority 元素。我们可以一遍又一遍地重复地抛弃我们前面输入的范围,直到找到一个范围,该范围是以 Majority 元素开头且 count 计数器不会降到 0.

    如果第一个元素不是 Majority 元素, 那么该元素可能消耗 Majority 也可能消耗非 Majority 元素, 那么多数元素仍然是输入列表剩余部分的大部分

    (color{red}{所以, 列表中的元素的排列顺序不影响我们算法执行的最终结果.}), 不妨假设 Majority 元素都排在列表的首部

    -w764

    因此,如果 Majority 如果存在, 候选值必须是该列表的 Majority,并且是输入列表中的 Majority 的唯一可能的候选值.

    Majority Element II

    给定一个大小为 n 的整数数组,找到所有出现次数超过 (lfloor frac{1}{3} floor) 次的元素。该算法在线性时间 O(n) 和额外的 O(1) 空间复杂度内完成计算

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

    def majority(nums):
        if not nums:
            return []
        
        # 这里 candidate1 和 candidate2 设置为不同值为了
        # 防止Majority 就等于 candidate1 或 candidate2, 
        # 那么如果只有一 Majority, 那么却返回两个相同的, 所以要初始化为不同值
        count1, count2, candidate1, candidate2 = 0, 0, 0, 10
    
        # 第一轮循环
        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  # 1个同时否决两个, 很重要
        
        # 第二轮循环
        res = [n for n in (candidate1, candidate2) if nums.count(n) > len(nums) // 3]
    
        return res                    
    

    共有有 3 种情况:

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

    第 1 种情况

    由于列表没有 Majority 元素, 自然没有元素的频数大于 (lfloor frac{1}{3} floor), 第二轮 2 个候选值都会被淘汰. 难缠的是第 2 种情况, 所以放在最后, 下面先说第 3 种情况

    第 2 种情况

    如果只有 1 个 Majority 元素,

    列表只有 1 个 Majority 元素, 数量是超过列表元素的 (frac{1}{3}), 那么非 Majority 元素的数量可能大于总数的 (frac{1}{2}), 那我们选择的候选值中会包括 Majority 元素吗?

    假设非 Majority 元素不会相互取消投票, 所有的非 Majority 元素都用来否决 Majority 的投票. 那么非 Majority 在否决 Majority 的同时也会否决另一个 非 Majority, 也就是说想要否决超过总数 (frac{1}{3}) 的 Majority 需要超过总数 (frac{2}{3}) 的非 Majority 元素, 这是不可能的, 所以 Majority 元素一定会成为候选者之一.

    在第二轮循环中, 通过奇数排除 非Majority元素, 搞定

    第 3 种情况

    首先, 我们先考虑会不会出现两个 Majority 相互取消彼此投票的可能 ?

    如果有两个 Majority 元素, 那么非 Majority 元素的数量少于总数的 (frac{1}{3})
    假设两个 Majority 元素分别为 Majority A 和 Majority B, Majority A 为当前候选值之一, 如果 Majority B 要取消 Majority A 的投票, 那么一定还会取消另一个非 Majority 元素的投票, 又由于 非 Majority 元素的数量少于总数的 (frac{1}{3}), 那么最后 Majority B 一定会成为候选者之一.

    (候选者还会取消彼此的投票, 这时候更不可能有非 Majority 元素成为候选者之一)


  • 相关阅读:
    sem_timedwait
    netty
    嵌入式系统设计师
    新知识点
    20228
    20227
    mdbook
    optparse模块使用
    TiDB集群销毁删除
    configparser模块的使用
  • 原文地址:https://www.cnblogs.com/nowgood/p/9767939.html
Copyright © 2020-2023  润新知