簡體   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)?

我有 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM