Обозначение Big O полезно, потому что с ним легко работать, и оно скрывает ненужные сложности и детали (для некоторого определения ненужных). Один хороший способ понять сложность алгоритмов «разделяй и властвуй» - это метод дерева. Допустим, у вас есть версия быстрой сортировки с медианной процедурой, поэтому вы каждый раз разбиваете массив на идеально сбалансированные подмассивы.
Теперь создайте дерево, соответствующее всем массивам, с которыми вы работаете. В корне у вас есть исходный массив, корень имеет двух дочерних элементов, которые являются подмассивами. Повторяйте это до тех пор, пока у вас не будет одноэлементных массивов внизу.
Поскольку мы можем найти медиану за время O (n) и разделить массив на две части за время O (n), работа, выполняемая на каждом узле, - это O (k), где k - это размер массива. Каждый уровень дерева содержит (самое большее) весь массив, поэтому работа на уровень составляет O (n) (размеры подмассивов складываются в n, и, поскольку у нас есть O (k) на уровень, мы можем сложить это) , В дереве есть только уровни log (n), так как каждый раз мы вдвое сокращаем ввод.
Таким образом, мы можем ограничить объем работы O (n * log (n)).
Однако Big O скрывает некоторые детали, которые мы иногда не можем игнорировать. Рассмотрим вычисление последовательности Фибоначчи с
a=0;
b=1;
for (i = 0; i <n; i++) {
tmp = b;
b = a + b;
a = tmp;
}
и давайте просто предположим, что a и b - это BigIntegers в Java или что-то, что может обрабатывать произвольно большие числа. Большинство людей сказали бы, что это алгоритм O (n) без дрожания. Причина в том, что у вас есть n итераций в цикле for, а O (1) работает в стороне цикла.
Но числа Фибоначчи велики, n-е число Фибоначчи экспоненциально по n, поэтому простое его сохранение займет порядка n байтов. Выполнение сложения с большими целыми числами потребует O (n) объема работы. Таким образом, общий объем работы, выполненной в этой процедуре, составляет
1 + 2 + 3 + ... + n = n (n-1) / 2 = O (n ^ 2)
Так что этот алгоритм работает в квадратическое время!