Halo

A magic place for coding

0%

找到喜好值为k的用户个数

题目

  字节跳动的经典笔试题:为了不断优化推荐效果,今日头条每天要存储和处理海量数据。假设有这样一种场景:我们对用户按照它们的注册时间先后来标号,对于一类文章,每个用户都有不同的喜好值,我们会想知道某一段时间内注册的用户(标号相连的一批用户)中,有多少用户对这类文章喜好值为k。因为一些特殊的原因,不会出现一个查询的用户区间完全覆盖另一个查询的用户区间。

  输入描述:第1行为n,代表用户的个数。第2行为n个整数,第i个代表用户标号为i的用户对某类文章的喜好度。第3行为一个正整数q代表查询的组数。第4行到第(3+q)行,每行包括三个整数lrk代表一组查询。查询的是在[l,r]中的用户对这类文章喜好值为k的用户的个数。

  输出描述:一共q行,每行一个整数代表喜好值为k的用户的个数。

1
2
3
4
5
6
7
8
9
10
11
12
Input:
5
1 2 3 3 5
3
1 2 1
2 4 5
3 5 3

Output:
1
0
2

思路

  最基本的思路是对于给出的查询区间的每个用户进行遍历,判断其喜好度是否与k相等,然后再进行计数。这种做法效率不高,它的效率受到了查询区间大小的限制。

  第二种方法更加高效,做法如下。我们首先同时记录用户的标号和喜好度(使用自定义结构体)。然后对喜好度进行排序。这样喜好度一样的用户就会放在相邻的位置。然后我们建立一个map,它的key是user的喜好度,value是在数组中相同喜好度用户的开始下标。然后进行查询操作,我们首先通过喜好度k在map中找到开始的下标,然后逐一遍历喜好度为k的用户,判断其标号是否在查询范围内。

  两种做法实质上是操作的顺序不一样。第一种做法是先判断范围再判断喜好度,第二种做法是先判断喜好度再判断范围。先后顺序的不同是提升效率的关键,因为喜好度相同的用户数量相对较少,这样遍历可以大大减少循环的次数。


实现

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;

struct Node {
int index;
int key;
Node(int index_, int key_) {
index = index_;
key = key_;
}
};

bool cmp(const Node &a, const Node &b) {
return a.key < b.key;
}

int main() {
int n;
cin >> n;
vector<Node> user;
for (int i = 1; i <= n; i++) {
int tmp;
cin >> tmp;
Node node(i, tmp);
user.push_back(node);
}
// 从小到大
sort(user.begin(), user.end(), cmp);
//cout << user[0].key << endl;
//cout << user[1].key << endl;

map<int, int> m; // <key, first_idx>
for (int i = 0; i < user.size(); i++) {
int key = user[i].key;
if (m.count(key) > 0) {
continue;
}
else {
m[key] = i;
}
}
int q;
cin >> q;
while (q--) {
int l, r, k;
cin >> l >> r >> k;
int count = 0;
if (l > r || m.count(k) < 1) {
cout << 0 << endl;
continue;
}
int first_idx = m[k];
for (int i = first_idx; i < user.size() && user[i].key == k; i++) {
if (user[i].index >= l && user[i].index <= r) {
count++;
}
}
cout << count << endl;
}

return 0;
}

总结

  这道题目看起来比较困难,因为用第一种做法很容易超时。但是理清思路后,先判断喜好度,再判断范围,这样的话就能够减少循环的次数。希望这篇博客能够帮助到你,谢谢!

Welcome to my other publishing channels