剑指Offer:面试题29——数组中出现次数超过一半的数字(java实现)

PS:在前几天的面试中,被问到了这个题。然而当时只能用最低效的方法来解。

问题描述:

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

思路1:

低效的做法:直接用哈稀表来存储<key, value>,并找出出现次数value大于等于一半的那个key。

代码:

import java.util.Map;
import java.util.HashMap;
public class Solution {
    boolean InvalidInput = false;
    public int MoreThanHalfNum_Solution(int [] array) {

        if(array == null || array.length == 0){
            InvalidInput = true;
            return 0;
        }
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        int max = -1;
        int ind = -1;
        for(int i = 0; i < array.length; i++){
            if(!map.containsKey(array[i])){
                map.put(array[i], 1);
            }else{
                map.put(array[i], map.get(array[i])+1);
            }

            if(max < map.get(array[i])){
                max = map.get(array[i]);
                ind = array[i];
            }
        }

        if(max > array.length/2){
            return ind;
        }else{
            return 0;
        }
    }
}

思路2:

—由于出现的次数超过了数组的一半,那么如果我们将数组排序,中间的那个数一定是要求的数字(前提是它一定存在)
—这里排序时采用快排的基本思想,随机选择一个数,调整数组使得它左边的数都小于它,右边的数都大于它。(参考Partition函数)(下一篇博客的代码中给出了关于Partition函数详细的注释)

import java.util.Map;
import java.util.HashMap;
public class Solution {
    static boolean InvalidInput = false;
    public int MoreThanHalfNum_Solution(int [] array) {

        if(array == null || array.length == 0){
            InvalidInput = true;
            return 0;
        }

        int middle = array.length >> 1;//设定中位数位置
        int start = 0;
        int end = array.length - 1;
        int index = Partition(array, start, end);//随机选择一个标杆,进行调整数组

        while(index != middle){//若所选的标杆最后位置不是中位数的位置,则根据情况对符合情况的部分进行再次调整
            if(index > middle){
                end = index - 1;
                index = Partition(array, start, end);
            }else{
                start = index + 1;
                index = Partition(array, start, end);
            }
        }

        int result = array[middle];

        //检查中位数出现的次数是否超过了一半
        if(!CheckMoreThanHalf(array, result)){
            result = 0;
        }

        return result;
    }

    public static boolean CheckMoreThanHalf(int[] array, int number){
        int times = 0;
        int length = array.length;

        for(int i = 0; i < length; i++){
            if(array[i] == number){
                times++;
            }
        }

        boolean isMoreThanHalf = true;

        if(times * 2 <= length){
            InvalidInput = true;
            isMoreThanHalf = false;
        }

        return isMoreThanHalf;
    }

    public static int Partition(int[] array, int start, int end){
        if(array == null || array.length == 0 || start < 0 || end < 0){
            InvalidInput = true;
            return -1;
        }

        int index = RandomInRange(start ,end);
        int tmp = array[index];
        array[index] = array[end];
        array[end] = tmp;


        int small = start - 1;

        for(index = start; index < end; index++){
            if(array[index] < array[end]){
                small++;

                if(small != index){
                    int temp = array[index];
                    array[index] = array[small];
                    array[small] = temp;
                }
            }
        }


        small++;
        tmp = array[small];
        array[small] = array[end];
        array[end] = tmp;


        return small;

    }
    public static int RandomInRange(int s, int e){
        int Min = s;
        int Max = e;
        int result = Min + (int)(Math.random() * ((Max - Min) + 1));
        return result;

    }
}

思路3:

根据数组特点找出O(n)的算法
1.数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有的数字出现的次数的和还要多。
2.因此我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。
3.当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1,否则减1.

代码:

import java.util.Map;
import java.util.HashMap;
public class Solution {
    static boolean InvalidInput = false;
    public int MoreThanHalfNum_Solution(int [] array) {

        if(array == null || array.length == 0){
            InvalidInput = true;
            return 0;
        }

        int result = array[0];
        int times = 1;
        for(int i = 1; i < array.length; i++){
            if(times == 0){
                result = array[i];
            }else if(array[i] == result){
                times++;
            }else{
                times--;
            }


        }

        if(!CheckMoreThanHalf(array, result)){
            InvalidInput = true;
            result = 0;
        }
        return result;
    }
   public static boolean CheckMoreThanHalf(int[] array, int number){
        int times = 0;
        int length = array.length;

        for(int i = 0; i < length; i++){
            if(array[i] == number){
                times++;
            }
        }

        boolean isMoreThanHalf = true;

        if(times * 2 <= length){
            InvalidInput = true;
            isMoreThanHalf = false;
        }

        return isMoreThanHalf;
    }
}

PS:检查出现频率最高的是否超过一半,也还是需要遍历一次数组的。

原文地址:https://www.cnblogs.com/wenbaoli/p/5655704.html