Hello,大家好,我是鸭血粉丝~
最近朋友出去面试某大厂,收到一题笔试题,阿粉看了下还是挺有意思的,跟大家分享一下。
首先我们先来看下题目的要求:
现在一个文件,包含大量的 sku 数据, 我们需要针对这些数据,需要完成三道题目。
这里就不完整介绍三道题目,今天就介绍前两道题目。
题目1:
我们需要实现一个方法,从包含 sku 的文件中读取数据,并且逐条打印。「注意:假设 sku 数据很多, 无法将 sku 列表完全加载到内存中」
题目 2:
现在需要统计 sku 数据,假设所有sku的价格都是精确到1元且一定小于1万元,计算当前这堆数据中,经过排序后的中间的价格。比如价格为1、1、2、25、25、25、25,按照题目要求,是第4个价格【25】)
「注意:假设 sku 数据很多, 无法将 sku 列表完全加载到内存中」
题目一
题目一其实非常简单,读取文件数据,遍历打印即可。
不过这里需要一点,题目再三强调了,sku 数据很多数据,无法加载到内存。
也就是说我们不能将数据全部读到内存中,然后遍历打印。
这就要求我们只能通过「流的方式」,逐条读取,然后打印输出。
流的方式,可以想到使用 BufferedReader
方式,一行一行读取。
不过,使用BufferedReader
相关代码比较繁琐,由于当前笔试没要求 JDK 版本的,所以我们可以通过使用 JDK8 Files#lines
的流式读取的方式。
示例代码如下:
Files.lines(ResourceUtils.getFile("classpath:文件路径").toPath()) // 跳过标题头 .skip(1) // 遍历元素 .forEach(line -> { // 将一行的字符串转化为一个对象,然后打印输出 });
通过 Files#lines
的方式,简化代码的复杂度,其实翻阅一下底层的源码,其实 Files#lines
底层实际使用的是 BufferedReader
。
题目二
题目一其实比较简单,应该来说是个热身题。现在来说下题目二,难度就比较大了。
首先分析一下题目:
「现在需要统计 sku 数据,假设所有sku的价格都是精确到1元且一定小于1万元,计算当前这堆数据中,经过排序后的中间的价格。」
这道题目需要计算排在中间的价格,有的同学可能会想到,将 sku 数据全部读取一个数组中,然后按照价格做个排序,这处于数据中间不就是所需要的价格吗?
其实这么当然没问题,但是我们需要注意一个条件,题目给出的条件,「内存有限,无法加载全部的 sku 数到内存中」。
这就直接破坏上述的方法。
那怎么办?
阿粉刚开始想的是,这个问题不就是海量数据求 「Top K」 ,然后搜索一下相关这类题解决办法,很复杂,那些解题思路编码起来也很复杂。
再次阅读了一下题目,阿粉看到关键信息:
「假设所有sku的价格都是精确到1元且一定小于1万元」
也就是说价格最多只有 1 万个元素,那我们是不是可以这样?
我们使用一个 TreeMap
保存数据,其中 key
为价格,value
为这个价格出现的次数。
由于 TreeMap
自身就会按照 key 做排序,所以里面的数据天然有序。;
最后统计一下全部的数量,除以一半,自然就得到中间元素的位置。
然后遍历 TreeMap
,计算中间元素的对应的价格。
// 价格为 key,value 为 sku 出现的次数 TreeMap<Integer, Integer> treeMap = new TreeMap<>(); // 使用题目一的方法遍历读取文件,将数据加载到 treeMap 中 Files.lines(ResourceUtils.getFile("classpath:文件路径").toPath()) // 跳过标题头 .skip(1) // 遍历元素 .forEach(line -> { // 将一行的字符串转化为一个对象,然后打印输出 // 由于价格都是整数 // 这里使用 merge,如果当前这个价格在 map 中不存在,则值为 1,否则将调用后面的设置的函数 treeMap.merge(skuDO.getPrice().intValue(), 1, Integer::sum); }); int mid = treeMap.size() / 2; int midPrice = 0; int count = 0; for (Map.Entry<Integer, Integer> entry : treeMap.entrySet()) { // 第一次 count 大于等于 mid,代表中位数位于这个区间 if (count + entry.getValue() >= mid) { // 中间的价格,偶数的情况下随便选一个 midPrice = entry.getKey(); break; } count += entry.getValue(); } System.out.println("价格排序后在中间的价格为" + midPrice);
总结
这次笔试题,阿粉觉得设计还是挺好的,题目循序渐进,既考察了算法的思想,又可以看到面试人员的编码习惯,一箭双雕。
由于朋友是线下做的笔试题,所以题目相当充裕,也可以慢慢想,再不济还可以网上找找解题思路。
那如果你是在线笔试呢,如果碰到这类题目,没有思路怎么办?
那阿粉建议你跟面试官沟通一下,你可以讲下你对题目的理解,一些初步做法,然后也可以让其提供一些思路。
今天文章提供的代码可能并不是很完善,如果你有更好的解题思路,欢迎留言区指出。