leetcode解题笔记:Majority Element I & II

剑指offer第29题,leetcode 169,229
题目:找到数组中出现次数超过一半的数字

Given an array of size n, find the majority element. The majority
element is the element that appears more than ⌊ n/2 ⌋ times.

解法一

由于要寻找的数字在数组中出现次数超过一半,这就说明如果整个数组排好序,那么中间的那个数肯定就是我们的答案。
因此可以考虑借鉴快排的方法,我们先随机选择一个数字,把比它小的都放它左边,比它大的都放它右边,这样就找到了这个数字最终的位置。

  • 如果这个位置正好是数组中间位置,那么这个数就是我们要的答案。
  • 如果这个位置在数组中间位置的右边,说明我们要找的数在这个数的左边,因此可以继续在其左边的数组中查找数组中间的那个数
  • 如果这个位置在数组中间位置的左边,说明我们要找的数在这个数的右边,因此可以继续在其右边的数组中查找数组中间的那个数
public class Solution {
    public int majorityElement(int[] nums) {
        int start=0;
        int end = nums.length-1;
        //利用快排思想找到中位数
        int index = partition(nums,start,end);
       while(index != nums.length/2){
             if(index > nums.length/2){
                 end = index-1;
                index = partition(nums,start,end);
            }else{
                start = index+1;
                index = partition(nums,start,end);
            }
        }
        return nums[index];

    }
    private int partition(int[] nums,int lo,int hi){
        int i = lo-1;
        int j = hi+1;
        while(true){
            while(nums[++i]<=nums[lo]){
                if(i == hi) break;
            }
            while(nums[lo]<=nums[--j]){
                if(lo == j) break;
            }
        //此时从左开始找到了第一个比基准数nums[lo]大的数
        //从右开始找到了一个比基准数nums[lo]小的数
        //如果i在j的左边,则此时需要交换这两个数
            if(i>=j) break;
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
        //此时代表i与j相等或i大于j,此时j左边的都比基准数小,i右边的都比基准数大

        int tmp = nums[lo];
        nums[lo] = nums[j];
        nums[j] = tmp;
        return j;

    }
}

很可惜的是这种方法在leetcode里一直超时…………

解法二

从另一个角度来想问题,数组中有一个数字出现的次数超过数组长度的一半,这说明它出现的次数比余下所有数出现次数的和还多
因此我们可以考虑在遍历数组时保存两个值,一个是数组中的一个数字,一个是这个数字出现的次数。
当我们遍历到下一个数字的时候:

  • 如果下一个数字和我们保存的相同,次数加1
  • 如果下一个数字和我们保存的不同,次数减1
  • 如果次数等于0了,说明保存的数和已经遍历过的其他数抵消了,那我们另下一个数字为要保存的数字。

最后剩下了次数没被抵消的数就是我们要找的数。

public class Solution {
    public int majorityElement(int[] nums) {
        int count = 1;
        int index = nums[0];
        for(int i = 1; i<nums.length; i++){
            if(count == 0){
                index = nums[i];
                count = 1;
            }else if(nums[i] == index){
                count++;
            }else{
                count--;
            }
        }

        return index;

    }

}

以上两种方法都是O(n)的时间复杂度。

follow-up

Majority Element II 中,题目改成了找出现次数超过1/3的数字

Given an integer array of size n, find all elements that appear more
than ⌊ n/3 ⌋ times. The algorithm should run in linear time and in
O(1) space.

这时,解法一明显已经不适用了,只能走解法二的思想。

题目给的hint是 How many majority elements could it possibly have?
对于出现次数超过1/3的数,最多可以有2个,因此我们设定两个候选数。

  • 记变量candidate1, candidate2为候选众数; count1, count2为它们对应的出现次数

  • 遍历数组,记当前数字为num[i]

  • 若num[i]与candidate1或candidate2相同,则将其对应的出现次数加1

  • 否则,若count1或count2为0,则将其置为1,对应的候选众数置为num[i]

  • 否则,将count1与count2分别减1

  • 最后,再统计一次候选众数在数组中出现的次数,若满足要求,则返回之。

public class Solution {
    public List<Integer> majorityElement(int[] nums) {
        List<Integer> result = new ArrayList<Integer>();
        if(nums.length == 0){
            return result;
        }
        //根据hint,最多有两个出现次数超过1/3的数
        //因此,统计两个候选数
        int count1 = 0;
        int count2 = 0;
        int candidate1 = nums[0];
        int candidate2 = nums[0];
        for(int i = 0; i<nums.length; i++){
            if(nums[i] == candidate1){
                count1++;
            }else if(nums[i] == candidate2){
                count2++;
            }else if(count1 == 0){
                candidate1 = nums[i];
                count1 = 1;
            }else if(count2 == 0){
                candidate2 = nums[i];
                count2 = 1;
            }else{
                count1--;
                count2--;
            }

        }
    count1 = 0;
    count2 = 0;
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] == candidate1)
            count1++;
        else if (nums[i] == candidate2)
            count2++;
    }
    if (count1 > nums.length / 3)
        result.add(candidate1);
    if (count2 > nums.length / 3)
        result.add(candidate2);
    return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值