C语言算法金手指摩尔投票法手撕绝大多数问题

目录
  • 正片开始
    • 概念
    • 优点
    • 算法核心
    • 实现

正片开始

概念

嘛是摩尔投票法?
简单来说就是投票法,算法解决的问题是如何在任意多的候选人,选出获得票数最多的那个。常见的算法是扫描一遍选票,也就是遍历,对每个候选人进行统计的选票进行统计。

那我遍历难道不香吗?

这里就要讲一下投票法的过人之处

优点

当执行有序情况时,只要找到中位数,然后检查中位数的个数是否超过选票的一半即可。但是当对象的数目不定时,统计选票可能会执行较长时间,相比原来的时间复杂度 O(n) ,可能需运行 O(n^2) 的时间。

针对无序且对象不定的情况,摩尔投票法应运而生。

算法核心

摩尔投票法的关键就是抵消和计数,抵消过程类似于是在进行投票,然后计数我们可以脑补一个情形:当三人中的一人得票数远超其他两人时,我们就可以进行等量对消,消完还有票的自然就是得票数最多的那个,模拟如下:(ppt手残勿喷)

实现

基本思路就是由一个计数器维护,在进行比较过程中,不同票消掉,相同票保留,如果消的没有了就直接放进去然后继续上述过程,直到维护对象只剩一个,就是我们要找的最大得票者(众数)。

基于LeetCode真题实践,先看一个初级栗子:

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

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

来源:力扣(LeetCode)

我最初的思路是哈希思想,qsort 之后用最大的元素 malloc 一块新的空间,桶排后用 count 进行++,最后输出 count 最大的。有点麻烦而且我忽视了一个最大的问题就是负数的存在,垮掉~

根据我们算法的思路,针对目标数组 nums,首先创建一个变量来执行计数器 count 再创建一个新数组 a 进行比对,因为始终是目标数组的内部元素比较,我可以直接把 nums 首元素赋给 a ,比较时从第二个元素开始和 a 比较,如果相同,count ++;不同消掉,count --;没有了就往里拿。

int majorityElement(int* nums, int numsSize){
    int count = 1;
    int a = nums[0];
    for(int i =1;i<numsSize;i++)
    {
        if(nums[i]==a)
        {
            count++;
        }
        else
        {
            count--;
        }
        if(count==0)
        {
        a = nums[i+1];
        }
    }
    return a;
}

格局抬高

现在整一道硬菜,直接上升级版:
229. 求众数 II
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

示例 :
输入:[1,1,1,3,3,2,2,2]
输出:[1,2]

还是那句话,题目越少眼泪越饱,短小精悍的题 nnd 硬磨了我一个下午,桶是肯定桶不了的,哈希又没学,不做吧心里太不舒服,这里就又有我摩尔投票法的一席之地了

注意,题目给的是超过 n/3 次的元素,这很重要,其实就是在暗示我们结果至多只会有三种情况:没有众数或者一个众数或者两个众数;但是头疼的是咱之前的摩尔投票法好像行不通,因为这里对象不定,如果出现了两个众数我的 “ 票 ” 该怎么投?

将之前的格局打开,我们可以将所有数字分为两类,> n/3 的元素 和 <= n/3 的元素,其中大于 n/3 的元素最多只有两个。因此可以设置三个变量,进行相对抵消,这里我们不妨再引入一个容器 b,再用一个新的计数器 count2 来进行维护,当三个元素相同时,count++,不同就抵消,为 0 就用把这个数放进去,基本思想和上面是差不多的:

    int a = nums[0];
	int b = nums[1];
	int count1, count2 = 0;
	for (int i = 0; i < numsSize; i++)
	{
		if (a == nums[i])
		{
			count1++;
			continue;
		}
		else if (b == nums[i])
		{
			count2++;
			continue;
		}
		else
		{
			count1--;
			count2--;
		}
		if (count1 < 0)
		{
			a = nums[i];
			count2++;
			count1 = 1;
		}
		if (count2 < 0)
		{
			b = nums[i];
			count1++;
			count2 = 1;
		}
	}

还没完,因为数组可能存在一个众数或者没有众数的情况,我们还需要再次遍历来统计我们筛选的众数的出现次数是否符合题目要求的大于n/3 以及排除数组为空或单项的情况:

    count1 = 0;
	count2 = 0;
	for (int i = 0; i < numsSize; i++)
	{
		if (a == nums[i])
		{
			count1++;
		}
	    else if (b == nums[i])
		{
			count2++;
		}
	}
		if (numsSize <= 1)
	{
		*returnSize = numsSize;
		return nums;
	}

最后直接依次放入一块新的空间再返回即可,全部代码如下:

