Оригинальная ссылка:Просмотр анимации, чтобы легко понять временную сложность (1)
Алгоритм — это набор методов, используемых для обработки данных и решения программных проблем. Для одной и той же задачи, используя разные алгоритмы, конечный результат может быть одинаковым.Например, сортировка имеет первую десятку классической сортировки и несколько странных сортировок.Хотя результаты одинаковы, ресурсы и время, затрачиваемые в процессе, будут Есть большие различия, такие как быстрая сортировка и сортировка обезьян :).
Так как же нам измерить плюсы и минусы разных алгоритмов?
В основном он рассматривается из двух измерений «времени» и «пространства», занимаемых алгоритмом.
-
Измерение времени: относится ко времени, которое требуется для выполнения текущего алгоритма, которое мы обычно описываем как «временную сложность».
-
Пространственное измерение: это относится к тому, сколько памяти требуется для выполнения текущего алгоритма.Мы обычно описываем его как «пространственная сложность».
В этом разделе будет проанализировано измерение «времени».
Что такое Большой О
Глядя на слово «время», мы определенно можем думать о запуске алгоритмической программы, и легко узнать сложность по времени выполнения.
Возможен ли этот путь? Конечно можно, но и у него много недостатков.
Например, старому компьютеру программиста Сяо Ву требуется несколько секунд для обработки данных 10 Вт с помощью пузырьковой сортировки, но iMac Pro читателя может потребоваться всего 0,1 секунды, поэтому ошибка результата очень велика. Более того, некоторые алгоритмы требуют много времени для запуска, и нет никакого способа запустить их полностью, например, обезьянью сортировку :).
Есть ли способ строго проанализировать временную сложность алгоритма?
немного!
«Древние» программисты предложили общий метод: «обозначение большого О», т. е.T(n) = O(f(n)).
где n — размер данных, а O(f(n)) — количество инструкций, которые необходимо выполнить для запуска алгоритма, пропорциональное f(n).
Обозначение Ландау, используемое в приведенной выше формуле, было впервые введено немецким теоретиком чисел Паулем Бахманом в его книге 1892 года «Аналитическая теория чисел» другим немецким теоретиком чисел Эдмундом Ландау (Edmund Landau). Роль нотации Ландау состоит в том, чтобы описывать поведение сложных функций с помощью простых функций, давая верхнюю или нижнюю (точную) оценку. При вычислении сложности алгоритма обычно используется только большой символ О, а маленький символ О, символ Θ и т. д. в системе символов Ландау обычно не используются. Первоначально O здесь была заглавной греческой буквой, но теперь это заглавная английская буква O; маленький символ o также является строчной английской буквой o, а символ Θ поддерживает заглавную греческую букву Θ.
Примечание. Границы в алгоритме, используемом в этой статье, относятся к нижней верхней границе.
Общие показатели временной сложности
Давайте начнем с понимания большого O из общего уровня меры временной сложности:
-
Постоянный порядок O(1)
-
Линейный порядок O (n)
-
Квадратный порядок O (n²)
-
Логарифмический O (logn)
-
Линейный логарифмический порядок O (nlogn)
O(1)
Независимо от того, сколько строк кода выполняется, другие области не повлияют на работу, временная сложность этого кода составляет O (1)
void swapTwoInts(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
O(n)
В следующем коде код в цикле for будет выполняться n раз, поэтому время, которое он потребляет, зависит от изменения n, поэтому его временная сложность может быть выражена как O (n).
int sum ( int n ){
int ret = 0;
for ( int i = 0 ; i <= n ; i ++){
ret += i;
}
return ret;
}
В частности, c в c * O(n) может быть меньше 1, как в следующем коде:
void reverse ( string &s ) {
int n = s.size();
for (int i = 0 ; i < n/2 ; i++){
swap ( s[i] , s[n-1-i]);
}
}
O(n²)
Когда есть двойной цикл, то есть код O(n) снова вложен, и его временная сложность равна O(n²).void selectionSort(int arr[],int n){
for(int i = 0; i < n ; i++){
int minIndex = i;
for (int j = i + 1; j < n ; j++ )
if (arr[j] < arr[minIndex])
minIndex = j;
swap ( arr[i], arr[minIndex]);
}
}
Вот простой вывод
- Когда i = 0, второй цикл нужно запустить (n - 1) раз.
- Когда i = 1, второй цикл нужно запустить (n - 2) раз.
- . . . . . .
Нетрудно получить формулу:
(n - 1) + (n - 2) + (n - 3) + ... + 0
= (0 + n - 1) * n / 2
= O (n ^2)
Конечно, не все двойные циклы имеют размер O(n²), например следующий вывод 30n раз.Hello,五分钟学算法:)
код.
void printInformation (int n ){
for (int i = 1 ; i <= n ; i++)
for (int j = 1 ; j <= 30 ; j ++)
cout<< "Hello,五分钟学算法:)"<< endl;
}
O(logn)
int binarySearch( int arr[], int n , int target){
int l = 0, r = n - 1;
while ( l <= r) {
int mid = l + (r - l) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] > target ) r = mid - 1;
else l = mid + 1;
}
return -1;
}
В коде метода бинарного поиска через цикл while диапазон поиска сокращается кратно 2, то есть требуется log2^n раз, чтобы выйти из цикла.
Точно так же следующие два фрагмента кода также имеют временную сложность уровня O(logn).
// 整形转成字符串
string intToString ( int num ){
string s = "";
// n 经过几次“除以10”的操作后,等于0
while (num ){
s += '0' + num%10;
num /= 10;
}
reverse(s)
return s;
}
void hello (int n ) {
// n 除以几次 2 到 1
for ( int sz = 1; sz < n ; sz += sz)
for (int i = 1; i < n; i++)
cout<< "Hello,五分钟学算法:)"<< endl;
}
O(nlogn)
Если код с временной сложностью O(logn) повторяется N раз, то его временная сложность равна n * O(logn), что равно O(nlogn).
void hello (){
for( m = 1 ; m < n ; m++){
i = 1;
while( i < n ){
i = i * 2;
}
}
}
В следующем разделе будет подробно проанализирована сложность рекурсивного алгоритма, так что следите за обновлениями :)
Статья впервые опубликована в публичном аккаунте: Learning Algorithms in Five Minutes