Может ли кто-нибудь точно объяснить, что происходит под капотом, чтобы сделать рекурсивный подход в следующей задаче намного более быстрым и эффективным с точки зрения временной сложности?
Проблема: напишите программу, которая будет принимать массив целых чисел в качестве ввода и вернуть три наибольших числа, отсортированных в массиве, без сортировки исходного (входного) массива.
Например:
Вход: [22, 5, 3, 1, 8, 2]
Вывод: [5, 8, 22]
Несмотря на то, что мы можем просто отсортировать исходный массив и вернуть последние три элемента, это займет не менее O(nlog(n))
time как самый быстрый алгоритм сортировки сделает именно это. Таким образом, задача состоит в том, чтобы выполнить задачу лучше и выполнить задачу за O(n)
время.
Итак, я смог придумать рекурсивное решение:
def findThreeLargestNumbers(array, largest=[]):
if len(largest) == 3:
return largest
max = array[0]
for i in array:
if i > max:
max = i
array.remove(max)
largest.insert(0, max)
return findThreeLargestNumbers(array, largest)
, в котором я все время находил наибольшее число, удалив его из исходного массива, добавив в мой пустой массив и снова рекурсивно вызывая функцию, пока в моем массиве не будет трех элементов.
Однако, когда я посмотрел на предложенный итерационный метод, я составил этот код:
def findThreeLargestNumbers(array):
sortedLargest = [None, None, None]
for num in array:
check(num, sortedLargest)
return sortedLargest
def check(num, sortedLargest):
for i in reversed(range(len(sortedLargest))):
if sortedLargest[i] is None:
sortedLargest[i] = num
return
if num > sortedLargest[i]:
shift(sortedLargest, i, num)
return
def shift(array, idx, element):
if idx == 0:
array[0] = element
return array
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
return array
Оба кода успешно прошли все тесты, и я был убежден, что итеративный подход быстрее (хотя и не такой чистый ..). Однако я импортировал модуль времени и проверил коды, предоставив массив из миллиона случайных целых чисел и подсчитав, сколько времени потребуется каждому решению, чтобы вернуть обратно отсортированный массив из трех наибольших чисел.
рекурсивный подход был намного быстрее (примерно в 9 раз быстрее), чем итерационный подход!
Почему так? Несмотря на то, что рекурсивный подход заключается в трехкратном обходе огромного массива и, вдобавок ко всему, каждый раз, когда он удаляет элемент (что занимает O(n)
времени, поскольку все остальные 999
элементы должны быть перемещены в памяти), тогда как итерационный подход заключается в обходе входного массива только один раз, и да, выполнение некоторых операций на каждой итерации, но с очень незначительным массивом размера 3, который вообще не потребует времени!
Я действительно хочу иметь возможность судите и выбирайте наиболее эффективный алгоритм для любой данной проблемы, чтобы любое объяснение чрезвычайно помогло.