英文:
How to write customer equalsTo/compareTo/hasCode for TreeMap to get Top N values
问题
问题是:客户想要查看到目前为止销售最多的物品。我们希望有效地向客户提供这些数据。
样本输入:
已售物品清单:
F|400
B|1000
A|500
A|600
C|1000
D|700
E|300
样本输出:
A|1100
C|1000
B|1000
D|700
我的想法是在第一个 API processInput()
中,使用一个映射将物品和价值关联起来,以跟踪到目前为止已经销售的所有物品,并将更新后的物品添加到最大优先级队列中。在第二个 API processQuery()
中,只需从队列中选择前 K 个物品。一个问题是在 processQuery
中,时间复杂度是 O(NlogN)
。
是否可能通过单个 TreeMap
来解决这个问题,通过覆盖 equalsTo
、compareTo
和 hasCode
,使 TreeMap 按值排序。这样我们只需迭代 TreeMap 并返回前 N 个物品?
英文:
The question is:<br/>Clients want to see which items are most sold so far. We want to provide this data to our clients efficiently.<br/>Sample input:<br/>List of sold items:<br/>F|400 <br/> B|1000<br/>A|500<br/>A|600<br/>C|1000<br/>D|700<br/>E|300<br/>Sample output<br/>A|1100<br/> C|1000<br/>B|1000<br/>D|700
My thought is in first API processInput()
, use a map of item and value to track all items sold so far and then add the updated item into a max priority queue. And in second API processQuery()
, just select top K from queue. One issue is in processQuery
, time complexity is O(NlogN)
.<br/><br/> Is it possible we solve this problem with a single TreeMap
by overriding equalsTo
, 'compareTo
, and hasCode
so that that treemap is sorted by value. So we just iterate over the TreeMap and return topN items?
答案1
得分: 0
假设输入是以提到的格式为例的字符串列表,第一步 processInput
是将输入列表转换为一些对象{name, num}的列表:
public static List<Obj> processInput(List<String> data) {
return Arrays.stream(data)
.map(s -> s.split("\\|"))
.map(arr -> new Obj(arr[0], Integer.parseInt(arr[1])))
.collect(Collectors.toList());
}
然后第二个方法按名称分组,累加总值,并返回类似的列表作为结果:
public static List<Obj> getTopTotals(List<Obj> data, int topCount) {
return data.stream()
.collect(
Collectors.groupingBy(Obj::getName, TreeMap::new,
Collectors.summingInt(Obj::getNum)
)) // 获得总值的 TreeMap<String, Integer>
.entrySet().stream()
.sorted((a, b) -> b.getValue().compareTo(a.getValue())) // 按值降序排序
.limit(topCount)
.map(e -> new Obj(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
英文:
Assuming that the input is a list of strings in the mentioned format, the first step processInput
is to convert the input list into list of some objects {name, num}:
public static List<Obj> processInput(List<String> data) {
return Arrays.stream(data)
.map(s -> s.split("\\|"))
.map(arr -> new Obj(arr[0], Integer.parseInt(arr[1])))
.collect(Collectors.toList());
}
Then the second method groups by the name, sums up the total value, and returns similar list as a result:
public static List<Obj> getTopTotals(List<Obj> data, int topCount) {
return data.stream()
.collect(
Collectors.groupingBy(Obj::getName, TreeMap::new,
Collectors.summingInt(Obj::getNum)
)) // get TreeMap<String, Integer> of totals
.entrySet().stream()
.sorted((a, b) -> b.getValue().compareTo(a.getValue())) // sort by value desc
.limit(topCount)
.map(e -> new Obj(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
答案2
得分: 0
你需要在两个方向上都实现高效的访问:当项目到达时,您需要根据它们的名称(A、B、C)获取聚合金额,但是为了对它们进行排序,将使用聚合金额。我认为您可以使用Map
来按名称查找项目,并使用SortedSet
来进行排序。由于金额本身不是唯一的(例如在示例中,B和C都为1000),所以排序也应该利用名称(因为名称是唯一的),这就是为什么对于排序部分而言,只使用一个集合就足够了:
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class Test {
static class Item implements Comparable<Item> {
final String name;
int amount;
Item(String name, int amount) {
this.name = name;
this.amount = amount;
}
public int compareTo(Item o) {
if (o.amount < amount)
return -1;
if (o.amount > amount)
return 1;
return o.name.compareTo(name);
}
public String toString() {
return name + "|" + amount;
}
}
Map<String, Item> itemmap = new HashMap<>();
SortedSet<Item> sorted = new TreeSet<>();
void add(String name, int amount) {
Item i = itemmap.get(name);
if (i != null) {
sorted.remove(i);
i.amount += amount;
} else {
i = new Item(name, amount);
}
itemmap.put(name, i);
sorted.add(i);
}
public static void main(String[] args) {
Test t = new Test();
t.add("F", 400);
t.add("B", 1000);
t.add("xA", 500);
t.add("xA", 600);
t.add("C", 1000);
t.add("D", 700);
t.add("E", 300);
System.out.println("itemmap: " + t.itemmap);
System.out.println("sorted: " + t.sorted);
}
}
它会产生如下输出(在 Ideone 上测试:https://ideone.com/2d79aC)
itemmap: {B=B|1000, C=C|1000, D=D|700, E=E|300, F=F|400, xA=xA|1100}
sorted: [xA|1100, C|1000, B|1000, D|700, F|400, E|300]
我故意将A
重命名为xA
,否则itemmap
将会是A|1100, B|1000, C|1000
,但这只是字母顺序的巧合(对几个连续的单个字母字符串进行哈希处理实际上并不能很好地混合顺序)。C和B以相反的顺序出现是因为这是您要求的。如果您希望它们是B,然后是C,请交换compareTo()
方法的返回值,或者只需将当前返回值添加一个负号。
英文:
You need efficient access in both directions: when items come, you need to get the aggregated amount based on their name (A,B,C), but for sorting them, the aggregated amount is going to be used. I think you could use a Map
for finding items by name, and a SortedSet
for sorting. As amounts themselves are not unique (like B and C have both 1000 in the example), sorting should also make use of the name (as those are unique), and that's why a set is enough for that part:
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
public class Test {
static class Item implements Comparable<Item> {
final String name;
int amount;
Item(String name,int amount){
this.name=name;
this.amount=amount;
}
public int compareTo(Item o) {
if(o.amount<amount)
return -1;
if(o.amount>amount)
return 1;
return o.name.compareTo(name);
}
public String toString() {
return name+"|"+amount;
}
}
Map<String,Item> itemmap=new HashMap<>();
SortedSet<Item> sorted=new TreeSet<>();
void add(String name,int amount) {
Item i=itemmap.get(name);
if(i!=null) {
sorted.remove(i);
i.amount+=amount;
} else {
i=new Item(name,amount);
}
itemmap.put(name,i);
sorted.add(i);
}
public static void main(String[] args) {
Test t=new Test();
t.add("F",400);
t.add("B",1000);
t.add("xA",500);
t.add("xA",600);
t.add("C",1000);
t.add("D",700);
t.add("E",300);
System.out.println("itemmap: "+t.itemmap);
System.out.println("sorted: "+t.sorted);
}
}
It produces the output (test on Ideone: https://ideone.com/2d79aC)
> itemmap: {B=B|1000, C=C|1000, D=D|700, E=E|300, F=F|400, xA=xA|1100}
> sorted: [xA|1100, C|1000, B|1000, D|700, F|400, E|300]
I deliberately renamed A
to xA
, otherwise the map would be A|1100, B|1000, C|1000
, but that's just a coincidence of alphabetical order (hashing a few consecutive, single-letter strings does not really mix the order). C and B come in this reverse order because that's what you have asked for. If you want them as B, then C, swap the compareTo()
, or just return the current one with a negative sign.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论