知乎数据仓库实习面试
- 1 自我介绍
- 2 代码题
- 2.1 单链表反转
- 2.2 SQL
- 3. 数学题
- 说一说假设检验有那几种?
- 3.1 定义
- 3.2 假设形式
- 3.3 假设的分类
- 我看你有建模经验啊,说说你都用什么模型了?
- 3.4 层次分析法
- 3.5 模糊聚类分析
- 4 项目经历
- 4.1 说说对Hadoop的理解
- 4.1.1 HDFS
- 4.1.2 MapReduce
- 4.2 zookeeper
- 4.3 都知道Hive是数据仓库,数据仓库和数据库的区别是什么?
- 4.4 HBase
- 4.4.1 预分区
- 4.4.2 rowkey设计
- 4.5 Kafka
- 4.5.1 内部实现原理的两种模式
- 4.5.2 kafka写入流程
- 4.5.3 消费者消费模型
- 5 描述题
- 5.1 寻找一个字符串中只出现一次的第一个字符
- 5.2 说说你对网络协议的理解(http TCP UDP IP)
- 6 MySQL模糊查询
- 6.1 like
- 6.2 rlike & regexp
- 7 Linux查看进程
- 8 进程和线程的区别?
这几天有点别的事,没有更新,今天更新一下吧。11月29日面试的知乎。
今天这个形式是微信面试。而且不是视频哦,是语音,而且是群聊。
HR、一轮面试官、二轮面试官、我。
知乎是真的高效,直接一面二面连起来开整!
这篇博客我就分代码和口头问题写吧:
因为其实代码题和问题是穿插着来的,已经过去两天了,所以都记不大清了!
他往死搂了我两个小时。很多问题已经不能全部记住了。
我能想起来多少就写多少吧,后续想起来再补充!
1 自我介绍
还是那一套懒得扯了,大家看我以前的面经。
2 代码题
2.1 单链表反转
面完字节之后复习过这个链表的反转,不过还是一下子没想起来!
public ListNode reverseList(ListNode n) {
ListNode newListNode = null; //新链表节点
ListNode temp = null; //临时节点,用来遍历原链表的,起到一个传递作用
while(n != null) { //如果当前节点不为空 则进行操作一波
temp = n.next; //1. 记录下一个,等会再回给n,起到一个传递的作用
n.next = newListNode; //2. 这两行就是每倒着放一个都到时候再接上(不好理解,慢慢想想,下面有个过程可以看看)
newListNode = n; //
n = temp; //传到下一个
}
return newListNode;
/**
* 链表 1-> 2 -> 3 -> 4 ->5
* temp = n.next temp = 2
* n.next = newListNode 1-> null
* newListNode = n new是 1
* n = temp n = 2
*
* temp = n.next temp = 3
* n.next = newListNode 2-> 1
* newListNode = n new是 2 -> 1
* n = temp n = 3
*/
}
然后他让我用递归实现:
/**
* 链表反转递归实现 Recursive 单词 递归的意思
* @param n 传入的节点
* @return
*/
public static ListNode reverseListRecursive(ListNode n){
if(n == null || n.next == null){
return n;
}
ListNode pre=reverseListRecursive(n.next);
n.next = n;
n = null;
return pre;
}
2.2 SQL
- 分数表socreTable:student class score 输出每个学生考试成绩前三的科目
-- 首先创建一个表格
CREATE TABLE scoreTable(
student VARCHAR(10),
class VARCHAR(10),
score INT
)
插入数据后,如下图:
-- 注意:MySQL中不支持row_number
SELECT * FROM
(SELECT st.* ,row_number
(PARTITION by student ORDER BY score DESC) as rowNum
from socreTable st)
where rowNum <= 3
2. 关注表:follow
字段:member_id ,followed_member_id
取出互相关注的用户
- 这个之前也问过 就是面试滴滴那次
select f1.* from follow left join follow f2 on f1.member_id = f2.followed_member_id
and f2.member_id = f1.followed_member_id
3. 数学题
说一说假设检验有那几种?
3.1 定义
假设检验就是根据某个样本总体首先进行全局评估,总结归纳出有关特征的假设,然后根据抽样得到几个个体进行判断是否有这个特征,来决定对这个假设是拒绝还是接受。
3.2 假设形式
H0:原假设
H1:备择假设
3.3 假设的分类
- T检验
用于样本含量较小,总样本方差未知的正态分布
用t分布理论来推断差异发生的概率,从而比较两个平均数是否有差异。 - Z检验(U检验)
实际问题中的大多数变量都服从或近似正态分布,U作为检验统计量与x的均值是等价的,而且U的分位数查表比较好查,根据样本观测值得到U的观测值,从而推断均值的显著性。 - F检验
联合假设检验。统计值服从F分布,分析超过一个参数的统计模型,分析其中的一个或者全部参数是否适合全部母体。 - 卡方检验
两个率或两个构成成比比较的卡方检验;多个或者多个构成成比比较的卡法检验。
我看你有建模经验啊,说说你都用什么模型了?
这里我说了层次分析法和模糊聚类分级法。自己点到连接里看吧,也是我自己写的。
3.4 层次分析法
3.5 模糊聚类分析
4 项目经历
是根据我的简历里来的,看到我简历里面写的是关于Hadoop很多,就问我了下面的这些问题:
4.1 说说对Hadoop的理解
Hadoop是一个分布式的基础框架,不用了解他的底层原理,是一个分布式的程序开发软件。使用集群的资源进行高速运算和存储。
- Hadoop具有4V特点:
- volumn 数据规模庞大
- Velocity 数据更新频繁
- Variety 数据类型多样
- Value 数据价值很高
- 特点
- 高可靠性
- 高效性
- 高容错性
- 高效性
- 成本低
4.1.1 HDFS
4.1.2 MapReduce
听学姐学长说,MapReduce已经不在企业里使用了,但是我简历那个项目里面写的有,所以他就问了。
主要两个部分嘛
自定义的Mapper和自定义的Reducer。
Mapper是按行传入的,传入的键值对,根据业务以键值对的形式传出。
传出后在Reducer端传入是以key为单位的,每个key是一条。因此Reducer是以按key传入的,因此Reducer端进行是合并操作。
4.2 zookeeper
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。
Zookeeper = 文件系统 + 通知机制。
- 设计模式角度理解:
基于观察者模式设计的分布式服务管理框架,负责存储和管理大家都关心的数据,然后接受观察者的注册,当数据的状态发生变化,就会通知已经在Zookeeper上注册的贯彻着做出相应的反应。
4.3 都知道Hive是数据仓库,数据仓库和数据库的区别是什么?
首先Hive是一个使用SQL的数据仓库组件,它节约了开发人员的学习成本,只需要编写SQL就能进行数据分析,其本质是将SQL转换成MapReduce程序、把数据分文件转换成数据库表。
- 数据仓库是数据库的升级,他的存储容量比数据库更加庞大
- 数据仓库更适合用来做数据分析,数据库更适合用来做数据存储。
- 数据仓库存储的是历史数据,数据库存储的是在线交易的数据。
- 数据仓库的基本元素是维度表,数据库的基本元素是事实表。
维度表和事实表?
- 事实表就是你要关注的内容。
- 维度表就是基于观察整个事务的角度,注重是从哪个角度观察这个内容的。
列如:
某地区某商品的销量,从地区的角度观察销量的。事实表就是销量表;维度表就是地区表。
- 数据仓库的组成部分包括:
- 数据抽取工具
- 数据库
- 信息发布系统
- 数据仓库管理
- 元数据
- 数据集市
- 访问工具
- 数据仓库的数据建模阶段:
- 业务建模
- 领域概念建模
- 逻辑建模
- 物理建模
- 数据仓库和数据库并不能相互取代,二者只是相辅相成的关系,数据仓库主要面向主题设计,数据库主要面向事务设计。
4.4 HBase
非关系型的列式存储的数据库,适合大数据分析。列族的每一个列都是存储的数据都是成对存储的。每一条数据都有唯一标识rowkey(行键)。
最有名的是rowkey设计和预分区,这里扯一扯。
4.4.1 预分区
我们知道HBase里的角色分为HMaster和HRegoinserver。
HMaster:
- 监控RegoinServer
- 处理RegoinServer的故障转移
- 处理元数据的变更
- 处理Regoin的分配和移除
- 在空闲的时候进行数据的负载均衡
- 通过Zookeeper发布自己的位置给客户端
HRegionServer:
- 负责存储HBase的实际数据
- 处理分配给他的Region
- 刷新缓存到的HDFS
- 维护HLog
- 执行压缩
- 负责处理Region切片
正如上述所说,HRegoinServer是负责他管理的Region。那每一个HRegionServer都负责一个区域,这个区域(Region)是以startRowKey和endRowKey来确定起始位置的,每存到一个数据根据他的rowKey将它交给相应的HRegionServer去管理,查询提取的时候也是由它来负责,则会大大提升效率。
create 'table4','info',SPLITS => ['10','20', '30', '40']
在网页端口可以看到分区
4.4.2 rowkey设计
有时候数据如果根据没有经过设计的rowkey去存储的时候,很容易造成热点问题(一个region数据很多,其他没有或者很少),这样造成的负载不均衡的问题不利于集群的性能提升。
rowkey设计原则:
整体分散 局部聚集 rowkey唯一
- 加盐
在rowkey中人为的设置编号,但是仍然保留rowkey的原id信息。甚至可以添加额外信息,人们可以看到这个rowkey就知道这条记录 存储的什么信息。 - rowkey是哈希处理
根据rowkey值算出哈希码,这样的哈希码是随机的,根据哈希码来村就会省很多事 - 字符串反转
- - 增加前缀
4.5 Kafka
分布式消息队列。
4.5.1 内部实现原理的两种模式
点对点模式
p2p point to point
一对一模式,消费者主动拉取消息数据,消息数据收到后再清楚。
发布订阅模式
一对多模式,消息数据产生后,推送给所有的订阅者。
4.5.2 kafka写入流程
4.5.3 消费者消费模型
Kafka 采用拉取模型, 由消费者自己记录消费状态,每个消费者互相独立地顺序读取每
个分区的消息。如下图所示,有两个消费者(不同消费者组)拉取同一个主题的消息,消费者 A 的消费进度是 3,消费者 B 的消费进度是 6。消费者拉取的最大上限通过最高水位(watermark)控制,生产者最新写入的消息如果还没有达到备份数量,对消费者是不可见的。这种由消费者控制偏移量的优点是: 消费者可以按照任意的顺序消费消息。比如,消费者可以重置到旧的偏移量,重新处理之前已经消费过的消息;或者直接跳到最近的位置,从当前的时刻开始消费。
5 描述题
5.1 寻找一个字符串中只出现一次的第一个字符
AABCBDAEFEF
则为 C
我说的用Set 和 Map Map用true和false来判断是否是第一次出现。
Set:不兼容重复元素;无序;这里我对他的遍历产生了疑惑,遍历Set的时候是按照存储的先后顺序吗?我自己写了一个程序验证:结果证明是按照存储顺序来的。
import java.util.HashSet;
import java.util.Set;
public class Test{
public static void main(String[] args) {
Set<Character> set = new HashSet<Character>();
set.add('A');
set.add('A');
set.add('B');
set.add('C');
set.add('B');
set.add('D');
set.add('A');
set.add('E');
set.add('F');
set.add('E');
set.add('F');
for(char c:set) {
System.out.println(c);
}
}
}
现在连编写代码完成刚刚那个问题
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Test{
public static void main(String[] args) {
// 用来存加入的数据
Set<Character> set = new HashSet<Character>();
// 键值对分别为字符数据 和 是否出现一次 仅出现1次 true 重复出现过则为false
Map<Character,Boolean> map = new HashMap<Character,Boolean>();
map.put('A', set.add('A'));
map.put('A', set.add('A'));
map.put('B', set.add('B'));
map.put('C', set.add('C'));
map.put('B', set.add('B'));
map.put('D', set.add('D'));
map.put('A', set.add('A'));
map.put('E', set.add('F'));
map.put('F', set.add('F'));
map.put('E', set.add('F'));
map.put('F', set.add('F'));
//如何遍历map?
// for (Map.Entry<Character, Boolean> entry : map.entrySet()) {
// System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
// }
for(char c:map.keySet()) {
if(map.get(c)) {
System.out.println("该字符串中只出现一次的第一个字符是:" + c);
return;
}
}
}
}
运行结果为:
该字符串中只出现一次的第一个字符是:C
5.2 说说你对网络协议的理解(http TCP UDP IP)
6 MySQL模糊查询
首先迷糊查询正则表达式是必不可少的。
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。 |
. | 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用象 ‘[.\n]’ 的模式。 |
[…] | 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。 |
[^…] | 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’。 |
p1|p2|p3 | 匹配 p1 或 p2 或 p3。例如,'z |
* | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。 |
- where后的条件中当我们判断字符串是否相等不可以用正则表达式
6.1 like
使用like的时候用的不是正则表达式,而是通配符,只支持两个:
- _
- %
SELECT follwering FROM follower where follwering LIKE '%s%'
6.2 rlike & regexp
rlike 和 regexp用法相同
- 模糊匹配,包含特定字符串
-- 查找字符串中含有gg的记录
SELECT follwering FROM follower where follwering REGEXP 'gg'
- 模糊匹配,以特定字符串开头
-- 查找以s开头的
SELECT follwering FROM follower where follwering REGEXP '^s'
-- 在这里使用like也可以
SELECT follwering FROM follower where follwering LIKE 's%'
- 模糊匹配 或关系
SELECT follwering FROM follower WHERE follwering regexp 'g|a'
- 模糊匹配,不包含特定字符串
SELECT follwering FROM follower WHERE follwering NOT rLIKE 's'
7 Linux查看进程
- netstat
- ps
8 进程和线程的区别?
. | 进程 | 线程 |
定义 | 操作系统分配资源的基本单位。 | 任务调度和执行的基本单位。 |
开销方面 | 每个进程有自己的独立的代码和结构空间,切换的开销比较大 | 线程是轻量级的进程,同一类线程共享代码和结构空间,切换的开销比较大 |
所处环境 | 每个操作系统中可以有多个进程(程序) | 每个进程(程序)中可以同时运行多个线程(由CPU调度,每个时间片只能运行一个线程) |
内存分配 | 系统在内存分配的时候会为每个进程分配一定的内存资源 | 线程不会被系统分配资源(CPU厨除外),线程所使用的资源来自本身所属的进程,同一线程组的线程可以共享资源。 |
包含关系 | 1. 没有线程的进程是单线程;2.一个进程可以包含多个线程,那么这个进程的执行不是一条线的,而是多条线共同完成的。 | 3. 线程是进程的一部分,因此线程也可以称为轻量级进程。 |