Как правило, вы найдете код со временем logN
в алгоритмах типа «разделяй и властвуй».Это означает, что мы разбиваем проблему на более мелкие части и затем решаем для результата.
Например, что-то вроде двоичного поиска:
mid = (hi + lo) / 2
if element == arr[mid]: element found, return
if element > arr[mid]: search(mid, hi)
else: search(lo, mid)
Этот код делит ваше пространство поиска на меньшие именьшие подзадачи.Каждый раз, когда вы сокращаете пространство поиска пополам, и вы можете делить целое число только пополам logN
раз.
Нечто похожее наблюдается в алгоритмах типа mergesort, которые рекурсивно делят массив на две половины для сортировки.Слияние по существу:
a = mergesort(arr[:len(arr)/2]) #left side
b = mergesort(arr[len(arr)/2:]) #right side
merge(a, b)
Это создает дерево подзадач, например, так:
4
/ \
2 2 #mergesort left and right
/ \ / \
1 1 1 1 #cannot break problem down further
\ / \ /
2 2 #merge
\ /
4 #merge
Каждое слияние происходит в O(N)
, а высота дерева подзадачи строится с помощью слиянияLogN
(каждый следующий уровень делится на два, вы можете делить только на два logN раз).Это дает O(N)
за время слияния LogN
слияния для сложности времени O(NlogN)
.
Другим примером может быть что-то вроде операции кучи, такой как percolate_up или percolate_down.Если у вас есть структуры данных, такие как двоичные деревья, где у каждого узла есть два дочерних элемента, высота дерева равна logN
.