算法专题:投票法

news2024/11/27 12:49:39

文章目录

    • 169.多数元素(找频率>n/2,且多数元素一定存在)
      • 思路
      • 完整版
      • 补充:
      • 注意点
    • 面试题 17.10. 主要元素(找频率>n/2,但多数元素不一定存在)
      • 思路
      • 完整版
    • 229.多数元素Ⅱ(找频率>n/3)
      • 思路
      • 最开始的写法
      • 修改完整版
      • debug测试:解答错误
    • 总结:找频率>n/3元素与找>n/2元素的区别

看这篇总结:【算法】摩尔投票法 找 多数元素_小威W的博客-CSDN博客

169.多数元素(找频率>n/2,且多数元素一定存在)

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -10^9 <= nums[i] <= 10^9

思路

这道题目是找出现频率高于n/2的元素,是投票法。

投票法(Boyer-Moore Voting Algorithm)是一种用于在数组中查找主要元素的算法,主要元素定义为一个元素出现次数超过数组长度的一半。它并不一定能找到频率最高的元素,例如在数组 [1, 2, 2, 3, 3, 3] 中,频率最高的元素是 3,但没有元素出现次数超过数组长度的一半,因此投票法不会返回任何元素。如果数组 [1, 1, 2, 2, 3, 3, 3, 3] 中,频率最高的元素也是主要元素,这时投票法会返回元素 3

如果一个元素是数组的多数元素(出现次数超过数组长度的一半),那么即使我们把它和其他每个不同的元素一一抵消(每次都从数组中删除两个不同的元素),最后剩下的一定是这个多数元素

  • 基本的投票法是找**数组中出现频率超过半数(必须是超过不能是等于)**的元素。
  • 数组中出现频率超过半数的元素,一定只有一个

完整版

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int ans=-1,count=0;
        for(int num:nums){
            if(count==0) ans = num;
            //这样写的话,else会和直接相邻的上一个if组成if-else
            if(num==ans) count++;
            else count--;
        }
        return ans;//注意ans就是多数元素!
    }
};

补充:

在C++中,else语句总是与最近的一个未配对的if语句进行配对。因此,上面写法中,else语句是与第二个if语句配对的,形成了一个if-else结构。

上面是比较直观的写法,也可以换成另一种逻辑更清晰的:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int ans=-1,count=0;
        for(int num:nums){
            if(count==0) ans = num;
            count += (num==ans)?1:-1;     
        }
        return ans;//注意ans就是多数元素!
    }
};

注意点

  • ans就是多数元素,因为只要count没有抵消到0ans就会存放当前累积的频率最大的(还没有被抵消掉的)候选结果,只有count抵消到0了,才会重新开始寻找下一个相同的元素。
  • 可以举出用例{2,2,2,2,3,2,3,3,3,3}来进行尝试,各占一半是会被抵消掉的
  • 上面这个写法,基于的大前提是数组中一定存在多数元素。否则,类似{2,2,2,2,3,2,3,3,3,3,4}这样的用例,得到的多数元素就会是4(因为前面2和3都抵消了),但是这个用例实际上是没有多数元素的。

面试题 17.10. 主要元素(找频率>n/2,但多数元素不一定存在)

数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。

示例 1:

输入:[1,2,5,9,5,9,5,5,5]
输出:5

示例 2:

输入:[3,2]
输出:-1

示例 3:

输入:[2,2,1,1,1,2,2]
输出:2

思路

本题是多数元素不一定存在的情况,也就是说,我们根据投票法找到元素之后,需要进行二阶段的判断判断这个元素出现次数是不是真的>n/2

对抗阶段的代码与上一题 169.多数元素 相同,但是本题需要增加计数阶段,也就是验证元素出现次数。

完整版

  • 如果count=0,先赋初值
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        //对抗阶段,先用投票法抵消找出一个备选值
        int ans=0,count=0;
        for(int num:nums){
            if(count==0) ans=num;//ans不需要赋特别大的初值因为会直接覆盖
            count+= (num==ans)?1:-1;
        }
        //计数阶段,验证这个备选值是不是真的出现次数高于n/2
        int cnt=0;
        for(int num:nums){
            if(num==ans) cnt++;
        }
        //判断次数是不是超过n/2
        if(cnt>nums.size()/2){
            return ans;
        }
        return -1;

    }
};

229.多数元素Ⅱ(找频率>n/3)

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

示例 1:

输入:nums = [3,2,3]
输出:[3]

示例 2:

输入:nums = [1]
输出:[1]