int* majorityElement(int* nums, int numsSize, int* returnSize) {
	if (numsSize <= 1)
	{
		*returnSize = numsSize;
		return nums;
	}
	int a = nums[0];
	int b = nums[1];
	int count1, count2 = 0;
	for (int i = 0; i < numsSize; i++)
	{
		if (a == nums[i])
		{
			count1++;
			continue;
		}
		else if (b == nums[i])
		{
			count2++;
			continue;
		}
		else
		{
			count1--;
			count2--;
		}
		if (count1 < 0)
		{
			a = nums[i];
			count2++;
			count1 = 1;
		}
		if (count2 < 0)
		{
			b = nums[i];
			count1++;
			count2 = 1;
		}

	}
	count1 = 0;
	count2 = 0;
	for (int i = 0; i < numsSize; i++)
	{
		if (a == nums[i])
		{
			count1++;
		}
	    else if (b == nums[i])
		{
			count2++;
		}
	}
	int* c = (int*)malloc(sizeof(int) * 2);
    *returnSize = 0;
	if (count1 > numsSize / 3)
	{
		c[(*returnSize)++] = a;
	}
	if (count2 > numsSize / 3)
	{
		c[(*returnSize)++] = b;
	}
	return c;
}

家人们明白了吗,那今天就到这里,摸了。

