我和面试官侃大山,面试官却问我A+B?

88 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接:

面试侃大山

小晨从大三的暑假开始准备找工作,到现在,陆陆续续也投递了近百份简历,也面试过不少厂子,很多次自我感觉面试答的还不错,面试却没有下文,后来请教了参与去年秋招的学长W,W学长去年秋招斩获包括福报厂、鹅厂、宇宙厂等各个厂子的offer,W学长帮小晨一起复盘了最近2~3个月的面试,发现小晨基础知识掌握的很好、也有实习经验,但是每次面试过程都需要的手撕算法都磕磕绊绊,不够流畅,于是乎...
今天,距离上次小晨请教W学长已经过去了半个月,小晨正准备下午2点一家独角兽厂的面试,面试开始之前,小晨更新了自己的简历,下午2点,面试官老Q准时出现在在线房间,打开简历,LeetCode800几个大字映入眼帘,原来小晨最近半个月恶补算法题,此时正信心满满。

A+B问题求解

下午2.45,过去的45min中,小晨对老Q提出的基础知识对答如流,和老Q就实习项目侃侃而谈,相谈甚欢,作为现在面试的惯例,老Q准备以一道算法题结束今天的面试,想到小晨简历上的LeetCode800,老Q问到小晨:给你出道题,公司组织年会抽奖活动,每个员工随机获得一个数字作为幸运号码X,今年的抽奖形式有所变化,每一轮随机出现一个抽奖数O,任何两位员工幸运号码之和等于抽奖数即中奖,请你找出每一轮中奖的员工?
小晨一看,这不就是LeetcodeNO.1第一题A+B嘛,

image.png 心中窃喜,感觉这次面试稳了,于是乎小晨三下五除二,歘欻欻5分钟就写完了,代码如下:

class Solution {
    public int[] twoSum(int[] nums, int target) {
    
        int len = nums.length;
        for (int i = 0; i < len; ++i) {
            for (int j = i + 1; j < len; ++j) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}
  • 时间复杂度:O(N^2),空间复杂度O(1)

老Q抬了抬眼皮,说道:这个时间复杂度比较高,可以优化下嘛?
小晨一听,内心OS:早就知道你要问这个,^。^,于是乎想到W学长之前推荐的博客,想到了空间换时间,于是乎使用哈希表存储数组元素,KEY:幸运号码X,VALUE:员工编号i,遍历一遍数组即可活动A+B的结果,代码如下:

class Solution {
    public int[] twoSum(int[] nums, int target) {

        Map<Integer, Integer> indexMap = new HashMap<>();
        int[] ret = new int[2];
        for (int i = 0; i < nums.length; ++i) {
            int diff = target - nums[i];
            if (indexMap.containsKey(diff)) {
                ret[0] = indexMap.get(diff);
                ret[1] = i;
                break;
            }
            indexMap.put(nums[i], i);
        }

        return ret;
    }
}
  • 时间复杂度:O(N),空间复杂度O(N)

老Q这一看,准备结束今天的面试,老Q转念一想,想看看小晨发散思维如何,于是乎抱着打破砂锅问到底的想法,又给小晨出了一道题:

image.png

指定和的连续子数组

小晨思索一会,想到:题中子数组需要连续,即有多少个子数组和为K,朴素的想法是对每一个子数组进行求和比较,但是这样复杂度很高(O(N^3)),进一步思考可以避免一些重复求和过程,如

  • [0:4]=[0:3]+[4]
  • [1:4]=[0:4]-[0],因此,通过预处理:累加[0:i],遍历数组计算各个子数组的和,进行比较,代码如下:
class Solution {
    public int subarraySum(int[] nums, int k) {
        
        int[] arr = new int[nums.length];
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (i == 0) {
                arr[i] = nums[i];
            } else {
                arr[i] = arr[i-1] + nums[i];
            }
            
            if (arr[i] == k) {
                count++;
            }
        }
        
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j < i; j++) {
                if (arr[i] - arr[j] == k) {
                    count++;
                }
            }
        }
        return count;
    }
}
  • 时间复杂度:O(N^2),空间复杂度O(N)

老Q看到小晨的解答,皱了皱眉头,表示这个解法思路没问题,但是时间复杂度还可以再优化吗,提示小晨用哈希表试试,小晨思索良久:上述O(N^2)的解法中,主要的问题在于数组每个位置i的求解需要遍历i-1次计算子数组的和pre[i] - pre[j](j < i),想到老Q提示的哈希表,小晨一拍脑门,i处的问题求解不就是求解pre[j] = pre[i] - k的j的数量,如下图所示,使用哈希表记录[0:j]的前缀和。

image.png 代码如下:

class Solution {
    public int subarraySum(int[] nums, int k) {
        
        // 哈希表记录前缀和
        Map<Integer, Integer> preMap = new HashMap<>();
        int count = 0;
        int sum = 0;
        preMap.put(0, 1);

        for (int i = 0; i < nums.length; i++) {

            sum += nums[i];
            int curCount = preMap.getOrDefault(sum - k, 0);
            if (curCount > 0) {
                count += curCount;
            }
            preMap.put(sum, preMap.getOrDefault(sum, 0)+1);
        }
        return count;
    }
}
  • 时间复杂度:O(N),空间复杂度O(N)

老Q看到小晨的解答,露出了欣慰的笑容...

道阻且长,诸位共勉!