二分查找技巧---字节跳动2018校招算法方向(第二批)---用户喜好

Posted reflecter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分查找技巧---字节跳动2018校招算法方向(第二批)---用户喜好相关的知识,希望对你有一定的参考价值。

[编程题]用户喜好

时间限制:3秒

空间限制:262144K

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

输入描述:
输入: 第1行为n代表用户的个数 第2行为n个整数,第i个代表用户标号为i的用户对某类文章的喜好度 第3行为一个正整数q代表查询的组数  第4行到第(3+q)行,每行包含3个整数l,r,k代表一组查询,即标号为l<=i<=r的用户中对这类文章喜好值为k的用户的个数。 数据范围n <= 300000,q<=300000 k是整型
输出描述:
输出:一共q行,每行一个整数代表喜好值为k的用户的个数
输入例子1:
5
1 2 3 3 5
3
1 2 1
2 4 5
3 5 3
输出例子1:
1
0
2
例子说明1:
样例解释:
有5个用户,喜好值为分别为1、2、3、3、5,
第一组询问对于标号[1,2]的用户喜好值为1的用户的个数是1
第二组询问对于标号[2,4]的用户喜好值为5的用户的个数是0
第三组询问对于标号[3,5]的用户喜好值为3的用户的个数是2

解题思路:
刚开始看到题目,觉得是区间查询的题目,于是想线段树、RMQ那些,结果发现查找的k值不能预先存为状态。搜了一下题解发现只是用到二分查找的技巧,看看下图就明白了:

 

技术图片

 

 为了进行二分查找,我们对喜好值进行排序,但是注意到有查询区间,所以标号也必须一起排序。需要注意的是在喜好值相等的片段里标号也要排成递增次序,这个可以写个结构体(记为Num)然后重写比较函数。那么在标号按递增次序后,我们可以发现,如果要查找[2,6]区间里喜好值为2的人数,只需要二分查找k=2,L=2的上界,以及k=2,R=6的下界。

(也可以理解为先二分查找k所在的区间,然后二分查找L和R的位置,然后取其长度,但实际上只需要两次二分查找就行了,毕竟排序函数如果可以用比较函数把数组排好序,那么同样可以只用一次二分查找把边界找出来,因为它们都用到自定义的比较函数)

如图找到下界4和上界6,那么上界与下界之间的长度即为[2,6]之间的喜好值为2的人数,即答案为2。

 

需要用到的是二分查找的函数,这里复习一下:

lower_bound找到第一个大于或等于左端点的位置,

upper_bound找到最后一个小于或等于右端点的位置

 

记一次发现:在使用如下比较函数时,用上例查找到的下标为2,4,然后4-2=2,并不是找到3和4的下标。尝试在下标比较后加入等号,然后ansR-ansL+1,但是这样提交的程序是错误的,待发现原因......

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn=300000+5;
 7 
 8 struct Num{
 9     int id;
10     int val;
11      bool operator<(const Num &b)const{
12         if(val==b.val)return id<b.id;    //注意,加等号是不对的
13         else return val<b.val;
14     }
15 }num[maxn];
16 
17 int main()
18 {
19     int n;
20     scanf("%d",&n);
21     for(int i=0;i<n;i++){
22         scanf("%d",&num[i].val);
23         num[i].id=i;
24     }
25     sort(num,num+n);
26     int q;
27     scanf("%d",&q);
28     while(q--){
29         int l,r,k;
30         scanf("%d%d%d",&l,&r,&k);
31         l--; r--;
32         Num L,R;
33         L.id=l;   R.id=r;
34         L.val=R.val=k;
35         int ansL=lower_bound(num,num+n,L)-num;
36         int ansR=upper_bound(num,num+n,R)-num;
37         printf("%d
",ansR-ansL);
38     }
39 
40     return 0;
41 }

 

 

 

以上是关于二分查找技巧---字节跳动2018校招算法方向(第二批)---用户喜好的主要内容,如果未能解决你的问题,请参考以下文章

字节跳动2018校招算法方向(第一批) —— 1-最外层点

字节跳动2018校招算法方向(第一批) —— 2-最大值区间

今日头条2018校招后端方向(第二批)第一题 二分查找

2021校招 字节跳动提前批

❤️TikTok字节跳动编程题实战2022校招——吐血分享总结。

求职字节跳动2019校招机器学习算法工程师面试