为了不断优化推荐效果,今日头条每天要存储和处理海量数据。假设有这样一种场景:我们对用户按照它们的注册时间先后来标号,对于一类文章,每个用户都有不同的喜好值,我们会想知道某一段时间内注册的用户(标号相连的一批用户)中,有多少用户对这类文章喜好值为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
解题方式1:时间复杂度O(n^2)
import java.util.*;
public class Main {
public static void main(String []args) {
Scanner sc = new Scanner(System.in);
int count = Integer.parseInt(sc.nextLine());//人数
//用户喜好度数组
int [] fav = new int[count+1];//牺牲掉空间0,从1开始计数
String[] str = sc.nextLine().split("\\s+");
for(int i = 1; i < count+1; i++) {
fav[i] = Integer.parseInt(str[i-1]);
}
int groups = Integer.parseInt(sc.nextLine());//组数
for(int i = 0; i < groups; i++) {
String[] g = sc.nextLine().split("\\s+");
int l = Integer.parseInt(g[0]);
int r = Integer.parseInt(g[1]);
int k = Integer.parseInt(g[2]);
int c = 0; //记录喜好数
for(int j = l; j <= r; j++) {
if(fav[j] == k)
c++;
}
System.out.println(c);
}
sc.close();
}
}
这种解法,在牛客网上跑由于时间复杂度问题,也仅能通过50%的测试用例
解法2:HashMap
import java.util.*;
public class Main {
public static void main(String []args) {
Scanner sc = new Scanner(System.in);
int count = Integer.parseInt(sc.nextLine());//人数
HashMap<Integer, List<Integer>> hs = new HashMap<>();
String[] str = sc.nextLine().split("\\s+");
for(int i = 1; i < count+1; i++) {//从1开始计数
int k = Integer.parseInt(str[i-1]);
if (hs.get(k) != null) {
hs.get(k).add(i);
hs.get(k).set(0, hs.get(k).size()-1);
}else {
List<Integer> ls = new ArrayList<>(2);
ls.add(1);
ls.add(i);
hs.put(k, ls);
}
}
int groups = Integer.parseInt(sc.nextLine());//组数
for(int i = 0; i < groups; i++) {
String[] g = sc.nextLine().split("\\s+");
int l = Integer.parseInt(g[0]);
int r = Integer.parseInt(g[1]);
int k = Integer.parseInt(g[2]);
if (hs.get(k) == null) {
System.out.println(0);
continue;
}
int len = hs.get(k).size();
if (hs.get(k).get(len-1) < l) {
System.out.println(0);
continue;
}
int c = 0;
for(int t = 1; t <= hs.get(k).get(0); t++) {
if(hs.get(k).get(t) >= l && hs.get(k).get(t) <= r){
c++;
continue;
}
if (r < hs.get(k).get(t)){
break;
}
}
System.out.println(c);
}
sc.close();
}
}
解法3:二分法+快排求解
(解法三可能有点问题等以后有空再做修改)
package test;
import java.util.Scanner;
public class UserFav {
public static class User{
int code;//用户标号
int k;//用户喜好
@Override
public String toString() {
return "User [code=" + code + ", k=" + k + "]";
}
}
public static void main(String []args) {
Scanner sc = new Scanner(System.in);
int count = Integer.parseInt(sc.nextLine());//用户量
User[] us = new User[count];
String[] ks = sc.nextLine().split("\\s+");
for (int i=0; i<ks.length; i++) {
User u = new User();
u.code = i;
u.k = Integer.parseInt(ks[i]);
us[i] = u;
}
quickSort(us, 0, us.length-1);
int gs = Integer.parseInt(sc.nextLine());
if (gs == 0) return;
for (int i=0; i<gs; i++) {
String[] inf = sc.nextLine().split("\\s+");
int lt = Integer.parseInt(inf[0])-1;
lt = lt > -1 ? lt : 0;
int rt = Integer.parseInt(inf[1])-1;
rt = rt > -1 ? rt : 0;
rt = rt <= us.length-1 ? rt : us.length-1;
int k = Integer.parseInt(inf[2]);
int p = binarySearch(us, 0, us.length-1, k);
int q = p+1;
int c = 0;
while (p >= 0) {
if(us[p].k != k)
break;
if (us[p].code <= rt && us[p].code >= lt)
c++;
p--;
}
while (q <= us.length-1) {
if (us[q].k != k)
break;
if (us[q].code <= rt && us[q].code >= lt)
c++;
q++;
}
System.out.println(c);
}
sc.close();
}
//快排(按喜好排序)
public static void quickSort(User[] arr, int l, int h) {
if (l == h) return;//递归出口
User tmp = arr[l];//取出低位数,作为本轮排序目标数
int low = l;
int high = h;
//开始寻找目标数位置
while(low < high) {
while (tmp.k < arr[high].k) {//从右往左
high--;
}
if (low < high) {
arr[low] = arr[high];
arr[high] = tmp;
low++;
}
while (tmp.k > arr[low].k) {
low++;
}
if (low < high) {
arr[high] = arr[low];
arr[low] = tmp;
high--;
}
}
if (l < low-1)
quickSort(arr, l, low-1);
if (high+1 < h)
quickSort(arr, high+1, h);
}
//param: arr,有序数组 l,左边界 r,右边界 k,查找值
public static int binarySearch(User[] arr, int l, int r, int k) {
int mid = (l+r)/2;//折半中间点
if (arr[mid].k == k) {
return mid;//成功查找,递归出口
}
if (l == r) {
return -1;//查找失败,递归出口
}
if (arr[mid].k > k) {//查找方向
return binarySearch(arr, l, mid-1, k);//不可能出现mid == r 或mid == l情况
}
return binarySearch(arr, mid+1, r, k);
}
}