Подтвердить что ты не робот

Извлечение 2 чисел n раз и возврат обратно в O (n) вместо O (n * log (n))

Я представляю проблему, которую показал мой профессор в классе, с моим решением O (n * log (n)):

Учитывая список номеров n, мы хотели бы выполнить следующие n-1 раз:

  • Извлеките из списка два минимальных элемента x,y и представим их
  • Создайте новый номер z, где z = x+y
  • Поместите z обратно в список

Предложите структуру данных и алгоритм для O(n*log(n)) и O(n)

Решение:

Мы будем использовать минимальную кучу:

Создание кучи за один раз займет только O (n). После этого извлечение двух минимальных элементов займет O (log (n)). Размещение z в куче принимало бы O (log (n)).

Выполнение вышеуказанных n-1 раз займет O (n * log (n)), так как:

O(n)+O(n∙(logn+logn ))=O(n)+O(n∙logn )=O(n∙logn )

Но как я могу это сделать в O (n)?

EDIT:

Говоря: "извлеките из списка минимальные элементы x,y и их), я имею в виду printf("%d,%d" , x,y), где x и y - наименьшие элементы в текущий список.

4b9b3361

Ответ 1

Это не полный ответ. Но если список был отсортирован, тогда ваша проблема будет легкой в ​​ O(n). Чтобы сделать это, упорядочьте все числа в связанном списке. Ведите указатель на голову и где-то посередине. На каждом шаге выньте два верхних элемента из головы, распечатайте их, продвиньте средний указатель до тех пор, пока он не появится, и вставьте сумму.

Начальный указатель будет перемещаться ближе к 2n раз, а средний указатель будет перемещаться примерно n раз, с n вставляет. Все эти операции O(1), поэтому сумма равна O(n).

В общем случае вы не можете сортировать во времени O(n), но есть ряд особых случаев, в которых вы можете. Поэтому в некоторых случаях это выполнимо.

Общий случай, разумеется, не разрешима во времени O(n). Почему нет? Потому что, учитывая ваш результат, со временем O(n) вы можете запускать выходные данные программы, составлять список попарных сумм по порядку и отфильтровывать их из вывода. Остается элемент исходного списка в отсортированном порядке. Это даст общий алгоритм сортировки O(n).

Обновление: меня попросили показать, как вы могли перейти от выхода (10, 11), (12, 13), (14, 15), (21, 25), (29, 46) к списку ввода? Фокус в том, что вы всегда держите все в порядке, тогда вы знаете, как выглядеть. При положительных целых числах следующая предстоящая сумма будет всегда находиться в начале этого списка.

Step 0: Start
  input_list: (empty)
  upcoming sums: (empty)

Step 1: Grab output (10, 11)
  input_list: 10, 11
  upcoming_sums: 21

Step 2: Grab output (12, 13)
  input_list: 10, 11, 12, 13
  upcoming_sums: 21, 25

Step 3: Grab output (14, 15)
  input_list: 10, 11, 12, 13, 14, 15
  upcoming_sums: 21, 25, 29

Step 4: Grab output (21, 25)
  input_list: 10, 11, 12, 13, 14, 15
  upcoming_sum: 29, 46

Step 5: Grab output (29, 46)
  input_list: 10, 11, 12, 13, 14, 15
  upcoming_sum: 75

Ответ 2

Это невозможно в общем случае.

В заявлении о проблеме говорится, что вы должны уменьшить свой массив до одного элемента, выполнив в общей сложности операции сокращения n-1. Поэтому число выполняемых восстановительных операций составляет порядка O (n). Для достижения общего времени работы O (n) каждая операция сокращения должна выполняться в O (1).

Вы четко определили свою операцию сокращения:

  • удалите 2 минимальных элемента в массиве и напечатайте их, затем
  • вставить сумму этих элементов в массив.

Если ваша структура данных была отсортированным списком, тривиально удалить два минимальных элемента в O (1) раз (вывести их из конца списка). Однако повторная установка элемента в O (1) невозможна (в общем случае). Как отметил Стив Джессоп, если вы можете вставить в отсортированный список в O (1) раз, то результирующие операции будут представлять собой алгоритм сортировки O (n). Но такого известного алгоритма нет.

Здесь есть некоторые исключения. Если ваши числа являются целыми числами, вы можете использовать "вставку радикса" для достижения вставок O (1). Если ваш массив чисел достаточно разрежен в числовой строке, вы можете вывести точки вставки в O (1). Существует множество других исключений, но все они являются исключениями.

Этот ответ не отвечает на ваш вопрос сам по себе, но я считаю, что он достаточно уместен, чтобы гарантировать ответ.

Ответ 3

Если диапазон значений меньше n, то это можно решить в O (n).

1 > Создайте массив mk размера, равный диапазону значений, и инициализируйте его на все ноль

2 > пересечь массив и значение приращения mk в позиции элемента массива.  Если элемент массива является [i], то приращение mk [a [i]]

3) Для представления ответов после каждого из операций n-1 выполните следующие шаги:

Есть два случая:

Случай 1: все из [i] положительны

        traverse through mk array from 0 to its size
        cnt = 0
        do this till cnt doesn't equal 2
          grab a nonzero element decrease its value by 1 and increment cnt by 1
        you can get two minimum values in this way
        present them 
        now do mk[sum of two minimum]++

Случай 2: некоторые из [i] отрицательны

        <still to update>

Ответ 4

O (nlogn) легко - просто используйте кучу, treap или skiplist.

O (n) звучит жестко.

https://en.wikipedia.org/wiki/Heap_%28data_structure%29
https://en.wikipedia.org/wiki/Treap
https://en.wikipedia.org/wiki/Skip_list