ElasticSearch的搜索和过滤

查询

Match

Match查询接收一个类型为text/numerics/dates的值,分析后返回一个查询,例如 {"match":{ k:v } }

默认的match查询是bool类型的,这意味着match提供的字符串会被分析器拆分成子串,然后构造成Bool查询,例如quick Fox会被拆成quick和fox。
因此match查询提供了一个operator参数,可以用于设置or/and来控制bool查询,默认是or。另外还有一个 minimum_should_match参数来控制should子句应该匹配的最少的数目。
例如有内容为Quick Fox的文档(并且不是Not_Analyzed的域),当提供v为fox one的条件搜索时,可以匹配结果,因为倒排链表保存的就是fox,而且match的内容fox one会被拆分成fox和one去匹配。

Match查询需要和Term查询区分,Term查询不会分析单词串,如果查找Fox,那么在倒排索引中就找不到数据。

zero_terms_query参数有none和all两个值,表示当所有词被分析器移除时(例如to be or not to be)查询应该返回所有文档还是什么都不返回。

Multi_match查询

基于query查询提供的查询。

1
2
query: v
fields: [k1,k2]

和query不同的是,fields的参数可以提供通配符支持,比如fields可以填入*name用于匹配last_name, first_name。由于query需要计算评分,所以这里可以指定某个field的权重。例如subject^3表示subject的权重为其余field的3倍。multi_match实际上会生成在dis_max查询下的多个match查询。可以通过把use_dis_max设为false来将dis_max转换为bool查询。

multi_match的type参数可以决定查询结果的排序。有以下几个值:

  1. best_fields:匹配所有field,但是用得分最高的match查询来决定整个查询的分数。tie_breaker参数可以让其他field的_score也参与计算。最终计算为Best_score + SUM _score * tie_breaker。
  2. most_fields:匹配所有field,然后加权平均,如果没有指定权重就直接使用算数平均值

Term查询

这个查询仅仅匹配给定字段中含有关键字的文档,而且是未经分析的关键字。Term查询还可以指定加权属性Boost(float)。

1
2
3
4
5
6
7
"term":{
"k":{
"value":v,
"boost":10.0
}
}
"term":{k:v}

Terms查询

和Term查询一样,关键字不予分析。提供多个关键字所有,可以配置minimum_match属性,表示至少有minimum_match个词条应该被匹配。

1
2
3
4
"terms":{
k:[v1,v2],
"minimum_match":1
}

Common查询

主要解决因为高频词(例如stop words)造成的查询问题,例如一个关键字a and b,如果使用正常的match查询,那么and被分析器拆开单独匹配文档时会匹配到大量的文档。因此需要对这类高频词进行处理。这里提供一个cutoff_frequency,定义一个值,来决定什么样的词是低频词,例如提供0.01,表示词频低于1%的为低频词。另外还可以提供high_freq_operator/low_freq_operator来决定词条间的关系,可以设置为and,or,例如high_freq_operator:and表示高频词组需要都出现,文档才能被匹配。

1
2
3
4
5
6
common:{
k:{
query:v,
cutoff_frequency:0.01
}
}

Match_phrase查询

类似于match查询,不过不需要指定operator。而是以slop的值来构建短语查询。
当slop为1时,表示词条和文档之间能有多少个未知词条。默认为0.表示 a b不能和a and b匹配。

Query String查询

Lucene式查询。query_string:{query: +k:v^10 -k:v +k:(+v +v) +v}。可以指定不带k时默认查询的k,default_field默认为_all。
lowercase_expand_terms表示是否需要将词条转换为小写再来查找,默认为true。
可以指定查询的fields。另外还有一个use_dis_max参数,值为bool。
Dis表示Disjunction,可以跨多个字段查询,每个字段有不同权重。Max表示对给定词条,直邮最高分的才在最后评分中,而不是简单求和。

此外还有一个simple_query_string。这个查询在query的参数有无效部分的情况下,会忽视无效部分,而不会抛出异常。

IDS查询

