leetcode笔记,求众数,摩尔投票

在做leetcode上的每日打卡题目时,看到了几道一系列的关于众数的问题,都可以用摩尔投票的方法来解答,做个小笔记

Leetcode第169题 多数元素

题目

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

解析

运用减而治之的思想,摩尔投票方法
这里题目假设了数组非空,且一定存在多数元素,因此忽略对这两种情况的检查

图摘自清华大学数据结构慕课电子讲义

在这里插入图片描述

算法

  • 从前向后遍历整个向量,借助计数器c,记录maj与其他元素的数量差额
  • 若当前元素A[i]与候选者maj相等,则计数器c++,反之c–
  • 每当c归零,都意味着此时的前缀P可以剪除,此时将众数候选者maj改为新的当前元素A[i],并将计数器更新为1

算法结束后的maj就是待求众数(这里题目已经明确了众数一定存在,所以可得出此结论,否则还需遍历一次向量,确保maj出现的次数超过向量元素总数的一半)

int majorityElement(vector<int>& nums) 
{
	int majority;
	for (int i = 0,c=0; i < nums.size(); i++)		//c为众数和其他数的差额计数器
	{
		if (0 == c)
		{
			majority = nums[i];
			c = 1;
		}
		else
		{
			majority == nums[i] ? c++ : c--;
		}
	}
	return majority;
}

Leetcode第229题 求众数Ⅱ

题目

给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。

解析

首先,超过n/k的众数最多只有k-1个。因此,对于此题,超过n/3的数最多只可能有2个。同样使用摩尔投票的方法和减而治之的思想,先随机选出两个候选者A、B,然后遍历数组进行相应比对。

注意,此题并没有给出向量一定存在众数的先决条件,同时也没给出向量非空的假设,可能出现空向量或向量只有一个元素的退化情况,因此要分别对上述情况做出判断

算法

  1. 判断输入向量是否为空或只有一个元素,若是则直接返回原输入向量
  2. 将候选者A,B初始化为任意两个数,同时设立两个差额计数器c1,c2并初始化为0
  3. 从前向后遍历整个向量:
    若投A(当前元素等于A),则A的票数++(计数器c1++);结束当前循环,进入下一次循环
    若投B(当前元素等于B),则B的票数++(计数器c2++);结束当前循环,进入下一次循环
    若A、B都不投,则先检查此时AB的票数是否减为0,若A或B是,则当前元素替代其成为新的候选者(同时该候选者的票数更新为1);若A、B两人的票数都不为0,那么A、B的票数均–(计数器c1–,c2–);结束当前循环,进入下一次循环
  4. 遍历结束后,会得到两个最终的候选者maj1、maj2,,此时还需再从头遍历一次数组,对maj1、maj2出现的次数进行计数,检查其各自出现次数是否满足超过n/3次的条件注意在此次遍历中,当前元素与maj1、maj2的各自判等比较中间采用的是else if连接,避免由于maj1等于maj2时将二者同时计数+1,最后得出两个符合要求的数的错误结论

注意上述步骤的顺序不能错,否则遇到一些特殊情况(如向量中前两个元素相同)时会出错

vector<int> majorityElement(vector<int>& nums)
{
	if (0 == nums.size() || 1 == nums.size())		//判断输入数组是否非空或只有一个元素
		return nums;

	vector<int> ans;
	int maj1 = 0, maj2 = 0;		//maj1,maj2初始化为任意两个数
	int count1 = 0, count2 = 0;
	for (int i = 0; i < nums.size(); i++)
	{
		if (maj1 == nums[i])
			count1++;
		else if (maj2 == nums[i])
			count2++;
		else if (0 == count1)
		{
			maj1 = nums[i];
			count1 = 1;
		}
		else if (0 == count2)
		{
			maj2 = nums[i];
			count2 = 1;
		}
		else
		{
			count1--;
			count2--;
		}
	}

	count1 = 0;
	count2 = 0;
	for (int num : nums)	//由于不保证到底有几个超过1/3的数,可能为0,1,2个,因此还需重新遍历一遍,进行确认	
	{
		if (maj1 == num)
			count1++;
		else if (maj2 == num)	//对maj1和maj2的判断一定要用else if,避免由于maj1等于maj2时将二者同时计数+1,最后得出两个符合要求的数的错误结论
			count2++;
	}

	if (count1 > nums.size() / 3)
		ans.push_back(maj1);
	if (count2 > nums.size() / 3)
		ans.push_back(maj2);
	return ans;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值