Некоторые заметки о Big O
Большой О
O(1) Константа — без циклов
O(logN) Логарифмический — цикл по половине коллекции на основе условия и рекурсивный (пример: двоичный поиск)
O(n) Linear — нормальные циклы
O(n*log(n)) Лог-линейный — один цикл и цикл в половинной коллекции на основе условия и рекурсии. Пример: сортировка слиянием.
O(n²) Quadratic — Петля в петле
Две отдельные коллекции: O(a+b)
Одна коллекция, но два разных цикла: O(n)
Правило большого O
- Всегда худший случай
- Удалить константу
public void test(a[]) {
int a1 = a[0] -> O(1)
a.forEach(element=>print(element)) -> O(n)
}
Мы опускаем O(1) => эта функция имеет O(n)
3. Разные входы должны иметь разные переменные.
public void test(a[],b[]) {
int a1 = a[0] -> O(1)
a.forEach(element=>print(element)) -> O(a)
b.forEach(element=>print(element)) -> O(b)
}
У нас разные входные данные => O(a+b)
И если у нас есть вложенный цикл с другим вводом => O (a * b)
4. Отбросьте недоминирующие термины.
public void test(a[],b[]) {
int a1 = a[0] -> O(1)
a.forEach(elementA=>print(elementA)) -> O(a)
b.forEach(elementB=>{
a.forEach(elementA=>print(elementA+elementB))
}) -> O(a*b)
}
в этой функции, потому что наиболее доминирующим является вложенный цикл, поэтому эта функция имеет O (a * b)

Объясните BIG(O) на примере и математике
- O(n²)
Пример: сортировка выбором
func selectionSort(a []int) {
for i:=0;i< len(a);i++ {
for j:=i+1;j<len(a);j++ {
if a[j] < a[i] {
a[j],a[i] = a[i],a[j];
}
}
}
}
Предположим, у нас есть массив a = {4,5,3,2,6,1}
С вышеуказанной функцией. Числовое действие для каждой последовательности цикла
i = 0 -> j= 1: у нас есть 6 действий
i = 1 -> j = 2 : у нас есть 5 действий
i=5 -> j = 6 : у нас 0 действий
Таким образом, количество действий (или BigO) этой функции будет:
T(n) = 1+2+3 +4 ….+ (n-1) + n
Используя индуктивный метод, я пришел к выводу, что
T(n) = n(n+1)/2 = 1/2*n² + 1/2*n
Мы правим Big(O ) . Мы отбрасываем константу ( 1/2 ) и отбрасываем не доминирующий член ( n ) => T (n) = n² => Big (O) сортировки выбором равен O (n²)
2. О(логN)
Пример: бинарный поиск
func binarySearch(a []int,left int,right int,searchValue int) int {
if(left==right) {
return left;
}
mid := math.Floor(float64((right + left) / 2));
if(searchValue < a[int(mid)]) {
return binarySearch(a,left, int(mid)-1,searchValue);
} else if(searchValue > a[int(mid)]){
return binarySearch(a,int(mid)+1, right,searchValue);
}
return -1;
}
Предположим, у нас есть массив a = {1,2,3,4,5,6,7,8} и searchValue = 1.
С помощью вышеуказанной функции необходимо разделить массив на 3 три раза (это также 3 действия), пока мы не найдем searchValue с последовательностью середины
середина = 4 -> середина = 2 -> середина = 1
Я могу обобщить количество действий, необходимых для этого агортима, чтобы сформулировать
N/2^k =1 (n — общий элемент массива, а k — общее действие, которое нам нужно выполнить)
N/2^k =1 -> 2^k = N -> k = logN