标志符查询。
ids:{ values: [id,id,id….] }
返回匹配这些id的文档。可以制定type。

Prefix查询

前缀查询。查询字符串前缀。查询的字符串不会被分析,所以quick Fox不可以被Fo匹配。

Fuzzy查询

模糊查询。可以让用户以crme查出crime这样的文档
value提供查询的词条。min_similarity表示最小相似度,对于字符串而言这应该在0-1之间,对数值而言,这是差值的绝对值,例如最小相似度是3,对于值20,可以匹配17-23之间的值,对日期而言,可以设为1d,1m,1s,表示时间相差长度。

Wildcard查询

通配符查询。支持通配符,?。匹配任意字符串,?匹配任意字符。

Range查询

查询一个字段在某个范围内的文档。使用gte,lte,le,te作为参数,具体范围作为值。

Dis Max查询

最大分查询。
和multi_match的best_field一样。用得分最高的match查询来决定整个查询的分数。tie_breaker参数可以让其他field的_score也参与计算。最终计算为Best_score + SUM _score * tie_breaker。

1
2
3
4
5
6
7
"query": {
"dis_max": {
"tie_breaker": 0.7,
"boost": 1.2,
"queries": []
}
}

正则查询

提供正则表达式查询。查询词条可以包含正则表达式。

Bool查询

提供should,must,must_not的子句。

Boosting查询

加权查询。
提供positive和negative查询。positive部分不改变_score,negative部分降低_score。需要提供negative_boost参数表示降分的程度。

过滤

查询用于使用不同条件得到结果及其评分,然而要在不影响评分的情况下获得子集,就需要过滤器。
过滤器不需要耗费CPU计算评分,因此相对速度比较快,而且过滤能够被索引。

过滤有两个大类型。
一个是post_filter,和query同级,执行操作时先通过query查询文档,再使用过滤器过滤。
而另外一种filtered过滤,内部包含query和filter,会先过滤再查询。性能稍微好一点。

过滤器类型大体和查询相似。

Range过滤器。

用于过滤范围。

Exists 过滤器。

提供一个field作为参数,要求返回的文档必须有该field

Missing过滤器

和exists相反,要求文档必须没有field字段。
另外还提供null_value参数,用于判断什么是“没有”该字段,例如null_value为0,表示所有field为0的文档或者没有该field的文档都会被返回。existence表示需要检查field的值,和null_value需要一起使用。

Script过滤器

用于计算过滤。script: "1000 - doc['num'] > 100"。找出1000-num大于100的文档。

Type过滤器

type: { value : FIELD }
限制返回的文档的TYPE

Limit过滤器

限定单个shard返回的文档数目。

And/Or/Not

可以使用逻辑过滤器,内部可以套嵌多个过滤器。

其余可以使用的查询都可以套嵌在Query中,功能一样,只是不影响评分。

Elastic Search Java API Section 1

配置

Maven配置。现在es的最新的release版本为1.7.1

1
2
3
4
5
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${es.version}</version>
</dependency>

客户端类型

Node Client

加入ES集群中,但是不持有数据,不会被选举成为Master节点(作为clients时),能够获取整个集群状态,执行API时可以减少一次网络跳跃。
因为Node Client拥有集群的数据,操作可以自动路由到操作最终会被执行的节点,而不需要制定double hop。
适用于客户端中需要少量生存时间较长的连接,并且Node Client相比Transport Client更加效率。

通过cluster.name加入集群。可以直接配置resoures/elasticsearch.yml设置cluster.name,也可以使用代码加入特定集群。

1
Node node = NodeBuilder.nodeBuilder().clusterName("yourclustername").node(); Client client = node.client();

如果希望这个节点不保存数据,不被选为Master,需要指定nodeBuilder.client(true)。或者将node.data设置为false。
data和client的区别在于,data只是不保存数据,但是可以被选为master,client不保存数据,也不会被选为master。
在需要进行单元测试时,可以将node设置为local模式。运行在本地JVM上,本地的两个服务可以组成一个集群。

Transport Client

