Сортировка массива результатов
Мне задали этот вопрос в интервью Adobe:
У нас есть целочисленный массив, который сортируется в порядке возрастания. Мы также имеем 3 целых числа A
, B
и C
. Нам нужно применить A*x*x + B*x + C
для каждого элемента x
в массиве и вернуть соответствующий отсортированный массив.
Пример I:
Input array = -1 0 1 2 3 4
A = -1, B = 2, C = -1`
Результат применения формулы к каждому элементу = -4 -1 0 -1 -4 -9
Итак, ожидаемый результат = -9 -4 -4 -1 -1 0
(отсортировано)
Мое лучшее решение заключалось в том, чтобы применить формулу и отсортировать ее в результате решения O(nlogn)
. Я не мог сделать это лучше.
Любое руководство по его улучшению полезно.
Ответы
Ответ 1
Приведенное уравнение параболическое. Таким образом, результат применения его к отсортированному массиву приведет к массиву, который будет иметь максимальный/минимальный размер, если подмассивы будут отсортированы слева и справа.
В вашем случае максимум 0
, а вспомогательный массив слева от него [-4 -1]
сортируется в порядке возрастания, а подматрица справа [-1 -4 -9]
сортируется в порядке убывания.
Все, что вам нужно сделать, это объединить эти отсортированные массивы, которые являются линейными по времени.
Итак, алгоритм:
- Применить уравнение для каждого элемента
- Найти максимум/минимум
- Объединить подмассивы
Ответ 2
Вы можете сделать это в O(n)
. Найдите минимальное значение многочлена, которое возникает, когда
2 * A * x + B = 0
так что
x_min = -B / 2 * A.
Затем пройдите массив до тех пор, пока вы не найдете целое число, самое близкое к x_min
. Это O(n)
. Отсюда последовательно выбирайте слева или справа от этого элемента в зависимости от того, меньше или меньше |x_min - left|
меньше |x_min - right|
. Верните значения оценки полинома в этих точках в полученном порядке. Это O(n)
.
Это предполагает, что A
положительно. Вы можете обрабатывать случай отрицательного A
аналогичным образом.
Пример:
input array = -1 0 1 2 3 4 A = -1, B = 2, C = -1
Здесь максимальное значение имеет значение x_max = -2 / 2 * -1 = 1
. Из входного массива ближайшим значением является 1
, третий элемент. Затем мы последовательно выбираем элементы в следующем порядке, исходя из их расстояния до 1
.
1, 0, 2, -1, 3, 4
Тогда, поскольку A
отрицательно, мы должны запустить их в обратном порядке
4, 3, -1, 2, 0, 1
и вычислить полином на них
-9, -4, -4, -1, -1, 0
Готово.
Заметим, что мы используем специальное свойство парабол. А именно, для x
менее x_extreme
и A
положительное применение полинома к такому x
является убывающей функцией от x
. Для x
больше, чем x_extreme
и A
положительное, применение полинома к такому x
является возрастающей функцией x
. (Подобное рассуждение применяется, если A
отрицательно.) Таким образом, разделите массив на две части, те x
меньше x_extreme
, а те x
больше, чем x_extreme
. Затем примените полином к этим двум частям, чтобы в итоге были отсортированы два массива. Теперь примените слияние, отсортированное по этим сортированным массивам. Обратите внимание, что вышеприведенное описание эффективно представляет собой сортировку слияния.
Ответ 3
Вы можете признать, что результат применения квадратичного к данным почти отсортирован (как описано в ответах выше, или, признав, что производная полинома степени n непрерывна, степени n - 1 и имеет при большинство n нулей).
Итак, если у вас в вашей библиотеке есть процедура сортировки, которая делает что-то умное с почти отсортированными данными (например, слияние, которое это учитывает), вы можете просто выбросить на него данные и ожидать линейной производительности. Поиск в Интернете находит Какой алгоритм сортировки лучше всего работает в основном отсортированных данных?
который указывает на http://svn.python.org/projects/python/trunk/Objects/listsort.txt.
Ответ 4
Решение O(N)
, и нет необходимости выполнять какое-либо исчисление, хотя оно помогает понять форму кривой.
Ответы выше делают самую интуитивную вещь и решают уравнение для нахождения минимума или максимума, а затем разбивают список.
Существует преимущество при вычислении первой производной, но на самом деле этого не нужно, и нам не нужно находить максимальную или минимальную точку в это время.
Просто знайте, что он может двигаться в одном направлении, а затем назад в другом направлении, но никогда не будет менять направление более одного раза.
Мы собираемся начинать с каждого конца и перебирать с обеих сторон, пока мы не сможем слиться где-нибудь посередине. Прежде чем мы сделаем что-нибудь еще, нам нужно проверить направление на каждом конце, которое мы будем делать, просто сравнивая два конца конца. Таким образом, мы видим, что один конец движется вверх, а другой вниз.
Если у нас есть элементы N
, скажем, мы имеем данные X[0]
и X[N-1]
, поэтому вычислим f(X[0])
и f(X[N-1])
и f(X[1])
и f(X[N-2])
. Если f(X[0]) < f(X[1])
и f(X[N-1]) > f(X[N-2])
, тогда фактически все наши данные являются одной стороной максимума/минимума и, следовательно, уже отсортированы. То же самое, если сравнения находятся в другом направлении. (В одном направлении может потребоваться обратное).
В противном случае просто выполните слияние с обоих концов, таким образом f(X[0])
и f(X[N-1])
будут либо максимумами, либо минимумами их поддиапазонов (мы знаем из предыдущих сравнений) и создаем объединенный список из того, что соответствует соответствующему направлению.
Применение к вашим данным:
-1 0 1 2 3 4
A = -1, B = 2, C = -1`
f = [ -4, -1, 0, -1, -4, -9 ]
-4 < -1
и -9 < -4
, поэтому мы пересекаем точку и у нас есть минимумы на каждом конце.
-9 is lower than -4
-4 and -4 are equal so push both
-1 and -1 are equal so push both
0 remains.
our sequence is [-9, -4, -4, -1, -1, 0 ]