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

题目描述

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

方法1:基于partition函数的算法  

public class Test {
    
    public static void main(String[] args) {
        int[] a = {1,1,1,3,4};
        System.out.println(moreThanHalfNum(a));
    }

    /**
     * 1、对数组进行快速排序,那么位于数组中间的数字一定是那个出现次数超过数组长度一半的数字。
     * @param array
     * @return
     */
    private static int moreThanHalfNum(int[] array) {
        if(array == null || array.length <= 0) 
            return 0;
        int low = 0;
        int high = array.length - 1;
        int mid = array.length >> 1; 
        int index = partition(array, low, high);
        
        while(index != mid) {
            if(index < mid) {
                index = partition(array, index+1, high);
            } else {
                index = partition(array, low, index - 1);
            }
        }
        int result = array[mid];
        
        if(!checkMoreThanHalfNum(array, result)) {
            return 0;
        }
        return result;
    }
    
    private static boolean checkMoreThanHalfNum(int[] array, int result) {
        int times = 0;
        for(int i=0; i<array.length; i++) {
            if(array[i] == result) {
                times ++;
            }
        }
        
        if(times * 2 <= array.length)
            return false;
        return true;
    }

    private static int partition(int[] a, int low, int high) {
        int key = a[low];                 // 第一个数作为key,将数组划为两部分
        while(low < high) {                // 左右未相遇
            while(low < high && a[high] >= key)      // 先从数组右边往前寻找第小于key的数(必须从右开始),交换
                high --;                                
            a[low] = a[high];
            while(low < high && a[low] <= key)     // 再从左往右找大于key的数,交换
                low ++;
            a[high] = a[low];              
        }
        a[low] = key;   // key放在合适的位置,其左边都小于key,右边都大于key
        return low;
    }
}

方法2:

数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值: 一个是数组中的一个数字, 一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加 l :如果下一个数字和我们之前保存的数字不同,则次数减 1 。如果次数为0,我们需要保存下一个数字,并把次数设为 1 。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为 1 时对应的数字。

public class Test {
    
    public static void main(String[] args) {
        int[] a = {1,2,3};
        System.out.println(majorityElement(a));
    }
    
    public static int majorityElement(int[] a) {
        if(a == null || a.length <= 0)
            return 0;
        
        int candidate = a[0];  // 用于记录出现次数大于数组长度一半的值
        int nTimes = 1;  // 次数
        for (int i = 1; i < a.length; i++)
        {
            if (nTimes == 0)
            {
                candidate = a[i];
                nTimes = 1;
            }
            else
            {
                if (candidate == a[i])
                    nTimes++;
                else
                    nTimes--;
            }
        }
        
        if(!checkMoreThanHalfNum(a, candidate))
            return 0;
        return candidate;
    }
    private static boolean checkMoreThanHalfNum(int[] array, int result) {
        int times = 0;
        for(int i=0; i<array.length; i++) {
            if(array[i] == result) {
                times ++;
            }
        }
        
        if(times * 2 <= array.length)
            return false;
        return true;
    }
}
原文地址:https://www.cnblogs.com/zywu/p/5782013.html