首页 > 其他分享 >【LeetCode摩尔投票】有趣的简单题:数组中出现次数超过一半的数字

【LeetCode摩尔投票】有趣的简单题:数组中出现次数超过一半的数字

时间:2023-06-24 20:56:30浏览次数:43  
标签:哈希 nums int 摩尔 num 数组 排序 LeetCode

数组中出现次数超过一半的数字

https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

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

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

限制:

1 <= 数组长度 <= 50000

初见思路:哈希表+排序

我的初见思路是用hash表统计个元素出现次数,然后排序,最后看排序后是否有元素的出现次数大于数组的长度的一半

有就返回该值实现代码如下:

class Solution {
public:
    class cmp{
        public:
        bool operator()(pair<int, int>& a, pair<int, int>& b){
            return a.second > b.second;
        }
    };
    int majorityElement(vector<int>& nums) {
        unordered_map<int, int> hash4Count;
        for(int num : nums) hash4Count[num]++;

        vector<pair<int, int>> vec4sort(hash4Count.begin(), hash4Count.end());

        sort(vec4sort.begin(), vec4sort.end(), cmp());

        int numLen = nums.size() / 2;
        if(vec4sort[0].second > numLen) return vec4sort[0].first;
        else return -1;
    }
};

该代码可以通过题目给的用例

但是,在一些特殊情况下,上述代码会有错误

例如,在测试用例[1,2,3,3,3,2,3,4,2]中,上述代码运行错误,原因是正是使用了哈希表和排序

测试用例[1,2,3,3,3,2,3,4,2]中,数字3出现的次数超过了数组长度的一半,因此应该返回3,但是由于哈希表对键值对的遍历顺序不确定,因此在将哈希表中的数据按照出现次数排序时,可能会出现3排在2的前面,从而导致算法返回错误的结果。

由于上述原因,使用哈希表的思路修改多次都会在某些情况下发生错误(尝试的修改包括:直接修改哈希表和排序的部分,遍历哈希表中的键值对,找到出现次数最多的那个数字,如果它的出现次数超过了数组长度的一半,则返回该数字,否则返回-1。仍然错误)

因此,应该换个思路

思路2:排序与众数

还是拿刚刚的用例[1,2,3,3,3,2,3,4,2]

对其进行排序可以得到[1,2,2,2,3,3,3,3,4]

我们发现"3"正好位于数组中间位置,这不是巧合,因为题目要求的数,该数的出现次数要大于数组长度的一半

在此规则下,数组排序之后,中间位置就会是我们要找的数,这个数也就是所谓的"众数"

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        return nums[nums.size()/2];
    }
};

这次ac了,代码还超级简单。。。

这种算法的时间复杂度为 O(nlogn),其中 n 是数组大小。

思路3:摩尔投票

回到之前的思路,如果要选一种代替哈希表+排序的思路,那么最正统的应该是Boyer-Moore算法(摩尔投票)

假设当前数字num是众数,初始时将计数器count4Vote赋为1

随后遍历数组,如果元素与num相同,则将计数器加1,否则将计数器减1。

当计数器变为0时,说明之前遍历过的所有数字中,num出现的次数不足一半,因此可以将num更新为当前遍历的数字,并将计数器重新设为1。最终留下来的num即为所求的答案。

图解见

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int count4Vote = 1;//记录投票的变量
        int num = nums[0];//众数,初始化为第一个数
        for(int i = 1; i < nums.size(); i++){
            if(nums[i] == num) count4Vote++;
            else{
                count4Vote--;//出现不同的元素,之前元素"投的票"减1
                if(count4Vote == 0){//当减完之后,上一个投票的元素就被踢掉了,当前遍历元素作为"投票者"继续游戏
                    num = nums[i];
                    count4Vote = 1;//票数重置
                }
            }
        }
        return num;//返回最后剩下的数,即"投票者"
    }
};

这种方法的性能要好于排序后取中间值

时间和空间复杂度为 O(n)和 O(1)

标签:哈希,nums,int,摩尔,num,数组,排序,LeetCode
From: https://www.cnblogs.com/DAYceng/p/17501668.html

相关文章

  • #yyds干货盘点# LeetCode程序员面试金典:各位相加
    1.简述:给定一个非负整数num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。 示例1:输入:num=38输出:2解释:各位相加的过程为:38-->3+8-->1111-->1+1-->2由于 2是一位数,所以返回2。示例2:输入:num=0输出:02.代码实现:classSolution{pu......
  • Java 一维数组的使用
    Java一维数组的使用1.一维数组的定义在不知道数组内容可以直接使用下面的定义方法:int[]arr=newint[数组个数];或intarr[]=newint[数组个数];在知道数组内容可以使用如下:int[]arr={data1,data2,data.....};2.数组的传递数组的传递与其他基本类型的值传递不同,......
  • #yyds干货盘点# LeetCode程序员面试金典:单词拆分 II
    题目:给定一个字符串s和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序返回所有这些可能的句子。注意:词典中的同一个单词可能在分段中被重复使用多次。 示例1:输入:s="catsanddog",wordDict=["cat","cats","......
  • [leetcode]114. 二叉树展开为链表
    总结:怎样写递归函数?关键是把递归函数的功能定义清楚,并在递归函数体中使用自身来做事,此时不要关注递归函数执行的细节。也就是写高层级代码的时候不要关注低层级的事情,这就叫抽象。关注也没有用,想不清楚的。 1classSolution{2publicvoidflatten(TreeNoderoot){......
  • c语言-字符串+转义字符+注释、语句、函数、数组、操作符 2
    一、字符串+转义字符+注释字符串类型(相较于字符数据类型):eg:“”;//空字符串定义:由双引号引起的一串字符为字符串字面值,简称字符串。(后面默认会有\0,结束标志不算内容intmain(){chararr1[]="abc";//数组//"abc"——'a''b''c''\0'——'\0'......
  • leetcode 16 最接近的三数之和 3sum-closest【ct】
    ===思路:在遍历中去计算,每一轮循环中都去计算,如果distance更小就去更新distance。如果sum>target,end--,如果sum<target,start++,如果等于,就可以直接返回target  ......
  • leetcode 113 路径总和2 path-sum-ii【ct】
    ===思路:很简单,记得递归的时候传入path.slice ......
  • 【剑指Offer】40、数组中只出现一次的数字
    【剑指Offer】40、数组中只出现一次的数字题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)。解题思路:这道题目相对比较难,一般情况下,我们首先可以想到的是顺序扫描数组,但其时间复杂......
  • 二维数组的传参方式
    1.传数组整体格式:(1)给函数传参时,用数组名arr;(2)函数定义时,用intarr[3][5]接收;(3)在函数中打印元素时,用arr[1][1]。voidtest(intarr[3][5]){ printf("%d",arr[1][1]);}intmain(){ intarr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}}; test(arr); return0;}2.传首元......
  • leetcode5最:长回文子串
    动态规划:1个回文串,两边加上同样的字符,也是回文串。这是一个性质,之后要用。对于一大串字符,从1长度的子串开始判断。1个长度的子串,肯定回文;如果这个子串两边加上同样的字符,长度变成了3,少了一次判断。因此还要加上,判断2长度的子串是不是回文。之后才会判断3长度的子串是不是回文......