不加入集群中,只有通信的作用。用于将应用和集群解耦,用于快速创建和销毁连接,更加轻量级。
在没有修改集群名字的情况下可以直接使用TransportClient。

1
2
3
4
5
6
Client client = new TransportClient()
.addTransportAddress(new InetSocketTransportAddress("host1", 9300))
.addTransportAddress(new InetSocketTransportAddress("host2", 9300));

// on shutdown
client.close();

如果集群名字不是默认的”elasticsearch”,就需要修改yml配置或者在代码中配置。否则会提示None of the configured nodes are available

1
2
3
Settings setting = ImmutableSettings.settingsBuilder().put("cluster.name", "slixurd").build();
Client client = new TransportClient(setting)
.addTransportAddress(new InetSocketTransportAddress("172.16.63.129", 9300));

客户端操作

索引

增加一个文档实际上就是将文档添加到一个特定的索引上。
生成Json文档有如下4种方式。

  1. 手写Json,用String自己生成一个Json。
  2. 使用Map,正如一般的kv对一样,该怎么写怎么写。
  3. Jackson,可以简单的将bean序列化为Json。

    1
    2
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString( bean );
  4. 使用Elasticsearch Helper

    1
    2
    3
    4
    5
    XContentBuilder builder = XContentFactory.jsonBuilder()
    .startObject()
    .field("k", "v")
    .endObject()
    String json = builder.string();

新建的API为:

1
2
IndexRequestBuilder prepareIndex(String index, String type, @Nullable String id);
IndexRequestBuilder prepareIndex(String index, String type);

可以通过client使用prepareIndex

1
IndexResponse response = client.prepareIndex("twitter","tweet","3").setSource(json).execute().actionGet();

SetSource函数可以接收一个Mapper,或者一个XContentBuilder,也可以接收一个字符串。
execute返回一个future的子类,actionGet得到ES包装后的一个Response。这个IndexResponse中包含index,id,type,version,created五个属性。

获取

1
2
GetResponse response = client.prepareGet(index, type, id).execute().actionGet();
GetResponse response = client.prepareGet(index, type, id).setOperationThreaded(false).execute().actionGet();

通过GetResponse保存获得的数据。prepareGet检索对应的文档。还可以使用setOperationThreaded控制执行的线程,默认为true,执行在其他线程上。

删除

1
DeleteResponse response = client.prepareDelete(index, type, id).execute().actionGet();

修改

通过UpdateRequest创建Request,然后使用client的update函数更新

1
2
3
UpdateRequest updateRequest = new UpdateRequest(index, type, id);
updateRequest.doc(jsonBuilder().startObject().field(key, value).endObject());
client.update(updateRequest).get();

也可以使用prepareUpdate函数,get相当于execute().actionGet()。

1
UpdateResponse response = client.prepareUpdate(index, type, id).setDoc(doc).get();

API还提供了一个类似Mysql的INSERT … ON DUPLICATE KEY UPDATE函数 upsert。

1
2
3
UpdateResponse response = client.prepareUpdate(index, type, id)
.setUpsert(XContentFactory.jsonBuilder().startObject().field(k1, v1).endObject())
.setDoc(XContentFactory.jsonBuilder().startObject().field(k1, v2).endObject()).get();

当index,type,id三者决定的唯一文档不存在时,那么插入k1,v1的这个对象,如果文档存在,则把k1更新为v2.

Bulk API

1
2
3
4
BulkRequestBuilder bulkRequest = client.prepareBulk();
bulkRequest.add(client.prepareIndex(index, type, id).setSource(doc1));
bulkRequest.add(deleteRequest);
BulkResponse bulkResponse = bulkRequest.execute().actionGet();

BulkResponse内部包含一个数组,为每一个失败的请求保留一份记录。
可以用于批量更新,批量删除,批量索引。

Elastic Search基本知识

安装

下载安装文件

wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.1.tar.gz

解压

tar xvf elasticsearch-1.7.1.tar.gz

安装管理软件Marvel

./bin/plugin -i elasticsearch/marvel/latest

客户端

node client

