剑指offer | 面试题39:数组中出现次数超过一半的数字

转载本文章请标明作者和出处
本文出自《Darwin的程序空间》
本文题目和部分解题思路来源自《剑指offer》第二版

在这里插入图片描述

开始行动,你已经成功一半了,献给正在奋斗的我们

题目

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

解题分析

这个问题,解决不难,有很多种方案可以解决这个问题:

比如说我们可以使用哈希表(也就是HashMap),键为出现的数字,值为出现的次数,每遍历一个元素就把它的次数加1,直到有一个数字出现的次数大于数据的size,这个数就是众数,这种方式很好理解,时间复杂度为O(n),但是需要的空间也是O(n);

再比如我们可以使用排序数组的方法进行,排序后的数组,数组的中间数即为众数(出现最多的那个数),数组排序的时间复杂度为O(nlgn)

我们还可以使用partiton函数的做法,时间复杂度为O(n),但是会修改原有的数据结构,需要的可以自行去百度一下这种算法,但是面试的时候需要询问面试官是否可以修改原有的数据结构


笔者推荐的是一种叫做投票算法的实现;

就比如我们小学的时候选举班长,选举的时候如果有一个人获取了全班一半以上人支持的话,他就当选了;
比如参加选举的同学有四个[小A、小B、小C和小D]
如果最后是小A当选的话,那么一定小B、小C和小D加起来的票数还没有小A多,所以每次如果叫一个选择小B或小C或小D的和选择小A的出教室,最后教室里面剩下的一定是选择小A的,那么如果是每次叫两个选择不一样的人出教室,最后剩下的那个也一定是选择小A的。道理很简单,小B、小C和小D加起来都没人家多,现在还有可能小B和小C和小D自行消耗,就更不可能有小A的多了。
在这里插入图片描述

(图片来源于网络)

这就是投票算法。

所以我们只需要记住当前遍历数组的数字middle,和它出现的次数count,如果有和它不一样的,就把count–,如果count小于1,那么就换下一个数字,如果出现的和这个数字一样的,就把count加1,最后剩下的一定是数组中出现次数超过一半的数字,也就是众数。

但是要注意,这种做法,数组里面一定要有众数才行,就算没有众数,这种算法也会给你一个答案的,如果还要判断数组里面是否存在众数,那推荐你使用哈希表。

代码(JAVA实现)

ps:这里笔者使用的jdk为1.8版本

public class Offer39_Mode {

    public static void main(String[] args) {
        System.out.println(mode(new int[]{1, 1, 1, 2, 2, 3, 4, 5, 2, 1, 1, 1, 1}));
    }

    public static int mode(int[] arr) {
        if (Objects.isNull(arr) || arr.length == 0) {
            return -1;
        }
        // 目前出现次数最多的数字
        int middle = arr[0];
        // 统计出现次数,用来抵消其他的数字
        int count = 1;
        for (int i = 1; i < arr.length; i++) {
            int num = arr[i];
            if (count == 0) {
                middle = num;
                count = 1;
            } else if (num == middle) {
                count++;
            } else {
                count--;
            }

        }
        return middle;
    }
}

喜欢的朋友可以加我的个人微信,我们一起进步
发布了156 篇原创文章 · 获赞 19 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_36929361/article/details/104358767