简体   繁体   English

如何从给定范围 Q 查询中创建数组 [1, 2, 3, 1],其中 N 个元素每个 [1, 3] [2, 3] [3, 4] 小于 O(QN)?

[英]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)?

I have a Q number of queries containing N elements each.我有 Q 个查询,每个查询包含 N 个元素。 Queries will be range.查询将是范围。

[1, 3] [2, 3] [3, 4]

If we flatten out range queries we will get this.如果我们展平范围查询,我们会得到这个。

1, 2, 3, 2, 3, 3, 4

I am trying to find a solution to create a array given below.我正在尝试找到一种解决方案来创建下面给出的数组。

[1, 2, 3, 4] --> elements in range
[1, 2, 3, 1] --> their frequency array

Explanation -> Element 1 comes only one time in range queries, similarly, 2 comes two times in range queries, and so on, and at last 4 comes only one time.解释 -> 元素 1 在范围查询中只出现一次,类似地,2 在范围查询中出现两次,依此类推,最后 4 只出现一次。

Which gives a array of elements frequency as mentioned above.如上所述,它给出了一组元素频率。

But flattening out the range and iterating over it to create a array has a time complexity of O(QN) [1, 2, 3, 2, 3, 3, 4] --> [1, 2, 3, 1]但是将范围展平并对其进行迭代以创建数组的时间复杂度为 O(QN) [1, 2, 3, 2, 3, 3, 4] --> [1, 2, 3, 1]

I failed to optimize it, my question is - How can we do it in the least possible time complexity (at least less than O(QN)?我没有优化它,我的问题是 - 我们如何才能以尽可能低的时间复杂度(至少小于 O(QN)?

I see two possible approaches.我看到了两种可能的方法。 The first assumes one iteration through full range of every query.第一个假设通过每个查询的全部范围进行一次迭代。 It's efficient at small ranges, but not better than 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;
}

The second approach assumes determining the range for all queries altogether and then iterating through each element from the range, checking whether it's included in each of the queries.第二种方法假定完全确定所有查询的范围,然后遍历范围中的每个元素,检查它是否包含在每个查询中。 In this approach you don't need to iterate through full range of each query, so I believe this is below O(QN), assuming that the ranges overlap to some extent.在这种方法中,您不需要遍历每个查询的全部范围,所以我相信这低于 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;
}

I tested in practice and with your example the first approach is much faster, but with long and overlapping ranges the second wins (I tested for [4,50000] [300000,500000] [2,100000] [3,800] [5,100000] [6,100000] [70000,900000] [8,100000] )我在实践中进行了测试,并以您的示例为例,第一种方法要快得多,但是由于范围长且重叠,第二次获胜(我测试了[4,50000] [300000,500000] [2,100000] [3,800] [5,100000] [6,100000] [70000,900000] [8,100000] )

It should be possible to reach O(Q log(Q) + N) using a sweep .使用扫描应该可以达到 O(Q log(Q) + N) 。 The basic idea is to place the intervals on a number line.基本思想是将区间放在数轴上。 Starts and ends of the intervals are processed in ascending order while maintaining a count of "not yet closed intervals".间隔的开始和结束按升序处理,同时保持“尚未关闭的间隔”的计数。

The following code demonstrates this:下面的代码演示了这一点:

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; }
    }         
}

Outputs输出

[[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

(first line is the input; number and count on each following line) (第一行是输入;接下来每一行的数字和计数)

Complexity is determined by sorting in O(Q log(Q)) time and by emitting the counts for each number in O(N).复杂性是通过在 O(Q log(Q)) 时间内排序并通过在 O(N) 中发出每个数字的计数来确定的。 If I'm not wrong, complexity should be O(Q log(Q) + N).如果我没记错的话,复杂度应该是 O(Q log(Q) + N)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从排序数组中查找小于O(n)的唯一数字 - Finding unique numbers from sorted array in less than O(n) 如何添加,将n个数字乘以数组中的给定范围,并在O(n)时间内反转数组中的范围? - How to add, multiply n numbers to a given range in array and also reverse the range in the array in O(n) time? 在小于 O(n^2) 的范围内对给定的整数列表进行排名 - Ranking a given list of integers in less than O(n^2) 查找数组在小于O(n ^ 2)内重复的次数 - Find the number of times a number is repeated in an array in less than O(n^2) 给定排序列表如何创建在 O(log(N)) 时间内不会落在某个范围内的整数列表? - Given sorted list how to create list of integers that wont fall in certain range in O(log(N)) time? 如何比 O(N^2) 更快地获取给定数组的所有子数组? - How can I get all subarrays of a given array faster than O(N^2)? 在不到O(n)的时间内获得数据结构中元素之间的最小差异 - Get the smallest difference between elements in a data structure in less than O(n) time 鉴于0 &lt; k &lt; n,并且在java中的O(k log n)时间,如何在大小为n的排序数组中获得超过n / k次的任何integer? - how to get any integer in a sorted array of size n that appear more than n/k times, given that 0 < k < n, and in O(k log n) time in java? 比 O(n) 更好的范围交集算法? - A range intersection algorithm better than O(n)? 如何创建一个元素少于给定数组的新数组? - How do I create a new array with fewer elements than a given array?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM