Учитывая список и диапазон, найдите сумму на основе нового списка за меньшее время

Дан целочисленный список размера n и список диапазонов m, где каждый диапазон указывает начальный и конечный индексы входного списка.

Сначала создайте новый список, используя этот диапазон, например:

n=6, list = [1, 2, 3, 2, 4, 5]
m=4, ranges = [[0, 1], [3, 4], [0, 0], [3, 4]]

Чтобы создать новый список, мы перебираем диапазоны, то есть от i=0 to m, и выбираем элементы по этим индексам:

for i=0, [0,1], pick items from list[0] to list[1], and append to newList, so newList = [1,2]
for i=1, [3,4], pick items from list[3] to list[4], and append to newList, so newList = [1,2,2,4]
for i=2, [0,0], pick items from list[0] to list[0], so newList = [1,2,2,4,1]
for i=3, pick items list[3], list[4] so newList = [1,2,2,4,1,2,4]

Следующий цикл от i до 0 до n, если индекс i является частью какого-либо диапазона, упомянутого выше, добавьте 0 к результату, если он не является частью какого-либо диапазона, затем подсчитайте, сколько элементов в newList, значения которых меньше списка. [я]

initialize result = 0
for i=0, it is part of range [0,1],[0,0] so adds 0 to result
for i=1, it is part of range [0,1] so adds 0 to result
for i=2, it is not part of any ranges so count how many items are there in newList [1,2,2,4,1,2,4], whose values are less than list[2] = 3, we get [1,2,2,1,2], so 5
for i=3, it is part of range [3,4] so adds 0 to result
for i=4, it is part of range [3,4] so adds 0 to result
for i=5, it is not part of any ranges so count how many items are there in newList [1,2,2,4,1,2,4], whose values are less than list[5] = 5, we get [1,2,2,4,1,2,4], so 7

Result = 0 + 0 + 5 + 0 + 0 + 7 = 12

Я реализовал для этого код, но его обработка занимает больше времени.

Вот мой код:

import java.util.*;

public class Main {

    public static long solution(List<Integer> list, List<List<Integer>> ranges) {
        // Create a set to keep track of all indices that contribute to new list
        Set<Integer> contributingIndices = new HashSet<>();
        
        // TreeMap to maintain the frequency of elements in the new list
        TreeMap<Integer, Integer> freqMap = new TreeMap<>();
        
        // Populate the frequency map and contributing indices
        for (List<Integer> range : ranges) {
            int start = range.get(0);
            int end = range.get(1);
            for (int i = start; i <= end; i++) {
                freqMap.put(list.get(i), freqMap.getOrDefault(list.get(i), 0) + 1);
                contributingIndices.add(i);
            }
        }

        long result = 0;

        for (int i = 0; i < list.size(); i++) {
            if (!contributingIndices.contains(i)) {
                // Calculate the number of elements in map that are smaller than list[i]
                result += freqMap.headMap(list.get(i), false)
                                        .values()
                                        .stream()
                                        .mapToInt(Integer::intValue)
                                        .sum();
            }
        }
    
        return result;
    }
    
    static void case1() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        List<List<Integer>> ranges = Arrays.asList(
                Arrays.asList(0, 1),
                Arrays.asList(0, 2),
                Arrays.asList(1, 2)
        );
        System.out.println(solution(list, ranges)); // Output: 14
    }
    static void case2() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        List<List<Integer>> ranges = Arrays.asList(
                Arrays.asList(1, 2),
                Arrays.asList(1, 1),
                Arrays.asList(2, 2),
                Arrays.asList(3, 3),
                Arrays.asList(4, 4)
        );
        System.out.println(solution(list, ranges)); // Output: 0
    }
    
    static void case3() {
        List<Integer> list = Arrays.asList(1, 2, 3, 2, 4, 5);
        List<List<Integer>> ranges = Arrays.asList(
                Arrays.asList(0,1),
                Arrays.asList(3,4),
                Arrays.asList(0,0),
                Arrays.asList(3,4)
        );
        System.out.println(solution(list, ranges)); // Output: 12
    }

    public static void main(String[] args) {
        case1();
        case2();
        case3();
    }
}

Чтобы улучшить время, я попытался объединить диапазоны, затем начал создавать freqMap, но он дает мне неправильную частоту, поскольку интервалы объединяются, поэтому мы получаем неправильное количество частот.

Я ищу лучшее решение, которое требует меньше времени.

Я не могу понять ваш абзац «как только этот новый список будет сформирован». Требуется переписать.

Basil Bourque 03.09.2024 19:06

@BasilBourque, я обновил пост сейчас

Sid 03.09.2024 19:11

Если это своего рода вызов кода, предоставьте ссылку.

WJS 03.09.2024 19:26

