Как найти количество инверсий в массиве?
Возможный дубликат:
Подсчет инверсий в массиве
Это вопрос о телефонном интервью: "Найдите количество инверсий в массиве". Я предполагаю, что они означают решение O (Nlog N). Я считаю, что это не может быть лучше, чем O (Nlog N), поскольку это сложность сортировки.
Ответы на аналогичный вопрос можно резюмировать следующим образом:
- Рассчитайте половину расстояния, на которое элементы должны быть перемещены для сортировки массива: скопируйте массив и отсортируйте копию. Для каждого элемента исходного массива
a[i]
найдите его позицию j
в отсортированной копии (двоичный поиск) и суммируйте половинки расстояний abs(i - j)/2
.
-
Изменить merge sort
: изменить merge
, чтобы подсчитать инверсии между двумя отсортированными массивами и запустить регулярный merge sort
с помощью этого измененного merge
.
Имеет ли смысл? Существуют ли другие (возможно, более простые) решения? Не слишком ли сложно провести собеседование с телефоном?
Ответы
Ответ 1
На самом деле это приложение алгоритма разделения и покорения, и если вы знакомы с ним, вы можете быстро найти решение.
Возьмем [1 3 8 5 7 2 4 6] в качестве примера, предположим, что мы отсортировали массив как [1 3 5 8] и [2 4 6 7], и теперь нам нужно объединить два массива и получить количество общих инверсий.
Поскольку у нас уже есть количество инверсий в каждом под-массиве, нам нужно только выяснить количество инверсий, вызванных слиянием массива. Каждый раз, когда элемент вставлен, например, 2 вставлен в [1 # 3 5 8], вы можете узнать, сколько инверсий существует между первым массивом и элементом 2 (3 пары в этом примере). Затем вы можете добавить их для получения количества инверсий, вызванных слиянием.
Ответ 2
Вы также можете использовать метод сортировки с подсчетом, если массив содержит только небольшие числа (например, если это массив символов):
inversions = 0
let count = array of size array.Length
for i = 0 to array.Length - 1 do
for j = array[i] + 1 to maxArrayValue do
inversions = inversions + count[j]
count[array[i]] = count[array[i]] + 1
В основном, учитывайте, сколько раз каждый элемент появляется. Затем на каждом шаге i
число инверсий, создаваемых элементом i
th, равно сумме всех элементов, превышающих i
, которые предшествуют i
, которые вы можете легко вычислить, используя счетчик, сохраняя.
Это будет O(n*eps)
, где eps
является областью элементов в вашем массиве.
Это, безусловно, проще, на мой взгляд. Что касается эффективности, то это хорошо, если eps
, очевидно, мало. Если это так, то это должно быть быстрее, чем другие подходы, поскольку нет рекурсии.