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

这篇博客探讨了三种找出数组中出现次数超过一半的数字的方法:使用哈希表统计,数组排序以及摩尔投票法。哈希表法通过计数找出多数元素;排序法虽然简单但效率较低;摩尔投票法则是一种高效算法,通过票数正负判断不断缩减数组范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

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

描述

示例 1

限制

方法一:哈希表统计法

方法二:数组排序法

方法三:摩尔排序法


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

描述

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

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

示例 1

输入

[1, 2, 3, 2, 2, 2, 5, 4, 2]

输出

2

限制

1 <= 数组长度 <= 50000

方法一:哈希表统计法

我们用哈希表的键值不可重复来计数,当计数达到数组长度一半时返回当前元素。

class Solution {
    public int majorityElement(int[] nums) {
        if (nums.length==1) return nums[0];
        int target=nums.length/2;
        HashMap<Integer,Integer> map=new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])){//如果表中没有这个元素
                map.put(nums[i],1);//添加元素,个数为1
            }else{//如果表中已经有这个元素了,那么个数加1即可
                int times = map.get(nums[i]);
                if (times+1>target){
                    return nums[i];//如果出现次数超过长度一半了,直接返回当前元素
                }
                map.put(nums[i],times+1);//个数加1
            }
        }
        return 0;
    }
}

方法二:数组排序法

 当我们对数组进行排序,众数一定会在数组的中点处,否则不满足出现次数大于数组长度的一半。

很长时间没写排序算法了,写个快排练练手。

class Solution {
    public int majorityElement(int[] nums) {
        int arr[]=QuickSort(nums,0,nums.length-1);
        return arr[arr.length/2];
    }

    public int[] QuickSort(int[] arr, int low, int high){
        if (low<high){
            int curPos=Partition(arr,low,high);//找到第一个元素在数组中的位置
            QuickSort(arr,low,curPos-1);//递归对元素左边进行排序
            QuickSort(arr,curPos+1,high);//递归对元素右边进行排序
        }
        return arr;
    }

    public static int Partition(int[] arr, int low, int high){
        int curPos=low;
        int curVal=arr[low];
        for (int i = low+1; i <= high; i++) {
            if (arr[i]<curVal){
                curPos++;
                if (curPos!=i){
                    swap(arr,i,curPos);
                }
            }
        }
        arr[low]=arr[curPos];
        arr[curPos]=curVal;
        return curPos;
    }

    public static void swap(int[] arr, int a, int b){
        int temp=arr[a];
        arr[a]=arr[b];
        arr[b]=temp;
    }
}

 排序的方法就是时间太慢了。

方法三:摩尔排序法

本方法是最巧妙的,也是本题的最优解。

我们假设数组nums的众数为x,并且数组长度为n,此时如果我们给众数的票数记为+1非众数的票数记为-1,那么所有数字的票数和一定大于0

如果数组的前a个数字票数和为0,那么数组剩余n-a个数字的票数和一定仍然大于0,即后面n-a个数字的众数仍然为x

我们假设第一个数x0为众数,那么开始对票数进行累加,当票数和为0时我们就可以不看前面的数组,这里有两种情况:

  1. 第一个数x0确实为众数,将前面票数和为0的部分去掉之后,后面的数组众数仍然为x0
  2. 第一个数x0不是众数,那么去除前面的部分之后,后面的数组众数仍然不改变,只不过不是x0罢了

 这样我们可以一直缩减数组的空间,直到最后假设某个数xn为众数遍历完成后票数和为正,那么此时的xn就是我们要找的真正的众数。

class Solution {
    public int majorityElement(int[] nums) {
        if (nums.length==1) return nums[0];
        int sum=1;
        int target=nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (sum==0){//如果此时票数和为0,则更新众数为当前元素
                target=nums[i];
            }
            if (nums[i]==target){//如果为众数,票数+1
                sum+=1;
            }else{//如果为非众数,票数-1
                sum-=1;
            }
        }
        return target;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值