Поиск k-ой порядковой статистики в двух массивах — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Варианты решения)
(Варианты решения)
Строка 21: Строка 21:
 
Итак, если одно из двух последних условий выполняется, то мы нашли нужный элемент. Иначе нам нужно сократить область поиска, как задумывалось в начале.  
 
Итак, если одно из двух последних условий выполняется, то мы нашли нужный элемент. Иначе нам нужно сократить область поиска, как задумывалось в начале.  
  
Будем использовать <tex>i</tex> и <tex>j</tex> как опорные точки для разделения массивов. Заметим, что если <tex>a[i] < b[j]</tex>, то <tex>a[i] < b[j - 1]</tex> (иначе второе условие бы выполнялось). В таком случае на месте <tex>i</tex>-го элемента может стоять максимум <tex>i + (j - 2) + 2 = (i + j)</tex>-ый порядковый элемент после слияния массивов (так произойдет в случае, когда <tex>a[i] > b[j - 2]</tex>), а значит элемент с номером <tex>i</tex> и все до него в массиве <tex>A</tex> никогда не будут <tex>k</tex>-ой порядковой статистикой. Аналогично элемент с индексом <tex>j</tex> и все элементы, стоящие после него, в массиве <tex>B</tex> никогда не будут ответом, так как на позиции <tex>j</tex> будет стоять <tex>(i + j + 2)</tex>-ой порядковый элемент после слияния, порядковые номера остальных же будут еще больше. Таким образом, далее мы можем продолжать поиск в массиве <tex>A</tex> только в диапазоне индексов <tex>[i + 1, n - 1]</tex>, а в массиве <tex>B</tex> - <tex>[0, j - 1]</tex>.
+
Будем использовать <tex>i</tex> и <tex>j</tex> как опорные точки для разделения массивов. Заметим, что если <tex>a[i] < b[j]</tex>, то <tex>a[i] < b[j - 1]</tex> (иначе второе условие бы выполнялось). В таком случае на месте <tex>i</tex>-го элемента может стоять максимум <tex>i + (j - 2) + 2 = (i + j)</tex>-ый порядковый элемент после слияния массивов (так произойдет в случае, когда <tex>a[i] > b[j - 2]</tex>), а значит элемент с номером <tex>i</tex> и все до него в массиве <tex>A</tex> никогда не будут <tex>k</tex>-ой порядковой статистикой. Аналогично элемент с индексом <tex>j</tex> и все элементы, стоящие после него, в массиве <tex>B</tex> никогда не будут ответом, так как на позиции <tex>j</tex> будет стоять <tex>(i + j + 2)</tex>-ой порядковый элемент после слияния, порядковые номера остальных же будут еще больше. Таким образом, далее мы можем продолжать поиск в массиве <tex>A</tex> только в диапазоне индексов <tex>[i + 1, n - 1]</tex>, а в массиве <tex>B</tex> - <tex>[0, j - 1]</tex>. Также, если <tex>b[j] < a[i]</tex>, то <tex>b[j] < a[i - 1]</tex>. Аналогичными рассуждениями приходим к тому, что в таком случае дальнейший поиск нужно осуществлять в массиве <tex>A</tex> в диапазоне <tex>[0, i - 1]</tex>, в массиве <tex>B</tex> - <tex>[j + 1, m - 1]</tex>.

Версия 15:52, 15 апреля 2015

Постановка задачи

Пусть даны два отсортированных массива [math]A[/math] и [math]B[/math] размерами [math]n[/math] и [math]m[/math] соответственно. Требуется найти [math]k[/math]-ый порядковый элемент после их слияния. Будем считать, что все элементы в массивах различны и нумеруются с нуля.

Варианты решения

Наивное решение

Сольем два массива и просто возьмем элемент с индексом [math]k - 1[/math]. Сливание будет выполнено за [math]O(n + m)[/math].

Чуть менее наивное решение

Будем использовать два указателя, с помощью которых сможем обойти массивы не сливая их. Поставим указатели на начало каждого из массивов. Будем увеличивать на единицу тот из них, который указывает на меньший элемент. После [math](k - 1)[/math]-ого добавления сравним элементы, на которых стоят указатели. Меньший из них и будет ответом. Таким образом, мы получим [math]k[/math]-ый элемент за [math]O(k)[/math] шагов.

Совсем не наивное решение

Оба решения, приведенные выше, работают за линейное время, то есть приемлемы только при небольших значениях [math]k[/math]. Следующее решение работает за [math]O(log(n) + log(m))[/math].

Чтобы получить логарифмическую сложность, будем использовать бинарный поиск, который сокращает область поиска с каждой итерацией. То есть для достижения нужной сложности мы должны на каждой итерации сокращать круг поиска в каждом из массивов.

Рассмотрим следующую ситуацию: пусть у нас есть элемент [math]a[i][/math] из массива [math]A[/math] и элемент [math]b[j][/math] из массива [math]B[/math] и они связаны неравенством [math]b[j - 1] \lt a[i] \lt b[j][/math]. Тогда [math]a[i][/math] есть [math](j + i + 1)[/math]-ый порядковый элемент после слияния массивов. Это объясняется тем, что до [math]a[i][/math]-ого элемента идут [math](j - 1)[/math] элемент из массива [math]B[/math], [math]i[/math] элементов из массива [math]A[/math] (включая сам элемент [math]a[i][/math]) и так как индексация массивов начинается с нуля, то необходимо прибавить еще [math]2[/math]. В итоге получаем [math](j - 1) + i + 2 = j + i + 1[/math]. Принимая это во внимание, будем выбирать [math]i[/math] и [math]j[/math] таким образом, чтобы [math]j + i + 1 = k[/math].

Подведем промежуточный итог:

  1. Инвариант [math]j + i = k - 1[/math]
  2. Если [math]b[j - 1] \lt a[i] \lt b[j][/math], то [math]a[i][/math] и есть [math]k[/math]-ая порядковая статистика
  3. Если [math]a[i - 1] \lt b[j] \lt a[i][/math], то [math]b[j][/math] и есть [math]k[/math]-ая порядковая статистика

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

Будем использовать [math]i[/math] и [math]j[/math] как опорные точки для разделения массивов. Заметим, что если [math]a[i] \lt b[j][/math], то [math]a[i] \lt b[j - 1][/math] (иначе второе условие бы выполнялось). В таком случае на месте [math]i[/math]-го элемента может стоять максимум [math]i + (j - 2) + 2 = (i + j)[/math]-ый порядковый элемент после слияния массивов (так произойдет в случае, когда [math]a[i] \gt b[j - 2][/math]), а значит элемент с номером [math]i[/math] и все до него в массиве [math]A[/math] никогда не будут [math]k[/math]-ой порядковой статистикой. Аналогично элемент с индексом [math]j[/math] и все элементы, стоящие после него, в массиве [math]B[/math] никогда не будут ответом, так как на позиции [math]j[/math] будет стоять [math](i + j + 2)[/math]-ой порядковый элемент после слияния, порядковые номера остальных же будут еще больше. Таким образом, далее мы можем продолжать поиск в массиве [math]A[/math] только в диапазоне индексов [math][i + 1, n - 1][/math], а в массиве [math]B[/math] - [math][0, j - 1][/math]. Также, если [math]b[j] \lt a[i][/math], то [math]b[j] \lt a[i - 1][/math]. Аналогичными рассуждениями приходим к тому, что в таком случае дальнейший поиск нужно осуществлять в массиве [math]A[/math] в диапазоне [math][0, i - 1][/math], в массиве [math]B[/math] - [math][j + 1, m - 1][/math].