LeetCode-剑指Offer-39-数组中出现次数超过一半的数字


题意描述:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。

注意 1 <= 数组长度 <= 50000


示例:

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

解题思路:

Alice: 这道题好面熟啊。
Bob: 那可定是之前做过了,有什么想法吗 ?
Alice: 可以用Python里面的字典计算每个数字出现的次数,然后找到出现次数最多的那个就好了,时间复杂度应该是O(n), 空间复杂度是O(k) k是不同数字的个数。
Bob: 还可以先排序,然后检查相邻相同元素的个数,找到最大的那个,时间复杂度应该是O(nlogn + n) 空间复杂度是O(1)
Alice: 我怎么总想起来快速排序呢 ?能借鉴里面的做法吗,出现次数超过数组长度一半。那排好序后这些元素可定会覆盖数组中间的位置啊。
Bob: 那就是说,排序后直接返回 nums.length / 2 位置的元素就行了 ?
Alice: 是啊,这样时间复杂度就下降到O(n * log n)了。哦哦哦,我想起来,还有一种算法,是O(n) 的时间复杂度,O(1) 的空间复杂度。
Bob: 我也想起来了,好像叫什么投票算法。不过我自己把它叫做 “群雄逐鹿” 算法。 想象一下,东汉末年,群雄并起,有一个儿数组,数组中的不同的元素表示不同的派系,相邻位置的元素之间战争不断,只有相邻且相同的元素才能共存,相邻不相同的元素只能同归于尽,问最后谁能问鼎中原呢 ?
Alice: hhh, 你这不就是群殴吗,只有相邻且相同的元素才能积蓄,相邻不相同的元素相互抵消。这样从前到后遍历数组,最后留下来的就多数,而且是数量最多的。你发现了没有,这个投票算法不仅仅可以求数量超过数组元素个数一般的,也可以求更一般的结果,可以求数组中相同数字最多的那个。
Bob: 👍👍😎😎
Alice: 还是不会用 Java 中的字典呀 ,🤷‍♀️


代码:

Python 方法一: 字典计数,然后求出现次数最多的。

class Solution:
    def majorityElement(self, nums: List[int]) -> int:

        cnt = {}
        for x in nums:
            if x not in cnt:
                cnt[x] = 1
            else:
                cnt[x] += 1
        
        maxCnt = 0
        ret    = 0 
        for x in cnt.keys():
            if cnt[x] > maxCnt:
                maxCnt = cnt[x]
                ret = x
        
        return ret

Java 方法二: 排序 + 检查相邻元素。

class Solution {
    public int majorityElement(int[] nums) {

        Arrays.sort(nums);
        int ret    = nums[0];
        int cnt    = 1;
        int maxCnt = 1;

        for(int i=1; i<nums.length; ++i){
            if(nums[i-1] == nums[i]){
                cnt += 1;
            }else{
                if(cnt > maxCnt){
                    maxCnt = cnt;
                    ret  = nums[i-1];
                }
                cnt = 1;
            }
        }

        if(cnt > maxCnt){
            maxCnt = cnt;
            ret  = nums[nums.length-1];
        }

        return ret;
    }
}

Java 方法二: 多于数组一半的元素连起来一定会覆盖数组中间的那个位置,也就是排序后nums.length / 2 的位置

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

Java 方法三: “功过相抵,逐鹿中原” ?

class Solution {
    public int majorityElement(int[] nums) {

        int cnt = 1;
        int pre = nums[0];

        for(int i=1; i<nums.length; ++i){

            if(cnt == 0){                // 新的一轮争霸
                cnt = 1;
                pre = nums[i];
                continue;
            }

            if(nums[i] == pre){
                cnt += 1;                 // "招兵"
            }else{
                cnt -= 1;                 // “打仗”
            }
        }

        return pre;
    }
}

易错点:

  • 一些测试点:
[1,2,3,2,2,2,5,4,2]
[1]
[1,1]
[1,2,1]
[2,3,3]
2
1
1
1
3

总结:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值