【算法】摩尔投票算法

目录

  • 1.概述
  • 2.算法思想
  • 3.代码实现
    • 3.1.t = ⌊n / 2⌋
    • 3.2.t = ⌊n / 3⌋
    • 3.3.t = ⌊n / (m + 1)⌋
  • 4.应用

参考:LeetCode_多数元素 II 题解

1.概述

(1)摩尔投票法 (Boyer–Moore Majority Vote Algorithm) 是一种用来寻找一组元素中多数元素的常量级空间复杂度算法。一般来说,摩尔投票法常用于求众数,求众数这个问题本身比较简单,但是想要使用常量级空间复杂度来实现却不是那么简单。而摩尔投票法正是这样一种算法。

(2)众数 (Mode) 是指在统计分布上具有明显集中趋势点的数值,代表数据的一般水平。 也是一组数据中出现次数最多的数值,有时众数在一组数中有好几个。用 M 表示。上面提到的多数元素与众数的含义差不多,只不过在摩尔投票法算法中,多数元素是指在数组(长度为 n)中出现次数大于某个阈值 (t) 的元素,并且存在如下结论:

  • 如果 t = ⌊n / 2⌋,那么多数元素最多只有 1 个;
  • 如果 t = ⌊n / 3⌋,那么多数元素最多只有 2 个;
  • 如果 t = ⌊n / (m + 1)⌋,那么多数元素最多只有 m 个;

2.算法思想

(1)摩尔投票法的核心思想为对拼消耗。首先我们考虑最基本的摩尔投票问题,比如找出一组数字序列中出现次数大于 ⌊n / 2⌋ 的数字(并且假设这个数字一定存在)。我们可以直接利用反证法证明这样的数字只可能有一个:

  • 假设我们当前数组中存在次数大于总数一半的元素为 x,数组的总长度为 n,则我们可以把数组分为两部分:
    • 一部分为相同的 k 个元素 x;
    • 另一部分为 n − k 2 \frac{n - k}{2} 2nk 对个不同的元素配对,
  • 此时我们假设还存在另外一个次数大于总数一半的元素 y,则此时 y 因该满足 y > n 2 \frac{n}{2} 2n ,但是按照我们之前的推理 y 应当满足 y ≤ n − k 2 \frac{n - k}{2} 2nk ,二者自相矛盾。

因此,对于 t = ⌊n / 2⌋,摩尔投票算法的核心思想是基于这个事实:每次从序列里选择两个不相同的数字删除掉(或称为抵消),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个元素。

(2)对于 t = ⌊n / 3⌋,我们可以利用反证法推断出满足这样条件的元素最多只有两个,同理,我们可以利用摩尔投票法的核心思想,每次选择三个互不相同的元素进行删除(或称为「抵消」),推导思路如下:

  • 假设数组中一定只存在一个次数大于 ⌊ n 3 \frac{n}{3} 3n⌋ 的元素 x,则此时我们可以把数组分成两部分:一部分相同的 k 个元素 x,另一部分为 n − k 3 \frac{n - k}{3} 3nk 组三个不同的元素,我们知道三个不同的元素会被抵消,因此最终只会剩下一个元素为 x。
  • 如果只存在 2 个次数大于 ⌊ n 3 \frac{n}{3} 3n⌋ 的元素时,假设这两个不同的元素分别为 x 和 y,则此时我们一定可以把数组分成三部分:第一部分相同的 m 个元素 x,第二部分相同的 k 个元素 y,第三部分为 n − m − k 3 \frac{n - m - k}{3} 3nmk 组三个互不同的元素,我们知道三个互不同的元素会被抵消,因此最终只会剩下两个元素为 x 和 y。

具体思路如下:

  • 我们每次检测当前元素是否为第一个选中的元素或者第二个选中的元素;
  • 每次我们发现当前元素与已经选中的两个元素都不相同,则进行抵消一次;
  • 如果存在最终选票大于 0 的元素,我们还需要再次统计已选中元素的次数,检查元素的次数是否大于 ⌊ n 3 \frac{n}{3} 3n⌋。

(3)对于 t = ⌊n / (m + 1)⌋,我们可以利用同样的方法推断出满足这样条件的元素最多只有 m 个。但是需要注意的是,此时算法时间复杂度为 O(n * m),空间复杂度为 O(m)

3.代码实现

3.1.t = ⌊n / 2⌋

