二分查找技巧---字节跳动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\n",ansR-ansL);
38     }
39
40     return 0;
41 }

原文地址:https://www.cnblogs.com/reflecter/p/11802405.html

时间: 2024-08-29 16:07:20

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

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

面试问题总结. 问题:1. 自我介绍.2. 介绍了一下自己简历上的项目.3. SVM详细原理.4. Kmeans原理,何时停止迭代. 算法题:1. 一个随机整数产生器产生[1,5],如何设计一个产生[1,7]的随机整数产生器.解法:设k1,k2属于[1,5], 生成k3 = 5*(k1-1)+k2+1, 则k3属于[1,25], 将k3分成两个部分,[1,21]和[22,25]. 判断,若属于[1,21], 则令op=k3%7+1,op属于[1,7], 否则丢弃. 2. 给定一个旋转的有序数组,

二分查找真的那么简单吗?——算法第二章上机实践报告

一.        实践题目 改写二分搜索算法 (20 分) 题目来源:<计算机算法设计与分析>,王晓东 设a[0:n-1]是已排好序的数组,请改写二分搜索算法,使得当x不在数组中时,返回小于x的最大元素位置i和大于x的最小元素位置j.当搜索元素在数组中时,i和j相同,均为x在数组中的位置. 输入格式: 输入有两行: 第一行是n值和x值: 第二行是n个不相同的整数组成的非降序序列,每个整数之间以空格分隔. 输出格式: 输出小于x的最大元素的最大下标i和大于x的最小元素的最小下标j.当搜索元素在

字节跳动 2019 春季算法实习生在线笔试

1. 题目一 求最少收到多少硬币,即优先用大面额的硬币找零,类似于求一个数的个位十位百位. #include <iostream> #include <stdio.h> using namespace std; int main() { int n; scanf("%d", &n); int change = 1024 - n; int a = change / 64; int b = (change - a * 64) / 16; int c = (c

头条笔试题2018后端第二批

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

二分算法入门——二分查找

二分查找,无论是从名字还是理论都十分简单一个算法,其博大精深,简直恐怖.Jon Bentley:90%以上的程序员无法正确无误的写出二分查找代码. 别人不知道,反正我早上是写了好久,这个查找算法,将查找的复杂度从 o( n ) 降到了 o( logn ) ,当之无愧的的好算法,更是许多高级算法的优化策略之一. 二分查找之基本思路 虽然二分查找是一个很吊的算法,但是跟很多算法一样,需要使用的基础条件——序列有序! 先假设一个单调非增序列:1 2 3 4 5 6 ,求找到3的位置,地球人都会马上这么

十大基础实用算法之归并排序和二分查找

归并排序 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 算法步骤: 1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置 3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 4. 重复步骤3直到某一指针达到序列尾 5. 将另一序列剩下的所有元素直接复制到合并序列尾 用分治策略解决问题分为三步:分解

算法(第4版)-1.1.10 二分查找

总结:本小节通过二分查找的例子展示本书学习新算法的基本方法,研究新算法的原理.用例.必要性(模拟实际情况)和性能. 重点: 1.二分查找: import java.util.Arrays; public class BinarySearch { public static int rank(int key, int[] a) { int lo = 0; int hi = a.length - 1; while (lo <= hi) { // 被查找的键要么不存在,要么必然存在于a[lo..hi]

【算法】二分查找与暴力查找(白名单过滤)

二分查找与暴力查找. 如果可能,我们的测试用例都会通过模拟实际情况来展示当前算法的必要性.这里该过程被称为白名单过滤.具体来说,可以想象一家信用卡公司,它需要检查客户的交易账号是否有效.为此,它需要: 将客户的账号保存在一个文件中,我们称它为白名单: 从标准输入中得到每笔交易的账号: 使用这个测试用例在标准输出中打印所有与任何客户无关的账号,公司很可能拒绝此类交易. 在一家有上百万客户的大公司中,需要处理数百万甚至更多的交易都是很正常的.为了模拟这种情况,我们提供了文件largeW.txt(10

二分查找的改进--差值查找

差值查找 在二分查找中,我们每次比较都可以排除一半的数据量,这个已经是很高效了.如果利用关键字本身的信息,每次排除的数据量充分依赖于关键字的大小,则查找会更高效,这就是差值查找的思想. 下面通过示例代码,比较二分查找和差值查找的不同,在不同中领略差值查找的改良之处. #include <stdio.h> #include <stdlib.h> int InterSearch(int *array, int n, int key) { if (array && n &