示例 3:

输入:nums = [1,2]
输出:[1,2]

提示:

  • 1 <= nums.length <= 5 * 104
  • -10^9 <= nums[i] <= 10^9

思路

这道题的问题是:在一个数组中找到所有出现次数大于n/3的元素。

大于n/3的元素,且不包含相等,也就是说一个数组里最多只有两个这样的元素。和基础的投票法思路相同,我们可以先预设两个备选值,再看后面的元素经过count的抵消,能不能继续保留这两个备选值。

算法分为两个阶段:

  1. 对抗阶段:遍历数组,找出两个候选的多数元素a1a2。对于遍历到的每个元素num,如果num等于a1a2,则增加a1a2的计数;否则,如果a1a2的计数为0,将num设置为a1a2,并将对应的计数设置为1;如果numa1a2都不相等,并且a1a2的计数都不为0,则numa1a2互相抵消,a1a2的计数都减1
  2. 计数阶段:再次遍历数组统计a1a2的出现次数。如果a1a2的出现次数大于n/3,则将其添加到结果列表。

最开始的写法

  • 本题没有说是不是真的存在多数元素,因此也需要计数阶段进行验证
  • 这种写法是错误的,因为三目运算符不能一次性执行多行语句,而在if-else中,我们需要同时更新候选元素num1和count1,再同时更新num2和count2。如果想用三目运算符,需要运用pair数据结构来确保一次性更新两个元素。
class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        //对抗阶段,写法和上面的主要元素是一样的
        int num1=0,count1=0;
        int num2=0,count2=0;
        for(int num:nums){
            //先赋初值
            if(count1==0)  num1=num;
            else if(count2==0)  num2=num;
            //累积频率,这里count2的更新是有问题的,count2最开始没有赋值是不能-1的
            count1 += (num1==num)?1:-1;
            count2 += (num2==num)?1:-1;      
        }
        //计数阶段,验证是不是真的>n/3
        int cnt1=0,cnt2=0;
        for(int num:nums){
            c1+=(num=nums1)?1:0;
            c2+=(num=nums2)?1:0;
        }
        //存储结果
        vector<int>result;
        if(c1>num.size()/3) result.push_back(nums1);
        if(c2>num.size()/3) result.push_back(nums2);
        return result;

    }
};

修改完整版

  • 注意,不能先判断count=0,因为本题有两个候选元素,防止影响两个候选元素的累加!
  • 元素num不等于任何一个候选,且不需要替代任何一个候选的时候,才进行抵消
class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        //对抗阶段,因为题目提示中的最大值是10^9,因此设置初始值为1e9+1
        int num1 = 1e9+1,cnt1=0;
        int num2 = 1e9+1,cnt2=0;
        for(int num:nums){//这种写法num就是nums[i]的数值,没有下标概念
            if(num==num1) cnt1++;
            else if(num==num2) cnt2++;
            else if(cnt1==0){
                num1=num;
                cnt1++;
            }
            else if(cnt2==0){
                num2=num;
                cnt2++;
            }
            else{//出现新元素,且不等于任何一个候选,且不需要替代任何一个候选,就进行抵消
                cnt1--;
                cnt2--;
            }
        }
        //对抗阶段结束后,num1就是第一个数字,num2就是第二个数字
        cnt1=0;
        cnt2=0;
        for(int num:nums){
            cnt1+=(num==num1)?1:0;
            cnt2+=(num==num2)?1:0;
        }
        vector<int>result;
        if(cnt1>nums.size()/3) result.push_back(num1);
        if(cnt2>nums.size()/3) result.push_back(num2);
        return result;
        

    }
};

debug测试:解答错误

在这里插入图片描述

这个错误是因为最开始,对抗阶段的逻辑出现了错误。对抗阶段逻辑最开始的写法:

        //对抗阶段,因为题目提示中的最大值是10^9,因此设置初始值为1e9+1
        int num1 = 1e9+1,cnt1=0;
        int num2 = 1e9+1,cnt2=0;
        for(int num:nums){//这种写法num就是nums[i]的数值,没有下标概念
            //这里的写法是错误的,有两个候选元素都需要累积!
            if(cnt1==0){
                num1=num;
                cnt1++;
            }
            else if(cnt2==0){
                num2=num;
                cnt2++;
            }
            else if(num==num1) cnt1++;
            else if(num==num2) cnt2++;
            else{
                cnt1--;
                cnt2--;
            }
        }