class Solution {public int majorityElement(int[] nums) {//设 candidate 为出现次数大于 ⌊n / 2⌋ 的元素int candidate = 0;int cnt = 0;//遍历数组 numsfor (int num : nums) {if (cnt == 0) {//重新确定选举人candidate = num;}if (num == candidate) {//candidate 的票数加一cnt++;} else {//对拼消耗,即相当于 candidate 的票数减一cnt--;}}return candidate;}
}

① 上述算法的时间复杂度为 O(n),空间复杂度为 O(1)。
② 有关 t = ⌊n / 2⌋ 的摩尔投票算法的具体细节,可以参考本题官方题解。

3.2.t = ⌊n / 3⌋

class Solution {public List<Integer> majorityElement(int[] nums) {//满足题目条件的元素最多只有两个,我们设为 ele1 和 ele2int ele1 = 0;int ele2 = 0;//设 vote1 和 vote2 分别为 ele1 和 ele2 的赞成票数int vote1 = 0;int vote2 = 0;for (int num : nums) {if (vote1 > 0 && num == ele1) {//num 为第一个元素,则票数加 1vote1++;} else if (vote2 > 0 && num == ele2) {//num 为第二个元素,则票数加 1vote2++;} else if (vote1 == 0) {//选择第一个元素ele1 = num;vote1++;} else if (vote2 == 0) {//选择第二个元素ele2 = num;vote2++;} else {//三个元素 ele1、ele3、num 互不相同,则其票数减 1,即对拼消耗vote1--;vote2--;}}//cnt1 和 cnt2 分别记录元素 ele1 和 ele2 出现的次数int cnt1 = 0;int cnt2 = 0;for (int num : nums) {if (vote1 > 0 && num == ele1) {cnt1++;}if (vote2 > 0 && num == ele2) {cnt2++;}}//检查元素出现的次数是否满足要求List<Integer> res = new ArrayList<>();if (vote1 > 0 && cnt1 > nums.length / 3) {res.add(ele1);} if (vote2 > 0 && cnt2 > nums.length / 3) {res.add(ele2);}return res;}
}

上述算法的时间复杂度为 O(n),空间复杂度为 O(1)。

3.3.t = ⌊n / (m + 1)⌋

class Solution {public static List<Integer> majorityElements(int[] nums, int m) {//存储多数元素的集合List<Integer> res = new ArrayList<>();//候选元素数组int[] candidates = new int[m];//对应候选元素的计数器数组int[] counts = new int[m];for (int num : nums) {//如果 num 存在于候选元素数组中,则将对应计数器加 1for (int i = 0; i < m; i++) {if (candidates[i] == num) {counts[i]++;break;}//如果 num 不在候选元素数组中且有空位置(计数器为 0),则将 num 加入候选元素数组if (candidates[i] == 0 && counts[i] == 0) {candidates[i] = num;counts[i]++;break;}}//如果候选元素数组已满,则将所有计数器减 1if (counts[m - 1] != 0) {for (int i = 0; i < m; i++) {counts[i]--;}}}//验证候选元素的计数器是否大于阈值for (int i = 0; i < m; i++) {int count = 0;for (int num : nums) {if (num == candidates[i]) {count++;}}if (count > nums.length / (m + 1)) {res.add(candidates[i]);}}return res;}
}

4.应用

大家可以去 LeetCode 上找相关的 Boyer-Moore 投票算法的题目来练习,或者也可以直接查看 LeetCode 算法刷题目录 (Java) 这篇文章中的 Boyer-Moore 投票算法章节。如果大家发现文章中的错误之处,可在评论区中指出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/164804.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flutter,uni-app开发调试ios

一、申请ios开发者账号 二、ios开发者配置 ios 开发者需要配置的地方 https://developer.apple.com/account/resources/certificates/list Certificates&#xff08;证书&#xff09;: 作用&#xff1a; 证书用于对应用程序和开发者进行身份验证&#xff0c;确保安全性和可…

如何为您的企业选择合适的多因素认证?

在传统的网络安全架构中&#xff0c;重点在于防止非法入侵&#xff0c;例如防火墙、VPN 、堡垒机等安全设备的重心都在于防止用户违规访问企业资源&#xff0c;一旦合法用户的账号密码被入侵者拿到&#xff0c;就可以冒充合法用户访问企业资源&#xff0c;所有的安全设备形同虚…

springcloud超市管理系统源码

技术说明&#xff1a; jdk1.8&#xff0c;mysql5.7&#xff0c;idea&#xff0c;vscode springcloud springboot mybatis vue elementui mysql 功能介绍&#xff1a; 后台管理&#xff1a; 统计分析&#xff1a;查看用户&#xff0c;商品&#xff0c;销售数量&#xff1b;…

Mock 数据

1. Mock 数据的方式 2. json-server 实现 Mock 数据 项目中安装json-server npm i -D json-server准备一个json文件添加启动命令 //package.json"scripts": {"start": "craco start","build": "craco build","test&q…

简单聊聊加密和加签的关系与区别

大家好&#xff0c;我是G探险者。 平时我们在项目上一定都听过加密和加签&#xff0c;加密可能都好理解&#xff0c;知道它是保障的数据的机密性&#xff0c;那加签是为了保障啥勒&#xff1f;它和加密有啥区别&#xff1f; 带着这个疑问&#xff0c;我们就来聊聊二者的区别。…

SHEIN出口车钥匙扣REACH认证指南解析

钥匙扣的材料一般为金属、皮革、塑料、橡胶、木头等。此物精致小巧、造型千变万化是人们随身携带的日常用品。钥匙扣是挂在钥匙圈上的一种装饰物品。钥匙扣出口需要办理REACH认证。 一、什么是REACH认证&#xff1f; REACH认证是欧盟28个成员国对进入其市场的所有化学品,&…

centos7中通过minikube安装Kubernetes

minikube是一款开源的Kubernetes集群管理器&#xff0c;它可以帮助您在本地计算机上轻松部署和管理Kubernetes集群。以下是minikube的安装和使用步骤&#xff1a; 安装Docker&#xff1a;如果您还没有安装Docker&#xff0c;可以从Docker官方网站上下载并安装适合您操作系统的…

Android和iOS应用程序加固方法详解:混淆、加壳、数据加密、动态加载和数字签名实现

目录 Android和iOS应用程序加固方法详解&#xff1a;混淆、加壳、数据加密、动态加载和数字签名实现 APP 加固方式 iOS APP加固代码实现 打开要处理的IPA文件 设置签名使用的证书和描述文件 开始ios ipa重签名 APP 加固方式 iOSAPP 加固是优化 iOS安全性的一种方法&…

C#枚举的使用

在C#中经常会用到枚举&#xff0c;是比较常用的定义一组常量集合的数据类型。我们使用枚举可以更方便理解和阅读代码&#xff0c;增强代码可读性&#xff0c;也在某种程度上提升了编程逻辑和维度。 基本语法&#xff1a; enum MyEnum {Value1,Value2,Value3&#xff0c;//...…

CSS 实现文本框签名

<div class"textarea-prepend"><textarea rows"6" placeholder"请输入消息内容"></textarea></div>.textarea-prepend {position: relative;}.textarea-prepend textarea {width: 300px;}.textarea-prepend::before {ba…

UE4基础篇十三:物理

一、笔记记录 1.1 碰撞交互 阻挡会设置为阻挡的两个(或更多)Actor之间自然发生。但是,需要启用模拟生成命中事件(Simulation Generates Hit Events)才能执行事件命中 ,要两个都相互设置阻挡模式才会生成命中事件 将Actor设置为重叠往往看起来它们彼此忽略,如果没有生…

【陈老板赠书活动 - 18期】-如何成为架构师这几本书推荐给你

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;赠书活动专栏&#xff08;为大家争取的福利&#xff0c;免费送书&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&am…

JavaScript基础—引入方式、注释和结束符、输入和输出、变量、常量、数据类型、检测数据类型、类型转换、综合案例—用户订单信息

版本说明 当前版本号[20231123]。 版本修改说明20231123初版 目录 文章目录 版本说明目录JavaScript 基础 - 第1天介绍引入方式内部方式外部形式 注释和结束符单行注释多行注释 结束符输入和输出输出输入 变量声明赋值变量初始化更新变量 关键字变量名命名规则 常量数据类型…

什么是指针碰撞

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

WorkPlus实现完全私有化部署,企业数据安全有保障

在这个信息化飞速发展的时代&#xff0c;企业正面临着越来越多的数据安全挑战。为了确保数据的安全性和隐私性&#xff0c;WorkPlus迎合市场需求&#xff0c;推出了完全私有化部署方案&#xff0c;为企业提供了全面、可靠的安全保障&#xff0c;成为企业移动办公的首选。 WorkP…

C#中的迭代器和分部类

目录 一、迭代器 1.示例源码 2.生成效果&#xff1a; 二、分部类 1.示例源码 2.生成效果 迭代器在集合类中经常使用&#xff0c;而分部类则提供了一种将一个类分成多个类的方法&#xff0c;这对于有大量代码的类非常实用。 一、迭代器 迭代器是可以返回相同类型的值的有…

LeetCode216. Combination Sum III

文章目录 一、题目二、题解 一、题目 Find all valid combinations of k numbers that sum up to n such that the following conditions are true: Only numbers 1 through 9 are used. Each number is used at most once. Return a list of all possible valid combination…

《微信小程序开发从入门到实战》学习二十五

3.3 开发创建投票页面 3.3.13 使用页面路径参数 写了很多重复代码&#xff0c;现在想办法将多选和单选投票页面合二为一。 将单选页面改造作为单选多选共同页面。 修改index.js中的代码&#xff0c;将路径都跳转到第一个单选页面&#xff0c;带上单选或多选的标志&#xff…

阿里云 E-MapReduce 全面开启 Serverless 时代

作者&#xff1a;李钰 - 阿里云资深技术专家、EMR 负责人 EMR 2.0 平台 阿里云正式发布云原生开源大数据平台EMR 2.0已历经一年时间&#xff0c;如今EMR 2.0全新平台在生产上已经全面落地&#xff0c;资源占比超过60%。EMR 2.0平台之所以在生产上这么快落地&#xff0c;源于其…

EPT-Net:用于3D医学图像分割的边缘感知转换器

EPT-Net: Edge Perception Transformer for 3D Medical Image Segmentation EPT-Net&#xff1a;用于3D医学图像分割的边缘感知转换器背景贡献实验方法Dual Positional Transformer&#xff08;双位置Transformer&#xff09;Learnable Patch EmbeddingVoxel Spacial Positiona…