以上就是C语言算法金手指摩尔投票法手撕绝大多数问题的详细内容,更多关于C语言摩尔投票算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • C语言实现Floyd算法

    本文实例为大家分享了C语言实现Floyd算法的具体代码,供大家参考,具体内容如下 #include <stdio.h> #include <stdlib.h> #include <limits.h> #define NUM 4 typedef struct MGraph /* 邻接表存储结构 */ { int edges[NUM][NUM]; int n,e; } MGraph; MGraph *build_mgraph(); void Floyd(MGraph *mg

  • 贪心算法的C语言实现与运用详解

    贪心算法 所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解. 贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解.贪心算法的基本思路如下: 1.建立数学模型来描述问题. 2.把求解的问题分成若干个子问题. 3.对每一子问题求解,得到子问题的局部最优解. 4.把子问题的解局部最优解合成原来解问题的一个解. 实现该算法的过程: 从问题的某一初始解出发:

  • C语言实现K-Means算法

    一.聚类和聚类算法 聚类,就是将数据对象划分成若干个类,在同一个类中的对象具有较高的相似度,而不同的类相似度较小.聚类算法将数据集合进行划分,分成彼此相互联系的若干类,以此实现对数据的深入分析和数据价值挖掘的初步处理阶段.例如在现代商业领域,聚类分析算法可以从庞大的数据集合中对消费者的消费习惯.消费倾向,以方便决策者制订消费策略.总之,作为数据挖掘中的一个模块,聚类分析算法可以作为一个单独的工具已发现数据库中分布的一些深层信息,并概括出每一类的特点.聚类分析算法也可作为数据挖掘算法中其他分析算法

  • 必须知道的C语言八大排序算法(收藏)

    概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 1.插入排序-直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到

  • C语言的10大基础算法

    算法是一个程序和软件的灵魂,作为一名优秀的程序员,只有对一些基础的算法有着全面的掌握,才会在设计程序和编写代码的过程中显得得心应手.本文是近百个C语言算法系列的第二篇,包括了经典的Fibonacci数列.简易计算器.回文检查.质数检查等算法.也许他们能在你的毕业设计或者面试中派上用场. 1.计算Fibonacci数列 Fibonacci数列又称斐波那契数列,又称黄金分割数列,指的是这样一个数列:1.1.2.3.5.8.13.21. C语言实现的代码如下: /* Displaying Fibona

  • C语言算法金手指摩尔投票法手撕绝大多数问题

    目录 正片开始 概念 优点 算法核心 实现 正片开始 概念 嘛是摩尔投票法?简单来说就是投票法,算法解决的问题是如何在任意多的候选人,选出获得票数最多的那个.常见的算法是扫描一遍选票,也就是遍历,对每个候选人进行统计的选票进行统计. 那我遍历难道不香吗? 这里就要讲一下投票法的过人之处 优点 当执行有序情况时,只要找到中位数,然后检查中位数的个数是否超过选票的一半即可.但是当对象的数目不定时,统计选票可能会执行较长时间,相比原来的时间复杂度 O(n) ,可能需运行 O(n^2) 的时间. 针对无

  • C语言之整数划分问题(递归法)实例代码

    C语言之整数划分问题(递归法)实例代码 整数划分问题是算法中的一个经典命题之一,有关这个问题的讲述在讲解到递归时基本都将涉及.所谓整数划分,是指把一个正整数n写成如下形式: n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,...,mi}为n的一个划分. 如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分.这里我们记n的m划分的个数为f(n,m); 例如但n=4时,他有

  • C语言算法练习之佩奇借书

    目录 1. 问题描述 2. 题目分析 3. 算法设计 4. 代码实现 5. 算法升级 1. 问题描述 佩奇有5本新书,要借给A.B.C这3位小朋友,若每人每次只能借1本,则可以有多少种不同的借法? 2. 题目分析 本题属于数学当中常见的排列组合问题,即求从 5 个数中取 3 个不同数的排列组合的总数. 我们可以将 5 本书进行 1-5 的编号,A.B.C 3个人每次都可以从 5 本书中任选 1 本,即每人都有 5 种选择,由于 1 本书不可能同时借给一个以上的人,因此只要这 3 个人所选书的编号

  • C语言算法练习之折半查找的实现

    目录 1. 题目描述 2. 问题分析 3. 算法设计 4. 动图演示 5. 代码实现 6.知识点补充 continue 语句 break 语句 continue语句 和 break语句的区别 7. 问题拓展 1. 题目描述 N 个有序整数数列已放在一维数组中,利用二分查找法查找整数 m 在数组中的位置. 若找到,则输出其下标值:反之,则输出 “ Not be found!”. 2. 问题分析 二分查找法(也叫折半查找)其本质是分治算法的一种. 所谓分治算法是指的分而治之,即将较大规模的问题分解成

  • C语言算法积累加tag的循环队列

    题目: 若希望循环队列中的元素都能得到利用,则需设置一个标志域tag,并以tag的值为0或1来区分队头指针front和队尾指针rear相同时的队列状态是“空”还是“满”. 试编写与此结构相应的入队和出队算法. 关键字: 循环队列+tag的使用 思路 : 循环队列: 需要变量:队头指针front,队尾指针rear,增减元素的开关:tag 1)入队算法 尾插法:Q.data[Q.rear]=x;Q.rear=(Q.rear+1)%Maxsize;Q.tag=1 队空条件:Q.front== Q.re

  • C语言算法的时间复杂度和空间复杂度

    目录 1.算法效率 1.1 如何衡量一个算法的好坏 1.2算法的复杂度 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算举例 3.空间复杂度 4.常见复杂度对比 5.复杂度的OJ练习 5.1消失的数字OJ 3.2 旋转数组OJ 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法的好坏呢?比如对于以下斐波那契数列: long long Fib(int N) { if (N < 3) return 1; return Fib(N - 1) +

  • Go语言算法之寻找数组第二大元素的方法

    本文实例讲述了Go语言算法之寻找数组第二大元素的方法.分享给大家供大家参考.具体如下: 该算法的原理是,在遍历数组的时,始终记录当前最大的元素和第二大的元素.示例代码如下: 复制代码 代码如下: package demo01    import (      "fmt"  )    func NumberTestBase() {      fmt.Println("This is NumberTestBase")        nums := []int{12, 2

  • C语言算法练习之打鱼还是晒网

    1. 问题描述 中国有句俗语叫 " 三天打鱼两天晒网 ".某人从 1990 年 1 月 1 日起开始 "三天打鱼两天晒网",问这个人在以后的某一天中是 "打鱼" 还是 "晒网". 2. 题目分析 根据题意可以将解题过程分为 3 步: (1) 计算从 1990 年 1 月 1 日开始至指定日期共有多少天. (2) 由于 "打鱼" 和 "晒网" 的周期为 5 天,所以将计算出的天数用 5 去

  • C语言算法练习之抓交通肇事犯

    1. 问题描述 一辆卡车违反交通规则,撞人后逃跑.现场有三人目击该事件,但都没有记住车号,只记下车号的一些特征. 甲说:牌照的前两位数字是相同的: 乙说:牌照的后两位数字是相同的,但与前两位不同: 丙是数学家,他说:四位的车号刚好是一个整数的平方. 请根据以上线索求出车号. 2. 题目分析 按照题目的要求造出一个前两位数相同.后两位数相同且相互间又不同的 4 位整数,然后判断该整数是否是另一个整数的平方. 即求一个四位数 a 1.a 2 .a 3. a 4,满足如下的条件: 3. 算法设计 该题

  • C语言算法练习之佩奇存钱方案

    目录 1. 问题描述 2. 问题分析 3. 算法设计 4. 代码实现 1. 问题描述 假设银行一年整存零取的月息为 0.63%. 现在佩奇手中有一笔钱,她打算在今后的 5 年中的每年年底取出 1000 元,到第 5 年时刚好取完. 请算出佩奇存钱时应存入多少? 2. 问题分析 根据题意,可以从第 5 年向前推算. 已知 “在今后的 5 年中,每年的年底取出 1000 元,这样到第 5 年的时候刚好可以取完”,因此,第 5 年年底会取出 1000 元,则可以计算出第 5 年年初在银行中所存的钱数为

随机推荐