这里的问题在于,本题我们有两个候选元素,我们必须先判断这两个候选元素自身能不能进行累积,才能判断count是否==0!

否则例如上图中{2,2}这种用例,只有一个候选元素在累积,那么另一个元素count=0的判定如果放在前面,就会导致原有的==num1的元素没有被累积到

修改为先判定num==num1/num==num2,再判断count==0的情况,即可

总结:找频率>n/3元素与找>n/2元素的区别

这两个问题之间的主要区别在于正在寻找的元素的数量

在查找主要元素的问题中,出现次数超过了n/2的只可能有一个元素。在这种情况下,当count变为0时可以安全地开始考虑一个新的候选元素,因为不可能有两个元素都出现次数超过n/2。所以当count为0时,可以肯定之前的元素不可能是主要元素,所以重新设置一个候选主要元素是合理的。

然而,当寻找的是出现次数超过n/3的元素时,一个数组有可能存在两个这样的元素。

所以,不能直接在count为0时就更改候选元素,必须先判断新元素是不是==num1/==num2!如果直接在count=0的时候更改候选元素,可能会导致另一个候选元素无法累积例如{2,2}这样的情况

也就是说,n/3的情况,我们需要同时追踪两个候选元素(num1和num2)并维护它们的count。出现一个新元素。首先进行两个候选元素的累积判断才能再判断这两个候选元素的count是否为0,为0的时候进行替换

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

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

相关文章

“智能文件批量改名工具:轻松去除文件名中的特殊符号“

您是否曾经为繁杂的文件命名而感到困扰&#xff1f;特殊符号导致文件名混乱难辨&#xff0c;给您的工作和学习带来不便&#xff1f;现在&#xff0c;我们的智能文件批量改名工具为您解决这一难题&#xff01; 首先&#xff0c;我们要进行文件批量改名高手主页面&#xff0c;并…

基于Docker容器安装TensorFlow测试GPU

前言 当基于nvidia gpu开发的docker镜像在实际部署时&#xff0c;需要先安装nvidia docker。安装nvidia docker前需要先安装原生docker compose 1. CentOS7安装docker详细教程 安装docker 1. Docker 要求 CentOS 系统的内核版本高于 3.10 &#xff0c;查看本页面的前提条件来验…

【iOS】CALayer的理解与简单使用

文章目录 前言一、UIView与CALayer的关系二、CALayer的简单使用1.圆角与裁剪2.contents3.边框属性 总结 前言 在实现网易云音乐demo开发的过程中&#xff0c;通过查阅网上资料&#xff0c;发现了我们可以对我们的视图进行裁剪来实现美观的体现&#xff0c;例如这样&#xff1a…

支付宝原生小程序组件与父级传递数据(微信小程序基本一样)

