面试题:如何找出数组里出现次数超过总数1/3的数

简介: 如果你每次从nums中拿出3个不一样的数作为一组,肯定会出现两种情况。一,nums被取空了,那么nums中每个数出现次数最多占总次数的1/3,写代码很好处理吧!! 二,还有剩余,这个情况就复杂了,有可能剩余多个,但是……但是,最多只可能剩余两种数。 为什么? 3个不同的数凑一组才能删掉,所以不可能删掉超过1/3的数。所以超过1/3的数肯定被剩下来,但是,剩下来的俩数并不一定都是超过1/3的,这点额外注意。

给你一个数组nums,如何找nums中出现次数超过总数的1/3的数,要求时间复杂度O(N)和空间复杂度O(1)。我觉得这不算是一道算法题,更像是一道智力题。接下来我先说下这道题怎么做,再谈谈我对此类题的看法。

 首先,看下题目要求,时间复杂度O(1),于是就可以排除一些常规的简单做法了,比如暴力(O(n^2)),排序(时间复杂度O(Nlogn)),计数(空间复杂度O(n))……脑子里回顾一遍所有的算法,似乎没啥算法能用的上(常见算法要么时间换空间,要么空间换时间),这个时候我们就要另辟蹊径了。。

 如果你每次从nums中拿出3个不一样的数作为一组,肯定会出现两种情况。一,nums被取空了,那么nums中每个数出现次数最多占总次数的1/3,写代码很好处理吧!! 二,还有剩余,这个情况就复杂了,有可能剩余多个,但是……但是,最多只可能剩余两种数。 为什么? 3个不同的数凑一组才能删掉,所以不可能删掉超过1/3的数。所以超过1/3的数肯定被剩下来,但是,剩下来的俩数并不一定都是超过1/3的,这点额外注意。 很容易举个例子, 比如


1 1 1 1 1 2 2 3 3 4 4 5

像上面这种情况,有可能剩下1 4,但4并没有超过1/3,所以还需要对剩下的俩数重新计数才能确认。

 看起来我们把原问题转换为如何快速高效的从数组中每次去掉3个不同的数,似乎又是一个难题。不就是俩数吗,我就把这俩数保存起来,再加俩计数器来计数他们俩最终还剩下多少个。这里也许很难理解,这么说吧,我先遍历nums,在遍历过程中如果凑够3个不一样的就丢掉这3个数。看下代码就很容易理解了。


import java.util.ArrayList;
import java.util.List;
class Solution {
    public List<Integer> majorityElement(int[] nums) {
        ArrayList<Integer> ans = new ArrayList<Integer>();
        if (nums == null || nums.length == 0)
            return ans;
        int a = nums[0], cnta = 0;  //我用a b分别表示两个不一样的数
        int b = nums[0], cntb = 0;
        for (int num : nums) {
            if (num == a) {
                cnta++;
                continue;
            }
            if (num == b) {
                cntb++;
                continue;
            }
            if (cnta == 0) {
                a = num;
                cnta++;
                continue;
            }
            if (cntb == 0) {
                b = num;
                cntb++;
                continue;
            }
            cnta--;
            cntb--;
        }
        cnta = 0;
        cntb = 0;
        for (int num : nums) {
            if (num == a) {
                cnta++;
            } else if (num == b) {
                cntb++;
            }
        }
        if (cnta > nums.length / 3) {
            ans.add(a);
        }
        if (cntb > nums.length / 3) {
            ans.add(b);
        }
        return ans;
    }
}


代码写的比较长,可能不是很好读,但思路是对的,最后那个if里我省略掉了一些代码,其实就是上文中说的情况一。

 其实这种题目还是比较考验应聘者的,首先要能想到要摒弃所学算法,可以证明应聘者对算法还是有一定理解的。如果应聘者真能想到上面的解法,说明思维还是比较灵活的。但是思维灵活和聪明还是有区别的,每个人处理问题的方式和其阅历有想当大的关系,我能想到这个解法,其实是因为我好像见过超过1/2的,当然你看过这个之后,你就能解决1/2,,1/4,1/5……的题了。。

目录
相关文章
|
19天前
|
算法
【数组相关面试题】LeetCode试题
【数组相关面试题】LeetCode试题
|
19天前
|
存储
力扣面试经典题之数组/字符串
力扣面试经典题之数组/字符串
27 0
|
19天前
|
算法 前端开发
经典面试题:扁平化嵌套数组
经典面试题:扁平化嵌套数组
24 0
|
19天前
|
存储 JavaScript 前端开发
【面试题】JS的14种去重方法,看看你知道多少(包含数组对象去重)
【面试题】JS的14种去重方法,看看你知道多少(包含数组对象去重)
|
19天前
|
前端开发 JavaScript Java
【面试题】说说 JavaScript数组常见的操作 (20个)
【面试题】说说 JavaScript数组常见的操作 (20个)
|
12天前
|
C++
【一刷《剑指Offer》】面试题 14:调整数组顺序使奇数位于偶数前面
【一刷《剑指Offer》】面试题 14:调整数组顺序使奇数位于偶数前面
|
12天前
|
算法 C++
【一刷《剑指Offer》】面试题 8:旋转数组的最小数字
【一刷《剑指Offer》】面试题 8:旋转数组的最小数字
|
19天前
|
JavaScript 前端开发 索引
经典面试题数组常用的方法
### 1.数组常用方法之 push()(==改变原数组,产生新数组==) - `push` 是用来在数组的末尾追加一个元素,返回添加以后的长度 ```javascript var arr = [1, 2, 3] // 使用 push 方法追加一个元素在末尾 arr.push(4) console.log(arr) // [1, 2, 3, 4] var res = arr.push(1,2,3,34); res//8 ``` ### 2.数组常用方法之 pop()(==改变原数组,产生新数组==) - `po
47 1
|
19天前
|
算法 C++ 索引
【力扣经典面试题】238. 除自身以外数组的乘积
【力扣经典面试题】238. 除自身以外数组的乘积
|
19天前
|
机器学习/深度学习 算法
【力扣经典面试题】189. 轮转数组
【力扣经典面试题】189. 轮转数组