剑指offer(39-数组中出现次数超过一半的数字) 题解

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

微信搜索【程序员画工师】关注更多Java编程技术、数据结构与算法、面试题相关内容。

题目

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路1-HashMap

借助于HashMap实现,遍历该数组,用哈希表记录每个元素出现的次数,每次更新次数时判断是否找到目标数字,找到则输出。时间复杂度O(n)、空间复杂度O(n)。

上代码

import java.util.HashMap;
import java.util.Map;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length <= 0){
            return 0;
        }
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int length = array.length;
        for(int i=0; i<length; i++){
            map.put(array[i], map.getOrDefault(array[i],0)+1);
            
            if(map.get(array[i])*2>length){
                return array[i];
            }
        }
        return 0;
    }
}

思路2-先排序

如果这个数组是一个排序的数组的话,由于目标在数组中出现的次数超过一半,不难想到数组中间的那个数字就是目标数字。
可得排序的思路:先排序,取中间的数,使用Arrays.sort()方法,底层是快排,时间复杂度为O(nlogn)。

上代码

import java.util.Arrays;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length <= 0){
            return 0;
        }
        Arrays.sort(array);
        int half = array.length >> 1;
        if(!checkHalf(array,array[half])){
            return 0;
        }
        return array[half];
    }
    public boolean checkHalf(int [] array,int num){
        int count = 0;
        for(int i = 0;i < array.length;i++){
            if(array[i] == num){
                count ++;
            }
        }
        if( count * 2 <= array.length){
            return false;
        }
        return true;
    }
}

思路3-Partiton

基于Partiton函数:同样是找中位数(长度为n的数组中第n/2大的数字),受快速排序算法的启发,先在数组中随机选一个数字,然后调整数组顺序,使得比选中数字小的数字都排在它的左边,比选中数字大的都排在它的右边。
如果选中的数字的下标是n/2,那么这个数字就是中位数;如果选中的数字的下标大于n/2,则中位数在它的左边,可以接着在它左边部分的数组中查找;如果选中的数字的下标小于n/2,则中位数在它的右边,可以接着在它右边部分的数组中查找。这是一个典型的递归过程。时间复杂度为O(n)。

上代码

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length <= 0){
            return 0;
        }
        int left=0,right=array.length-1;
        int index = partition(array,left,right);
        while(index!=array.length/2){
            if(index<array.length/2)
                index = partition(array,index+1,right);
            else
                index = partition(array,left,index-1);
        }
        int result = array[index]; 
        if(!checkHalf(array,result)){
            return 0;
        }
        return result;
    }
    public boolean checkHalf(int [] array,int num){
        int count = 0;
        for(int i = 0;i < array.length;i++){
            if(array[i] == num){
                count ++;
            }
        }
        if( count * 2 <= array.length){
            return false;
        }
        return true;
    }
    public static int partition(int[] nums,int left,int right){
        int pivot = nums[left];
        while(left<right){
            while (left<right && nums[right]>=pivot)
                right--;
            if(left<right)
                nums[left] = nums[right];
            while (left<right && nums[left]<pivot)
                left++;
            if(left<right)
                nums[right] = nums[left];
        }
        nums[left] = pivot;
        return left;
    }
}

思路4

根据数组的特点可得,数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数的和还要多。
所以有:初始假设第一个数出现次数最大,然后遍历每个数,如果遇到相同,则次数加1,否则次数减1,如果次数为0,则重置当前数为出现次数最多的数,那么要找的数字肯定是最后一次把次数设为1时的数字。
时间复杂度:O(n)

上代码

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length <= 0){
            return 0;
        }
        int result = array[0];
        int count = 1;
        for(int i = 0;i < array.length;i++){
            if(count == 0){
                result = array[i];
            }else if(result == array[i]){
                count ++;
            }else{
                count --;
            }
        }
        if(!checkHalf(array,result)){
            return 0;
        }
        return result;
    }
    public boolean checkHalf(int [] array,int num){
        int count = 0;
        for(int i = 0;i < array.length;i++){
            if(array[i] == num){
                count ++;
            }
        }
        if( count * 2 <= array.length){
            return false;
        }
        return true;
    }
}

注意

•判断数组是否有效:数组长度为0的特殊情况需要处理。
•如果数组中出现频率最高的数字都没有达到这个标准,就需要定义checkHalf函数来进行判断。

References

[1] 《剑指offer(第二版)》 何海涛著

程序员画工师公众号,获取更多详细Java、Python、C、前端、小程序、产品相关学习资料,欢迎交流
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值