[英]How to create array [1, 2, 3, 1] from given range Q queries with N elements each [1, 3] [2, 3] [3, 4] in less than O(QN)?
我有 Q 个查询,每个查询包含 N 个元素。 查询将是范围。
[1, 3] [2, 3] [3, 4]
如果我们展平范围查询,我们会得到这个。
1, 2, 3, 2, 3, 3, 4
我正在尝试找到一种解决方案来创建下面给出的数组。
[1, 2, 3, 4] --> elements in range
[1, 2, 3, 1] --> their frequency array
解释 -> 元素 1 在范围查询中只出现一次,类似地,2 在范围查询中出现两次,依此类推,最后 4 只出现一次。
如上所述,它给出了一组元素频率。
但是将范围展平并对其进行迭代以创建数组的时间复杂度为 O(QN) [1, 2, 3, 2, 3, 3, 4] --> [1, 2, 3, 1]
我没有优化它,我的问题是 - 我们如何才能以尽可能低的时间复杂度(至少小于 O(QN)?
我看到了两种可能的方法。 第一个假设通过每个查询的全部范围进行一次迭代。 它在小范围内有效,但并不比 O(QN) 好:
int[] freqCount1 (final List<int[]> queries){
Map<Integer, Integer> results = new HashMap<>();
for(int[] q : queries){
for(int i = q[0]; i <= q[1]; i++){
if (!results.containsKey(i)){
results.put(i, 1);
}
else {
results.put(i, results.get(i) + 1);
}
}
}
int[] count = new int[results.size()];
List<Integer> resultsValues = new ArrayList<>(results.values());
for (int i = 0; i < resultsValues.size(); i++){
count[i] = resultsValues.get(i);
}
return count;
}
第二种方法假定完全确定所有查询的范围,然后遍历范围中的每个元素,检查它是否包含在每个查询中。 在这种方法中,您不需要遍历每个查询的全部范围,所以我相信这低于 O(QN),假设范围在某种程度上重叠。
int[] freqCount2 (final List<int[]> queries){
int min = queries.stream().map(q -> q[0]).min(Integer::compareTo).get();
int max = queries.stream().map(q -> q[1]).max(Integer::compareTo).get();
int range = max - min + 1;
int[] entries = new int[range];
List<Integer> countList = new ArrayList<>();
for (int i = 0; i < range; i++){
entries[i] = i + min;
countList.add(0);
}
for (int[] q : queries) {
for (int i = 0; i < range; i++) {
if (entries[i] >= q[0] && entries[i] <= q[1]) {
countList.set(i, countList.get(i) + 1);
}
}
}
List<Integer> countListFiltered = countList.stream()
.filter(integer -> integer > 0)
.collect(Collectors.toList());
int[] count = new int[countListFiltered.size()];
for (int i = 0; i < countListFiltered.size(); i++){
count[i] = countListFiltered.get(i);
}
return count;
}
我在实践中进行了测试,并以您的示例为例,第一种方法要快得多,但是由于范围长且重叠,第二次获胜(我测试了[4,50000] [300000,500000] [2,100000] [3,800] [5,100000] [6,100000] [70000,900000] [8,100000]
)
使用扫描应该可以达到 O(Q log(Q) + N) 。 基本思想是将区间放在数轴上。 间隔的开始和结束按升序处理,同时保持“尚未关闭的间隔”的计数。
下面的代码演示了这一点:
import java.util.*;
import java.util.stream.IntStream;
public class Ranges {
public static void main(String[] args) {
List<Range> ranges = List.of(
new Range(2,7), new Range(1,6), new Range(7, 8), new Range(1, 9)
);
System.out.println(ranges);
List<Event> events = createEvents(ranges);
int open = 0;
int start = 0;
for (Event event : events) {
switch (event.type()) {
case START:
if (open > 0) {
int end = event.value();
output(start, end, open);
start = end;
} else {
start = event.value();
}
open++;
break;
case STOP:
int end = event.value();
if (open == 1 || start != end) {
output(start, end + 1, open);
start = end + 1;
}
open--;
break;
}
}
}
static List<Event> createEvents(List<Range> ranges) {
List<Event> events = new ArrayList<>();
for (Range range : ranges) {
events.add(new Event(EventType.START, range, range.start()));
events.add(new Event(EventType.STOP, range, range.end()));
}
Collections.sort(events);
return events;
}
static void output(int start, int end, int count) {
IntStream.range(start, end).forEach(value -> System.out.printf("%d %d \n", value, count));
}
/* helper types */
enum EventType {START, STOP}
static class Range {
int start, end;
Range(int start, int end) {
this.start = start;
this.end = end;
}
int start() { return start; }
int end() { return end; }
public String toString() {
return "[" + start + ", " + end + "]";
}
}
static class Event implements Comparable<Event> {
EventType type;
Range range;
int value;
Event(EventType type, Range range, int value) {
this.type = type;
this.range = range;
this.value = value;
}
@Override
public int compareTo(Event e) {
return Integer.compare(value, e.value);
}
EventType type() { return type; }
Range range() { return range; }
int value() { return value; }
}
}
输出
[[2, 7], [1, 6], [7, 8], [1, 9]]
1 2
2 3
3 3
4 3
5 3
6 3
7 2
8 2
9 1
(第一行是输入;接下来每一行的数字和计数)
复杂性是通过在 O(Q log(Q)) 时间内排序并通过在 O(N) 中发出每个数字的计数来确定的。 如果我没记错的话,复杂度应该是 O(Q log(Q) + N)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.