Поиск k-ой порядковой статистики в двух массивах
| Задача: |
| Даны два отсортированных массива и размерами и соответственно. Требуется найти -ый порядковый элемент после их слияния. Будем считать, что все элементы в массивах различны и нумеруются с нуля. |
Содержание
Варианты решения
Наивное решение
Сольем два массива и просто возьмем элемент с индексом . Слияние будет выполнено за время , к тому же этот алгоритм использует дополнительной памяти.
Чуть менее наивное решение
Будем использовать два указателя, с помощью которых сможем обойти массивы, не сливая их. Поставим указатели на начало каждого из массивов. Будем увеличивать на единицу тот из них, который указывает на меньший элемент. После -ой итерации сравним элементы, на которых стоят указатели. Меньший из них и будет ответом. Таким образом, мы получим -ый элемент за шагов.
Еще одно решение
В первом массиве выберем элемент c индексом и бинарным поиском найдем во втором массиве позицию , на которой стоит наибольший элемент, меньший . Если , то мы нашли -ую порядковую статистику — это элемент . Иначе, если , то далее тем же способом ищем в массиве в диапазоне индексов , а если , то в диапазоне индексов . Решая задачу таким способом, мы получим асимптотику .
Совсем не наивное решение
Приведём теперь решение, работающее за время .
Для начала рассмотрим следующую ситуацию: пусть у нас есть элемент из массива и элемент из массива и они связаны неравенством . Тогда есть -ый порядковый элемент после слияния массивов. Это объясняется тем, что до -ого элемента идут элементов из массива , элементов из массива (включая сам элемент ). В итоге получаем . Принимая это во внимание, будем выбирать и таким образом, чтобы .
Подведем промежуточный итог:
- Инвариант
- Если , то и есть -ая порядковая статистика
- Если , то и есть -ая порядковая статистика
Итак, если одно из двух последних условий выполняется, то мы нашли нужный элемент. Иначе нам нужно сократить область поиска, как задумывалось в начале.
Будем использовать и как опорные точки для разделения массивов. Заметим, что если , то (иначе второе условие бы выполнялось). В таком случае на месте -го элемента может стоять максимум -ый порядковый элемент после слияния массивов (так произойдет в случае, когда ), а значит элемент с номером и все до него в массиве никогда не будут -ой порядковой статистикой. Аналогично элемент с индексом и все элементы, стоящие после него, в массиве никогда не будут ответом, так как после слияния на позиции будет стоять -ой порядковый элемент, порядковые номера остальных же будут еще больше. Таким образом, далее мы можем продолжать поиск в массиве только в диапазоне индексов , а в массиве — . По аналогии, если , то (иначе выполнялось бы третье условие). Аналогичными рассуждениями приходим к тому, что в таком случае дальнейший поиск нужно осуществлять в массиве в диапазоне , в массиве — .
Стоит отметить, что нам не нужно рассматривать элементы, стоящие и в том, и в другом массивах на позициях от -ой до конца (если такие есть), так как они тоже никогда не будут ответом. Поэтому первый раз запускаем нашу функцию от параметров .
int findKthOrderStatistic(int* a, int n, int* b, int m, int k):
if n == 1 // в этом случае можно сразу дать ответ
if b[k - 1] < a[0]
return b[k - 1]
else if a[0] < b[k - 2]
return b[k - 2]
else
return a[0]
if m == 1 // симметричен случаю с n = 1
return findKthOrderStatistic(b, m, a, n, k)
int i = n / 2
int j = (k - 1) - i // j > 0, так как i <= (k / 2)
if j >= m
return findKthOrderStatistic(a + i + 1, n - i - 1, b, m, k - i - 1)
// чтобы сохранить инвариант, сделаем a[-1] = -INF и b[-1] = -INF
int aiLeft = ((i == 0) ? INT_MIN : a[i - 1])
int bjLeft = ((j == 0) ? INT_MIN : b[j - 1])
if bjLeft < a[i] and a[i] < b[j]
return a[i]
else if aiLeft < b[j] and b[j] < a[i]
return b[j]
if a[i] < b[j]
return findKthOrderStatistic(a + i + 1, n - i - 1, b, j, k - i - 1)
else
return findKthOrderStatistic(a, i, b + j + 1, m - j - 1, k - j - 1)
Чтобы алгоритм работал за , будем передавать первым массивом в функцию тот, длина которого меньше. Тогда длина рассматриваемой области первого массива на каждой итерации уменьшается в два раза. После того, как она станет равна единице, ответ можно будет получить за несколько сравнений.