节点客户端以无数据节点身份加入集群,本身不存储诗句,但是知道数据的位置,并且能够转发请求道对应节点上。

transport client

不加入集群,只转发请求。
客户端都是以9300号端口交互,使用私有传输协议,ElasticSearch Transport Protocol。
http模式以9200端口交互。

操作

curl -X<VERB> '<PROTOCOL>://<HOST>/<PATH>?<QUERY_STRING>' -d '<BODY>'

VERB包括 GET,POST,PUT,HEAD,DELETE
BODY为JSON格式的请求主体。

Relational DB -> Databases -> Tables -> Rows -> Columns Elasticsearch -> Indices   -> Types  -> Documents -> Fields

关系类比。

一个集群可以包含多个索引,一个索引有多个类型,每个类型有多个文档,每个问的那个有多个字段。
一般来说,认为Document是最顶层结构/根对象 序列化成的JSON数据,而对象一般认为是一个JSON结构体,对象还能包含其他对象。
元数据主要有3个,_index,_type,_id。

创建

创建文档可以使用PUT,也可以使用POST,唯一的区别在于是否由用户提供_id

POST /website/blog/

使用POST可以由ES自己生成_id,避免由于_id相同导致的碰撞,id为22位的UUID
使用

PUT /website/blog/123?op_type=create

可以直接声明_id,但是如果id已经存在,则会替换旧的数据,_version版本号提高1,_create改为false,如果需要保证是创建而不是更新,可以使用显式的op_type参数,如果冲突,会返回409 Conflict,如果成功,会返回201 Created。
也可以使用简化

PUT /website/blog/123/_create

获取

GET /website/blog/123?pretty

可以获取文档。
参数可选
使用pretty可让文档显示的更加美观
使用_source可以控制_source字段返回的数据,ES可以仅仅返回用户需要的数据,例如_source=title,如果只使用_source,ES不返回metadata,只返回用户数据。
另外可以使用HEAD来确认文档是否存在,当返回为200时文档存在,返回为404时文档不存在

curl -i -XHEAD http://localhost:9200/website/blog/123

删除

DELETE /website/blog/123

使用DELETE来删除文档。不管文档是否删除成功,该文档的_version都会自增。

更新

更新可以直接使用PUT对已有数据进行更新。
也可以使用_update局部更新

搜索

空搜索

直接使用

GET /_search

响应内容中有 {took : times , hits : { total : n , hits : [ … ] }}
Took表示本次请求花费的毫秒,hits表示匹配到的文档。文档中包含一个_score评分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{    
"hits" : {
"total" :14,
"hits" : [
{
"_index":"us",
"_type":"tweet",
"_id":"7",
"_score":1,
"_source": {
"date": "2014-09-17",
"name": "John Smith",
"tweet":"The Query DSL is really powerful and flexible",
"user_id": 2
}

},

... 9 RESULTS REMOVED ...
],

"max_score" : 1
}
,

"took" :4,
"_shards" : {
"failed" :0,
"successful" :10,
"total" :10
}
,

"timed_out" :false
}

评分为relevance score,在提供了查询条件的情况下,文档按照相关性评分降序排列。
_shards表示参数查询的分片数。
time_out表示是否超时,如果要求响应速度,可以限制time_out,这样ES会在请求超时前返回收集到的结果

GET /_search?timeout=10ms

限定搜索

/index[,index]/_search 限制在几个索引间搜索数据

也可以使用通配符搜索。/g*/_search,以g开头的index中搜索。

/index[,index]/type[,type]/_search 在index索引中,搜索type中的文档。

分页

GET /_search?size=5&from=10

简单搜索

GET /index/type/search?q=key:value

表示搜索key字段包含value的文档

+k1:(v1 v3) -k2:v2 +date:>2014-09-10

表示必须含有k1:v1,v3和必须没有k2:v2,时间大于给定时间的文档

GET /_search?q=value

表示含有value的文档。因为ES会将所有的字段拼接起来,形成一个_all字段,最终被ES索引。

一般搜索结构:

