leetcode-169. Majority Element

来源:互联网 发布:国际象棋和围棋 知乎 编辑:程序博客网 时间:2024/06/06 05:12

leetcode-169. Majority Element


Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.
You may assume that the array is non-empty and the majority element always exist in the array.

题目大意:
给定一个大小为n的数组,找到大多数元素。大多数元素是出现超过⌊n / 2⌋次的元素。 您可能会认为该数组是非空的,而且大多数元素始终存在于数组中。

题目本身还是容易的,主要是记录一下各种解法:


解法一:HashMap

我们可以使用一个HashMap来将元素映射到value上,以便通过在数字上循环来计算线性时间的出现次数。然后,我们只需返回最大值的key。

class Solution {    private Map<Integer, Integer> countNums(int[] nums) {        Map<Integer, Integer> counts = new HashMap<Integer, Integer>();        for (int num : nums) {            if (!counts.containsKey(num)) {                counts.put(num, 1);            }            else {                counts.put(num, counts.get(num)+1);            }        }        return counts;    }    public int majorityElement(int[] nums) {        Map<Integer, Integer> counts = countNums(nums);        Map.Entry<Integer, Integer> majorityEntry = null;        for (Map.Entry<Integer, Integer> entry : counts.entrySet()) {            if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {                majorityEntry = entry;            }        }        return majorityEntry.getKey();    }}

方法二:排序

这个是比较好的方法,将数组排序后,出现超过半数的元素,肯定是length/2位置的元素

class Solution {    public int majorityElement(int[] nums) {        Arrays.sort(nums);        return nums[nums.length/2];    }}

方法三:随机

这种方法也不是很难理解,但是时间复杂度的证明是比较困难的,也就是为什么没有超时。。
该方法的思路是既然所求的元素在数组中出现的次数超过半数,那么我们随机取数的话,取到的元素也很有可能就是所求的元素。
每次随机取一个数,然后遍历整个数组,计算出现的次数是否超过半数,如果不超过半数就继续取值,直到取到结果为止。

class Solution {    private int randRange(Random rand, int min, int max) {        return rand.nextInt(max - min) + min;    }    private int countOccurences(int[] nums, int num) {        int count = 0;        for (int i = 0; i < nums.length; i++) {            if (nums[i] == num) {                count++;            }        }        return count;    }    public int majorityElement(int[] nums) {        Random rand = new Random();        int majorityCount = nums.length/2;        while (true) {            int candidate = nums[randRange(rand, 0, nums.length)];            if (countOccurences(nums, candidate) > majorityCount) {                return candidate;            }        }    }}

方法四:分治法

将数组分成两部分,寻找第一个部分中出现次数超过一半的元素为A,第二个部分出现次数超过一半的元素为B,如果A==B,那么A就是这个数组中出现次数超过一半的元素,如果A!=B,那么A和B都可能是这个数组中出现次数超过一半的元素,那么重新遍历这个数组,记录A和B出现的次数,返回出现次数多的元素,时间复杂度T(n)=2T(n/2)+2n=O(nlogn)

T(n)=2T(n2)+2n

根据 主定理,循环满足条件2,所以得:
T(n)=Θ(n logbalogn)=Θ(n logn)

public static void main(String[] args) {    int majorityElement(vector<int>& nums) {        return helper(nums,0,nums.size()-1);    }    helper(vector<int> nums,int begin,int end){        if(begin == end)            return nums[begin];        else{            int mid = begin + (end-begin)/2;            int left = helper(nums,begin,mid);            int right = helper(nums,mid+1,end);            if(left == right) return left;            else{                int leftCount = 0;                int rightCount = 0;                for (int i = begin;i<=end ;i++ ) {                    if (nums[i] == left)                         leftCount++;                    else if(nums[i] == right)                        rightCount++;                }                if (leftCount<rightCount)                     return right;                else return left;            }           }           }}

方法五:Boyer-Moore Voting Algorithm

建议! 读过思路后,阅读一下solution 代码,再回想一下思路 !,就比较容易理解。
这种方法的思路是:每次从数组中找出一对不同的数字,将他们从数组中删除,当最后肯定至少剩下一个元素(3个元素的情况下)(!保证数组中一定存在出现次数超过半数的数字)。
那怎么找到一对不同的数字?怎么删除呢?
在算法的执行过程中,使用一个常量空间来记录候选元素 c 以及他出现的次数 f(c) , c 即为当前阶段出现超过半次的元素(当前阶段指在遍历数组的过程中的 前i个元素)。
在开始之前,c 为第一个元素, f(c)0,然后开始遍历数组:
 如果数组A[i] == c,即元素相同,则f(c)+=1
 如果数组A[i] !== c,即元素不相同,则 f(c)-=1,即删除这个元素的计数。
 如果元素相同或者计数为0,则应该更新元素并将计数加1。
对于两个不同元素,在遍历过后就会使计数器重新变回0,就相当于删除了两个不同元素!
 在遍历的过程中,如果 f(c) = 0,表示当前并没有候选的元素存在;如果f(c)!=0就是在当前阶段存在候选元素(元素在当前阶段出现超过半数),那么他在剩余的字符串中出现的次数也用超过半数,所以方法是可行的。

int majorityElement(vector<int>& nums) {    int count=0;    int result=nums[0];    for(int i=0;i<nums.size();i++)    {        if(count==0||nums[i]==result)        {            count++;            result=nums[i];        }        else            count--;    }    return result;}

本来最后f(c) > 0的元素可能并不是出现半次以上的元素,比如{1,2,3},需要遍历数组确认是否出现超过半次,但是题目保证存在出现半次以上的元素,就不用考虑了。