1. 声明组件 在对应的目录下,右击点击 新建小程序,之后会生成对应的文件 2. 子组件 Component({data: {colorList: [#165FF6, #3D16F6,

【C++杂货铺】拷贝构造函数

&#x1f4d6;定义 拷贝构造函数是构造函数的一个重载&#xff0c;它的本质还是构造函数&#xff0c;那就意味着&#xff0c;只有在创建对象的时候&#xff0c;编译器才会自动调用它&#xff0c;那他和普通的构造函数有什么区别呢&#xff1f; 拷贝构造函数&#xff0c;是创建…

Ubuntu系统开发环境搭建和常用软件

目录 安装PHP7.3 安装MySQL5.7 安装Nginx 配置Nginx支持PHP 安装Jetbrains全家桶 将程序加入到桌面和收藏夹 安装Navicat15 安装 redis和客户端工具 截图工具 终端修改 其它软件 当前我的系统是Ubuntu22.04&#xff1a; 安装PHP7.3 如果使用 apt install php 默认应…

一文讲透 Redis 事务 (事务模式 VS Lua 脚本)

准确的讲&#xff0c;Redis 事务包含两种模式 : 事务模式 和 Lua 脚本。 先说结论&#xff1a; Redis 的事务模式具备如下特点&#xff1a; 保证隔离性&#xff1b; 无法保证持久性&#xff1b; 具备了一定的原子性&#xff0c;但不支持回滚&#xff1b; 一致性的概念有分歧…

BI-SQL丨XML PATH

XML PATH 在SQL Server中&#xff0c;XML数据类型的应用范围是非常宽泛的&#xff0c;除了可以使用value和nodes处理一行拆多行的情况&#xff0c;我们还可以使用PATH处理多行合并成一行。 使用实例 例子&#xff1a;使用PATH处理多行合并成一行。 创建一张表&#xff0c;表…

在vsCode 中执行Electron 项目时,出现中文乱码问题

问题&#xff1a;vscode 中执行Electron 项目时&#xff0c;控制台出现乱码 解决方法&#xff1a; 在 terminal 修改编码格式&#xff1a;65001代表UTF-8&#xff0c;936代表GBK

freeswitch的mod_xml_cdr模块

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 在语音呼叫的过程中&#xff0c;话单是重要的计价和结算依据&#xff0c;话单的产生需要稳定可靠&#xff0c;可回溯。 fs中的mod_xml_cdr模块提供了基本话单功能之外的选择&#xff0c;可以输出XML格式的本地话单或通…

arm day4

.text .global _start _start: /**********LED1点灯**************/bl rcc_initbl led_initbl led1_initbl led2_initloop:bl led_onbl delay_1sbl led_offbl delay_1sbl led1_onbl delay_1sbl led1_offbl delay_1sbl led2_onbl delay_1sbl led2_offbl delay_1sb looprcc_init…

数据结构和算法——快速排序(算法概述、选主元、子集划分、小规模数据的处理、算法实现)

目录 算法概述 图示 伪代码 选主元 子集划分 小规模数据的处理 算法实现 算法概述 图示 快速排序和归并排序有一些相似&#xff0c;都是用到了分而治之的思想&#xff1a; 伪代码 通过初步的认识&#xff0c;我们能够知道快速排序算法最好的情况应该是&#xff1a; 每…

前端 | ( 九)尚品汇实操练习 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 文章目录 &#x1f4da;顶部导航条&#x1f4da;头部&#x1f4da;主导航&#x1f4da;内容区_侧边导航&#x1f4da;内容区_侧边二级菜单⭐️&#x1f4da;内容区_右侧尚品快报&am…

docker安装mysql8.0+

文章目录 1.docker仓库找到需要的镜像版本2.安装Mysql镜像3.创建mysql配置文件4.创建mysql容器并运行5.建立软连接6.开放3306端口7.登录mysql8.修改mysql密码9.查看mysql日志10.重启mysql10.外部如何访问mysql 1.docker仓库找到需要的镜像版本 镜像仓库 2.安装Mysql镜像 找到所…

Redis九种数据类型及其持久化机制:探索数据存储的奇妙世界

目录 一、9种数据类型 3.1 Key操作 3.1.1 相关命令 练习&#xff1a; 3.2 String 3.2.1 结构图 3.2.2 相关命令 练习&#xff1a; 3.3 List(双向的链表) 3.3.1 结构图 3.3.2 相关命令 练习&#xff1a; 3.4 Set&#xff08;无序集合&#xff09; 3.4.1 结构图 3.4…

【GeoDa实用技巧100例】010:制作平滑地图

文章目录 一、平滑地图介绍二、加载实验数据三、平滑地图制作四、注意事项一、平滑地图介绍 平滑地图(Smooth,或称滑动平均地图)是以“平滑”的观测值(简称平滑值),而非实际的观测值编制的地图。某个地域单位的所谓平滑值是指该地域单位与周围地区观测值的平均值。地图平滑化…

精通正则表达式 - 打造高效正则表达式

目录 一、典型示例 1. 稍加修改——先迈最好使的腿 2. 效率 vs 准确性 3. 继续前进——限制匹配优先的作用范围 4. “指数级”匹配 二、全面考察回溯 1. 传统 NFA 的匹配过程 2. POSIX NFA 需要更多处理 3. 无法匹配时必须进行的工作 4. 看清楚一点 5. 多选结构的代…

zabbix 企业级监控 (5) Zabbix监控nginx

目录 简介 配置yum仓库 Web Zabbix端添加主机 启用之前自动发现的规则及动作 简介 nginx在生产环境中的应用越来越广泛&#xff0c;所以需要对nginx的性能状态做一些监控来发现出来出现的问题。zabbix监控nginx&#xff0c;首先确认nginx的监控指标&#xff0c;主要有&#…

HTML中常用的标签

注释标签&#xff1a;<!--内容--> 标题标签&#xff1a;<h1></h1>;<h2></h2>;<h3></h3>;<h4></h4>;<h5></h5>;<h6></h6> 段落标签&#xff1a;<p></p> 没有<p></p>时…

数据备份和恢复练习

创建数据库db create database db&#xff1b; 创建student和score表并插入数据 mysql> select *from student-> ; --------------------------------------------------------------- | id | name | sex | birth | department | address | ----…