1
2
3
4
5
6
7
8
9
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,
...
}
}
}

过滤

  • term : "term":{ k:v } 主要用于精确匹配,包括bool,日期,数字,未经分析的字符串。
  • terms : "terms":{ k:[v1,v2] } 可以用于精确匹配多个值。
  • range : "range": { k:{"lte":20,"gte":30} } 可以用于指定范围查找数据,范围操作符包含:gt,gte,lt,lte
  • exists|missing : "exists":{ "field":"k" } 这两个过滤语句仅用于已经找到文档后的过滤
  • bool过滤 :

    1
    2
    3
    4
    5
    6
    "bool": { 
    "must": {
    "term": { "folder": "inbox" }
    },
    "should": [{ … },{ … }]
    }

    可以用来合并多个过滤条件查询结果的布尔逻辑。
    must表示多个结果的完全匹配,must_not表示多个结果的完全匹配的否定,should表示至少有一个查询条件匹配

查询

  • match_all : "match_all": {} 表示匹配所有文档
  • match : match是一个标准查询
  • multi_match : "multi_match":{ k:v, k:[v,v] } 允许一次查询多个字段
  • bool查询 :

    1
    2
    3
    4
    5
    "bool": { 
    "must": {
    "match": { "title": "how to make millions" }
    }
    }

    bool查询相比bool过滤多了一部查询_score的步骤

组合查询

过滤和查询需要放置在对应的context当中,过滤对应filter,查询对应query
由于search API中只能使用query语句,所以多重查询中需要使用filtered来包含query和其他过滤语句。
例如同时使用match查询和term过滤。

1
2
3
4
5
6
"query": { 
"filtered": {
"query": { "match": { k:v } },
"filter": { "term": { k:v } }
}
}

如果想要匹配所有的文档,可以忽略query语句的match查询,这样filtered会默认补充一个match_all的查询。以下两句效果相同。