Что такое вызов кода? Существуют веб-сайты, такие как LeetCode, которые используются для практики программирования или решения задач. Они представляют проблемы, которые следует решить путем создания кода. Если вопрос о переполнении стека связан с проблемой на таком сайте или иным образом имеет веб-страницу, описывающую проблему, было бы вежливо и, возможно, полезно включить ссылку на эту страницу в текст вопроса.

Old Dog Programmer 03.09.2024 20:27
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Насчет быстрее не уверен, надо мерить, но проще:

    public static long solution(List<Integer> list, List<List<Integer>> ranges) {
        Set<Integer> contributingIndices =
                IntStream.range(0, list.size())
                        .boxed()
                        .collect(Collectors.toSet());

        TreeMap<Integer, Integer> freqMap = new TreeMap<>();

        for (List<Integer> range : ranges) {
            int start = range.get(0);
            int end = range.get(1);
            for (int i = start; i <= end; i++) {
                freqMap.merge(list.get(i), 1, Integer::sum);
                contributingIndices.remove(i);
            }
        }

        return contributingIndices.stream()
                .flatMapToInt(i ->
                        freqMap.headMap(list.get(i), false)
                                .values()
                                .stream()
                                .mapToInt(Integer::intValue))
                .sum();
    }

Если у вас много дубликатов в списке, вы можете кэшировать эту часть.

freqMap.headMap(list.get(i), false)
       .values()
       .stream()
       .mapToInt(Integer::intValue))

Нет необходимости перебирать каждый элемент каждого диапазона.

Unmitigated 03.09.2024 20:57
Ответ принят как подходящий

Во-первых, мы можем использовать массив разностей, чтобы подсчитать, сколько раз каждый элемент содержится в диапазоне в O(n + m).

Затем создайте список всех элементов, содержащихся хотя бы в одном диапазоне, и отсортируйте его. Чтобы быстро ответить на запросы диапазона, также создайте массив совокупных (префиксных) сумм частот элементов в этом списке.

Наконец, для каждого элемента исходного списка, не входящего ни в один диапазон, выполняется двоичный поиск последнего элемента, содержащегося в диапазоне, меньшем, чем текущий элемент. Получите количество элементов меньше текущего элемента из массива сумм префиксов по индексу, найденному в результате двоичного поиска, и добавьте его к результату.

Общая временная сложность O(m + n log n).

public static long solution(List<Integer> list, List<List<Integer>> ranges) {
    var diff = new int[list.size() + 1];
    for (var range : ranges) {
        ++diff[range.get(0)];
        --diff[range.get(1) + 1];
    }
    var inRange = new ArrayList<Integer>();
    for (int i = 0; i < list.size(); ++i) {
        if (i > 0) diff[i] += diff[i - 1];
        if (diff[i] > 0) inRange.add(i);
    }
    inRange.sort(Comparator.comparing(list::get));
    var prefSum = new long[inRange.size()];
    long currTot = 0;
    for (int i = 0; i < inRange.size(); ++i) prefSum[i] = currTot += diff[inRange.get(i)];
    long result = 0;
    for (int i = 0; i < list.size(); ++i)
        if (diff[i] == 0) {
            int low = 0, high = inRange.size() - 1;
            while (low <= high) {
                int mid = low + high >>> 1;
                if (list.get(i) > list.get(inRange.get(mid))) low = mid + 1;
                else high = mid - 1;
            }
            if (high >= 0) result += prefSum[high];
        }
    return result;
}

Логика diff очень интересна, есть ли какое-нибудь название у этого алгоритма для создания массива diff, я имею в виду код for (var range : ranges) { ++diff[range.get(0)]; --diff[range.get(1) + 1]; } for (int i = 0; i < list.size(); ++i) { if (i > 0) diff[i] += diff[i - 1]; }

Sid 03.09.2024 22:47

@Sid Это просто массив разностей, одна из основных структур данных, обычно используемых в соревновательном программировании.

Unmitigated 03.09.2024 23:00

Другие вопросы по теме

Добавление DP в рюкзак 0/1
Написал программу для печати простых чисел. Как мне ее улучшить?
Нахождение максимального произведения элемента массива и расстояния
Эффективное интервальное хранение с использованием стандартной библиотеки C++
Самая длинная полная подпоследовательность упорядоченных гласных
Есть ли способ получить проекцию на заархивированные векторы std::tuple без лямбды?
Как оптимально разместить две точки на числовой прямой, чтобы минимизировать общее расстояние до набора заданных точек
Есть ли в этом алгоритме преобразования двоичного дерева поиска в отсортированный связанный список в Википедии ошибка?
Почему на этапе разделения быстрой сортировки используется одно условие остановки с двумя указателями L и R, а (L <= R)? Может ли это быть пока (L < R)?
Учитывая двоичную строку, подсчитайте количество плотных подстрок за время O (nlogn)