LeetCode-169

多数元素

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

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

题解

方法 1 哈希表

利用哈希映射来存储每个元素及其出现的次数,键表示一个元素,值表示该元素出现的次数。为了找出其中的多数元素,可以在哈希表构造完成之后遍历,返回值最大的键,或者在哈希表构造过程中采用打擂台的方法,维护最大的值,省去最后的遍历步骤。

方法 2 排序

由于数组一定存在出现次数大于一半的多数元素,所以若将数组元素进行排序无论单调递增还是单调递减,第 ⌊ n/2 ⌋ +1 个元素(数组中下标为 ⌊ n/2 ⌋ ) 一定是该多数元素。

方法 3 随机化

由于多数元素占据超过一半,因此随机挑选一个元素并验证,很大概率能找到该多数元素。因此,可以随机选择数组下标,检查该下标对应的元素是否为众数,如果是则返回,否则继续随机选择下标。

方法 4 分治

假设 a 是数组 nums 的众数,将 nums 分成两部分,则 a 至少是其中一部分的众数。因此,可以采用分治法,将数组分成左右两部分,分别求出左半部分和右半部分的众数,从中选出正确的结果。
采用分治法分解大问题为多个子问题,直至所有子问题都是长度为 1 的数组。若数组长度为 1,则直接返回;若数组长度大于 1,则划分左右子区间,比较左右区间的众数,如果众数相同,则返回,如果不同则计算两个众数在整个区间的出现次数,大于一半者为该区间的众数。

方法 5 Boyer-Moore 投票算法

  1. 算法步骤:
    a. 第一趟扫描得到一个候选节点 candidate
    b. 第二趟扫描判断 candidate 出现次数是否大于 ⌊ n/2 ⌋

    在第一趟扫描结束后,如果数组中存在多数元素,则 candidate 即为多数元素,如果数组中不存在多数元素,则 candidate 无意义。第二趟扫描是用来统计 candidate 出现的次数从而判断其是否为多数元素。
    注:在本题中给定的数组一定存在多数元素,故只需要第一趟扫描。

  2. 相关变量:
    a. candidate:初值可以为任何数
    b. count:初值为0

    对于数组中每一个元素,先判断 count 是否为0,若为 0 则将 candidate 设置为当前元素,再判断 candidate 是否与当前元素相等,若相等则 count++,否则 count–。

  3. 算法思想:

    假设数组第一个元素 m 不是多数元素,则一开始扫描到第一个元素时 candidate 设置为当前元素 m,count++ 为1,然后继续向后扫描,当遇到数组元素为 m 时 count++,否则 count–。由于 candidate 为 m(不是多数元素),所以 count 一定会减到 0,然后下一个扫描的元素将重新赋值给 candidate。

    在 count 值减为 0 的过程中,与 candidate 相抵消的元素:若全为多数元素,则 count 变为 0 的前一部分中多数元素 n 占一半,因为多数元素占数组的一半以上,故 n 仍为剩下部分的多数元素,该前一部分可以直接忽略。若不全为多数元素,则剩下部分中存在更多的多数元素, n 仍为剩下部分的多数元素。

    假设数组第一个元素 m 是多数元素,则当 count 减为 0 时消耗了与多数元素一样多的非多数元素,m 仍为数组剩余部分的多数元素。

    上述两种情况说明:数组中从 candidate 被赋值到 count 减为 0 的那一段可以去除,余下部分的多数元素仍是原数组的多数元素。

    扫描一遍数组完成后 count 一定大于 0 ,此时 candidate 即为原数组的多数元素。

更多题解

代码

方法 1 哈希表

  1. 哈希表存储(key 为元素,value 为元素的出现次数)
  2. 打擂台方法维护 value 最大值对应的元素
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        unordered_map<int,int> counts;
        int majority=0,cnt=0;
        for(int num:nums){
            ++counts[num];
            if(counts[num]>cnt){
                cnt=counts[num];
                majority=num;
            }
        }
        return majority;
    }
};

方法 2 排序

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

方法 3 随机化

  1. rand() 函数:返回一个从0到最大随机数的任意整数
    int num = rand() % n +a;
    a是起始值,n-1+a是终止值,n是整数的范围
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        while(true){
            int candidate=nums[rand()%nums.size()];
            int count=0;
            for(int num:nums){
                if(num==candidate){
                    ++count;
                }
            }
            if(count>nums.size()/2){
                return candidate;
            }
        }
        return -1;
    }
};

方法 4 分治

  1. count_in_range(vector& nums,int target,int lo,int hi)
    计算区间 nums(lo,hi) 内 target 出现的次数
  2. majority_element_rec(vector& nums,int lo,int hi)
    求出区间 nums(lo,hi) 内的众数(多数元素)
  3. majorityElement(vector& nums)
    求出数组 nums 中的多数元素
class Solution {
    int count_in_range(vector<int>& nums,int target,int lo,int hi){
        int count=0;
        for(int i=lo;i<=hi;i++){
            if(nums[i]==target){
                ++count;
            }
        }
        return count;
    }

    int majority_element_rec(vector<int>& nums,int lo,int hi){
        if(lo==hi){
            return nums[lo];
        }
        int mid=(lo+hi)/2;
        int left_majority=majority_element_rec(nums,lo,mid);
        int right_majority=majority_element_rec(nums,mid+1,hi);
        if(count_in_range(nums,left_majority,lo,hi)>(hi-lo+1)/2){
            return left_majority;
        }
        if(count_in_range(nums,right_majority,lo,hi)>(hi-lo+1)/2){
            return right_majority;
        }
        return -1;
    }

public:
    int majorityElement(vector<int>& nums) {
        return majority_element_rec(nums,0,nums.size()-1);
    }
};

方法 5 Boyer-Moore 投票算法

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int candidate=0,count=0;
        for(int num:nums){
            if(count==0){
                candidate=num;
            }
            if(candidate==num){
                count++;
            }else{
                count--;
            }
        }
        return candidate;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值