Как найти количество инверсий в массиве?

Возможный дубликат:
Подсчет инверсий в массиве

Это вопрос о телефонном интервью: "Найдите количество инверсий в массиве". Я предполагаю, что они означают решение 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, очевидно, мало. Если это так, то это должно быть быстрее, чем другие подходы, поскольку нет рекурсии.