剑指Offer FindNumberMoreThanHalf 找出数组中出现次数超过一半的数字

题目描述:

数组中有一个数字出现次数【超过 不会等于】数组长度的一半,请找出这个数字

思路:

  1. 先对数组进行排序,出现次数超过一半的数组元素一定是该数组的中位数
    这里使用效率比较好的双指针快排
/**
     * 基于快排算法
     */
    public static int findNumberBySort(int[] array) {
        if (array == null || array.length == 0) {
            return -1;
        }
        //获取第一轮排序后 排序好元素的最终下标
        int resultIndex = partition(array, 0, array.length - 1);
        //中位数下标
        int middleIndex = array.length / 2;
        while (resultIndex != middleIndex) {//一直循环到找到中位数
            if (resultIndex < middleIndex) {//比中位数小
                resultIndex = partition(array, resultIndex + 1, array.length - 1);
            } else {//比中位数大
                resultIndex = partition(array, 0, resultIndex - 1);
            }
        }

        //检查中位数元素出现次数是否满足数组长度的一半
        if (checkNumCount(array, array[resultIndex])) {
            return array[resultIndex];
        } else {
            return -1;
        }
    }
 /**
     * 检查元素是否在数组中出现次数超过一半
     *
     * @param array  数组
     * @param result 目标元素
     * @return true/false
     */
    private static boolean checkNumCount(int[] array, int result) {
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == result) {
                count++;
            }
        }
        return count >= array.length / 2;
    }

    /**
     * 双指针快排
     */
    private static int partition(int[] array, int start, int end) {
        if (start >= end) {
            return -1;
        }
        int base = array[start];
        int leftIndex = start;
        int rightIndex = end;
        while (leftIndex != rightIndex) {
            while (leftIndex < rightIndex && array[rightIndex] >= base) {
                rightIndex--;
            }
            while (leftIndex < rightIndex && array[leftIndex] <= base) {
                leftIndex++;
            }
            if (leftIndex < rightIndex) {
                int temp = array[leftIndex];
                array[leftIndex] = array[rightIndex];
                array[rightIndex] = temp;
            }
        }

        array[start] = array[leftIndex];
        array[leftIndex] = base;

        return leftIndex;
    }

2.不使用排序的话 还可以根据由于该元素在数组中出现的次数大于一半,因此我们可以知道该元素出现的次数只和会大于其余元素出现次数的总和。
因此我们可以用一个变量count来计数,一个变量result用来标识最小元素的值。首先result为第一个元素的值,count初始值为1,遍历数组,如果count为0的话,证明之前的元素中出现最多的元素出现的次数和其余元素出现次数和抵消,从当前元素开始重新计算,即result设置为当前元素,count重新设置为1,如果count不为0的时候再进行判断,如果当前元素不等于result,那么count自减,否则count自增。

注意数组中有可能出现没有元素出现的次数超过数组的一半,因此最后需要进行判断

    public static int findNumberByCount(int[] array) {
        if (array == null || array.length == 0) {
            return -1;
        }
        int result = array[0];
        int count = 1;
        for (int i = 1; i < array.length; i++) {
            if (count == 0) {//如果count为0 则把result设置为当前元素 count设置为1
                result = array[i];
                count = 1;
            } else if (array[i] == result) {//如果当前元素和result相同 count+1
                count++;
            } else {
                count--;
            }
        }
        if (checkNumCount(array,result)){
            return result;
        }else{
            return -1;
        }
    }

测试:

@RunWith(Parameterized.class)
public class FindNumberMoreThanHalfTest {

    @Parameterized.Parameters
    public static List<Object[]> data(){
        List<Object[]> data = new ArrayList<>();
        data.add(new Object[]{new int[]{1,2,3,2,2,2,5,4,2}, 2});
        data.add(new Object[]{new int[]{1,2,3,6,8,5,5,4,2}, -1});
        data.add(new Object[]{null,-1});
        return data;
    }

    private final int[] array;
    private final int max;

    public FindNumberMoreThanHalfTest(int[] array, int max) {
        this.array = array;
        this.max = max;
    }

    @Test
    public void testFindBySort() throws Exception {
        int result = FindNumberMoreThanHalf.findNumberBySort(array);
        assertEquals(max,result);
    }

    @Test
    public void testFindByCount() throws Exception {
        int result = FindNumberMoreThanHalf.findNumberByCount(array);
        assertEquals(max,result);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值