1
2
"query": { "filtered": { "query": { "match_all": { } } , "filter": { "term": { k:v } } }
"query": { "filtered": { "filter": { "term": { k:v } } }

另外,过滤语句中可以使用query方式代替bool过滤子句。
例如 "must_not": { "query" : { ... } } 但是这种方式使用比较少。

查询检测 - validate API

validate API可以用于检测查询语句是否合法。

GET /index/type/_validate/query <BODY>

如果需要查询具体错误信息,可以加上explain参数,query?explain
如果查询语句合法的情况下,explain会针对每一个index返回不同的描述。因为不同的index有不同的映射关系和分析器,例如tweet:powerful这样的查询语句,在一个分析器里可能查询powerful单词,在另外一个使用english分析器的index里就是查询power单词。

排序

默认排序: _score

一般情况下得到的文档都以_score降序排列,相关性高的排在前面。过滤语句不影响_score,如果使用了match_all或者隐式使用了match_all,那么所有的文档的得分都是1.

字段值排序

使用sort对字段值进行排序。

{ "query" : { … }, "sort": { "date": { "order": "desc" } } }

如果使用了sort排序,那么在没有显式指定track_scores为true的情况下,每一个文档的_score和查询的max_score都不会被计算。
因为相关性的计算比较消耗性能,如果指定了排序规则,就没有必要计算了。另外假如排序是date的情况下,date会被转成timestamp用于计算。

如果需要顺序排列时,可以使用简写。 "sort": "key"
如果需要多级排序,可以使用: "sort": [ k1:{"order": "desc" }, k2:{"order": "desc" } ]
如果需要排列的字段是一个数组,那么可以使用min, max, avg 或 sum这些模式来排序。"sort": { k1:{"order": "desc","mode": "min" } }

如果对于针对全文搜索而使用了analyzer的字段上进行排序,很难得到正确的结果。因此针对这些值,需要重新指定类型。
默认:

1
2
3
4
"tweet": {
"type":"string",
"analyzer": "english"
}

修改后:

1
2
3
4
5
6
7
8
9
10
"tweet": {
"type": "string",
"analyzer": "english",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}

tweet 字段用于全文本的 analyzed 索引方式不变。新增的 tweet.raw 子字段索引方式是 not_analyzed。
后面可以使用 "sort": "tweet.raw" 来对这个字段进行排序。

Bower

什么是Bower

Bower 是 twitter 推出的一款包管理工具,基于nodejs的模块化思想,把功能分散到各个模块中,让模块和模块之间存在联系,通过 Bower 来管理模块间的这种联系。Bower使用flat dependency tree

安装和使用

这里使用Ubuntu,安装Nodejs和包管理npm请使用apt-get。由于Ubuntu有个包叫node,所以直接运行bower会提示出错/usr/bin/env: node: 没有那个文件或目录。为了解决这个问题,需要做个软链。另外大部分的依赖包都是从git上面拉取下来的,这都快成为业界规范了,因此再装个git吧。

1
2
3
4
5
6
7
8
9
10
sudo apt-get install nodejs
sudo apt-get install npm
sudo apt-get install git
sudo npm install -g bower
//解决node文件不存在的问题
sudo ln -s /usr/bin/nodejs /usr/bin/node
//解决bower没有权限的问题
sudo chown $USER:$USER ~/.config/configstore/bower-github.yml
//需要运行在非SUDO模式下,否则会有ESUDO错误。
bower install jquery

缓存目录位于~/.config/configstore/bower_components。可以通过find自行查找find ./ -name bower_components

1
2
3
4
5
6
7
8
//列出所有当前路径下的包
bower list
//搜索
bower search bootstrap
//包信息检索

bower info bootstrap#3.0.0
//初始化创建bower.json
bower init

随后可以开始创建页面。如果当前目录没有出现bower_components这个文件夹,可以重新运行一次bower install package。
引用包的路径为bower_components/packagename/dist/filename

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!doctype html>
<html>
<head>
<title>Learning Bower</title>
</head>
<body>
<button>Animate Me!!</button>
<div style=”background:red;height:100px;width:100px;position:absolute;”>
</div>
<script type=”text/javascriptsrc=”bower_components/jquery/dist/jquery.min.js”></script>
<script type=”text/javascript”>
$(document).ready(function(){
$(“button”).click(function(){
$(“div”).animate({left:’250px’});
});
});
</script>

</body>
</html>

参考

http://bower.io/#getting-started
//这个html的引用路径出错
http://blog.fens.me/nodejs-bower-intro/

漫长的实习求职

经历了漫长的3月和4月,终于走完了实习校招季,下面做个小结以后再分别说各个公司

从最早3月初的广研笔试,到最迟4月下旬的WPS笔试,一直到4月23拿到企鹅offer,5月初拿到阿里offer

曾在被网易HR刷掉以后一度想放弃实习,好好蹲宿舍自己看书,网易是我最早走完所有流程的公司,因此也投入了很大的期望,当我得知他们已经签约我却被刷的时候那种失落感无发言表,当然,福无双至祸不单行,网易签约那天也是我广研终面那天,于是我带着绝望的心情去总监面,当他问及我遇到的最难的问题的时候,我轻蔑的说了:没有,都能google到的东西有什么难的,于是我又愉快的被广研刷掉了.

在4月份来回骑行深圳以后,我才振作起来,开始把面试官问道的一切我不知道的内容搞懂,确定自己的方向并且为面试做准备.在拿到腾讯offer后轻松迎战金山和阿里,由于STL基础实在不足被金山刷掉,阿里那边则飞快的完成面试,因为那天也是我腾讯签约的日子,结果我为了在阿里面试迟到了1个多小时.直到签约结束我才知道,原来只要和HR说自己已经拿到腾讯offer,就可以获得当场签约阿里的特殊优待,可惜我知道的太晚了.

好,回到总结.按照最后一场面试的时间排序:

  1. 腾讯广研:走完面试流程-笔试-初面-项目-总监面-在小组都收到拒信的情况下我被默拒
  2. 网易游戏TTT:走完面试流程-笔试-技术一面-技术二面-HR面-被拒
  3. 腾讯深圳:走完面试流程-笔试-技术一面-技术二面-技术三面-HR面-OFFER
  4. 金山WPS:笔试-技术一面-被拒
  5. 阿里巴巴:走完面试流程-笔试-技术一面-技术/HR同时二面-OFFER

按照笔试时间排序: 广研-网易-腾讯-阿里-金山

笔试难易度排行:网易游戏阿里巴巴广研腾讯金山WPS

但是就考试压力来说,阿里巴巴要和网易游戏换个位,阿里选错倒扣.

笔试

先从基础说起,其实笔试不需要非常熟悉的掌握那些知识,毕竟我们是在和别人比,因此只要能够比周围大部分都高那就足够了,不需要追求一定要拿高分,反正面试的时候不见得因为高分就有优势.

广研

(对了,13年和14年题目基本一致,15年如果你准备去广研并且对自己笔试没信心,务必请先搜索

  1. 计算ackerman(3,3),当然只有基础公式没有每一层的公式,只要花点时间就可以解决
  2. catalan数,求1,2,3,4,5顺序入栈那么出栈有几种排列.
  3. 26进制转换(手写代码,当然相当简单,而且实际上广研评分不计代码题
  4. SQL语句在数据库中的执行顺序(GROUP/HAVING
    其他的题目真的很基础,例如TCP/IP,OSI模型,排序稳定性,排序时间复杂度,排序空间复杂度,二分查找手写或者修改
    数据库,还有#define.

还有稍微问到一些LINUX相关的内容,例如time的结果:real,sys,user的意义,基础命令.目录结构,

此外还有一些计算机相关但不是非必学的内容,例如windows栈大小,这些只能靠平时积累(其实还是考前搜一下历年题目比较靠谱)

网易游戏TTT:

网易游戏的笔试题是我做过的题目里面最难的,先亮一道内存对齐的题目吧:

1
2
3
4
5
6
7
8
9
struct node{
 int a;
 short int b;
};

 node t;
 t.b=0x0102;
 char * p = (char*)
 cout(int)*(p+4);//写出结果

此外还有写出写出绕Y轴旋转的旋转矩阵,C指针的判断和处理,二分查找,快排,短路运算符及括号内运算次序,

这样的宏有什么错? #define assert(e) if(!e) assert_error(_ERROR_LINE), (实际上就是e外面缺括号)

类继承以及类继承以后对ctor/dtor的调用顺序,linux的fork执行顺序,bash的标准重定向(例如 ./program file 2&1 )

还有一些编程题,大部分都是N选1来写,写自己最熟悉的代码就好,约瑟夫环,纸牌顺子判断,程序改错

另外需要注意的是试卷分为两部分,基础部分如果分数不达标就直接PASS不看后面的题目

腾讯/金山

为什么放在一起写?因为这两个实在太简单了,简单的一个半小时的试卷我都在40分终左右交卷走人

另外腾讯笔试签了保密协议,所以大概说个方向:

笔试中规中矩,相比其他科技公司的笔试,腾讯笔试更像研究生考试多一些(什么,你说我都没考过研究生怎么知道像研究生考试?)

总而言之,涉及到各种基础题,计算机网络,操作系统,甚至连js都有,更别说C/C++了(反正还是注意指针,char,类等基本只是).不过其实只要上课有认真学,自己也稍有编码的话基本不成问题

金山WPS就更搞笑了,题目简单的无力吐槽,我觉得大二学生也能轻松过

大题都非常简单,例如对一个student的类进行函数补全及二次封装,几个长方形堆叠起来以后计算至少要多大的长方形才能覆盖住前面的长方形(提供每个长方形的x,y,w,h)

对SHAPE类,TRIANGLE,LINE类进行简化,这简直就是大一都会写的题.

最难的是最后一题,不过可以只写伪代码:根据字典(KV对)来进行翻译,最好能够实现最长匹配,例如(go 去 , home 回家 , go home 回家,这样遇到go home翻译为回家而不是去家),扩展是逆向翻译

阿里巴巴

对于选错倒扣这种考试方式,实在太考验心理了

du/df的区别,多路归并复杂度,快排复杂度,概率论,几道IQ题,跳跃链表求单次查找的平均复杂度,如何做均衡负载防止SPOF和保证健壮性.修改错误的二分查找代码.malloc,根据树的度求叶子数.50fps的640*360的24位真彩色视频能否在24Mpbs的蓝牙下传输,C的malloc/calloc/realloc.

有个比较有意思的题目:

某次比赛,按照强弱编号1-8.当编号强弱对比小于等于2的时候可能爆冷.先进行1/4,然后1/2,然后决赛,请问哪个获胜几率最高.

面试

面试只挑个别的来讲讲,很多大同小异的就没有必要复述了

比较常见的问题(一般都会根据简历来问,所以项目不熟悉请不要乱写):

  1. 介绍自己写过的比较得意的项目,详细叙述一下系统的结构以及你所负责的区域
  2. 你在项目中遇到了哪些困难,如何解决的(这个也算是广研总监唯一问的一道题)
  3. 最近有看什么技术书籍,有什么收获,挑几个用自己的语言描述一下问题及解决方案

有兴趣的可以看看一篇不错的文章 不是技术牛人,如何拿到国内IT巨头的Offer

每个公司都有自己不同的侧重方向,所以请找准重点.

以我后台开发的角度看,腾讯非常侧重项目,大片的时间叙述项目的结构和处理.网易侧重基础,而且问的很深入,无论是C/C++编程方面还是数据库,对项目也比较有兴趣.
阿里问的很均衡,一部分算法,一部分项目,特别喜欢问EPOLL,金山WPS除了问底层实现就是STL底层实现

对了,注意准备好自我介绍,部分面试官会要求自我介绍,但是也有很多面试官不让我自我介绍,比例大概在2:3的样子.

腾讯技术3面(BOSS面):

腾讯面试3场分了3天,中间间隔N天

3面的面试官是我未来部门的BOSS,他也是在我十几场面试生涯中,唯一一个做了自我介绍的面试官

然后开头就告诉我,会从5个方向来进行这次面试

  1. 设计与开发:请基于一种协议(TCP/HTTP etc)设计一个可扩展的网络协议,用登陆来作样例解释这个协议的字段和方法
  2. 项目经历:介绍项目系统结构,遇到困难点和如何解决
  3. 计算机基础:介绍数组,链表,树,哈希表,TCP.IP.UDP.HTTP的特点.字符编码的转换UNICODE,UTF-8,UCS-2,GBK,GB18030
  4. 业界视野:互联网产品有什么特点,与传统产品有什么不同
  5. 生涯规划:未来的发展路线

网易TTT:

技术一面和技术二面在一个早上内结束,如果一面失败就不会有机会二面

  1. 如何在一个语句内执行”有就更新没有就插入”. 实际上就是MYSQL扩展语法 INSERT … ON DUPLICATE KEY UPDATE …
  2. apache和mod-php之间怎么传输数据
  3. CI底层如何路由
  4. SOCKET的服务原语,实现,使用
  5. C++基础,例如复制构造函数,深浅拷贝,虚函数表的实现, struct和class的区别(本质只有访问权限的区别),NEW和MALLOC的区别
  6. AJAX实现原理和机制,JS的常用方法
  7. 数据库底层,例如分快读取,行储存,列储存,NOSQL和SQL等

金山WPS:

因为面试的时候我已经有了企鹅的offer,所以只是抱着经历人生的想法去的.不过这个面试官让我对金山的印象瞬间提高了几个档次
问题:

  1. 请问你用过智能指针吗?先讲一下用法,然后描述一下底层数据结构及实现(我讲了share_ptr)
  2. 请问你用过vector吗?讲一下底层实现然后手写一下用iter反转vector的代码
  3. 介绍一下常见的数据结构
  4. 手写memcpy,并问了需要注意什么地方(其实就是防止内存覆盖)
  5. 还有一些其他的一些算法和STL源代码,已经记不太清了