iOS數據結構與算法實戰 - 算法的符號大 O

爲何要學習算法呢

知道一個算法執行多快佔用多少空間很是有用,以此來選擇解決當前問題的算法。 大 O 符號讓你能粗略的估計算法的運行時間和他使用了多少內存。 一個最壞的例子算法的運行須要的時間是O( n2 ) ,而且使用O(n)的空間。那麼這個算法真的運行緩慢還須要不少額外的空間。 計算大O的算法一般是經過數據分析進行的。咱們跳過數學分析,這個n關聯的是須要處理數據的數量,例如打印1000個元素的數組的每一個元素則 n = 1000。git

在描述算法複雜度時,常常用到o(1), o(n), o(logn), o(nlogn)來表示對應算法的時間複雜度。

這裏對數學忘了的童鞋咱們來複習下數學的對數和指數等方面的知識以便於咱們來理解這幾個函數公式表明的意思。github

對數百度百科解釋:若是a的x次方等於N(a>0,且a不等於1),那麼數x叫作以a爲底N的對數(logarithm),記做x=logaN。其中,a叫作對數的底數,N叫作真數。 舉個例子: 以2爲底8的對數就是3。套入上面的概念,2(a)的3(x)次方等於8(N),那麼數3叫作以2爲底8的對數,記做3 = log2 (8) 。因爲把8放到右上角很難打出來因此這裏省略爲3 = log2 (8)也記作 log(8)。 注意:在大 O 記法中 log (n)的底數有多是其餘常數 好比2,3。物理上經常使用e,數學計算中經常使用10,計算機相關經常使用2,所以計算機裏經常使用是2。算法

指數百度百科解釋:指數是冪運算aⁿ(a≠0)中的一個參數,a爲底數,n爲指數,指數位於底數的右上角,冪運算表示指數個底數相乘。當n是一個正整數,aⁿ表示n個a連乘。當n=0時,aⁿ=1。 咱們來理解下 :冪運算 8=2x2x2 2的三次方標識3個2連乘 。對數運算 3 = log 2 (8)。 他們之間什麼關係呢看圖: 數組

對數和指數的關係圖

下面咱們來舉幾個例子解釋下上面幾個常見的複雜度函數公式

O(1) 常熟階 這個是最好的,無論你處理多少數據,該算法執行的時間是同樣的。好比從取數組裏面第一個下標的元素。

NSArray *testArray = @[@"2",@"5」]; NSString *testStr = testArray[1]; 複製代碼

固然以前咱們講的進棧出棧也是這個複雜度。 這個算法的運行次數f(n) = 1,根據推導大O階的方法,第一步是將1改成1,在保留最高階項是,它沒有最高階項,所以這個算法的時間複雜度爲O(1); 注意:無論這個常數是多少,3或12,都不能寫成O(3)、O(12),而都要寫成O(1)。bash

O(logn) 對數階 當數據增大n倍時,耗時增大logn倍(這裏的log是以2爲底的,好比,當數據增大256倍時,耗時只增大8倍,是比線性還要低的時間複雜度)。二分查找就是O(logn)的算法,每找一次排除一半的可能,256個數據中查找只要找8次就能夠找到目標。舉個簡單的例子

int j = 1;                   
    while (j <= n)     
    {
        j *= 2;
    }
複製代碼

可能你們看這個有點模糊解釋下: 語句1的頻度是1, 設語句2的頻度是f(n),
則:2^f(n) <= n;f(n)<=log2n , 取最大值f(n)=log2n, T(n)=O(log2n ) ,我手寫了下看下圖: 函數

手書

O(n) 線性階 性能良好,表明數據量增大幾倍,耗時也增大幾倍。好比常見的遍歷算法,順序搜索。舉個例子

NSArray *testArray = @[@"2",@"5"];
    
    for (NSInteger i = 0; i < testArray.count; i++)
    {
        NSLog(@"%@",testArray[i]);
    }
複製代碼

這個算法的運行次數f(n) = n, 也就是O(n) 由於循環體中的代碼需要執行n次。性能

O(n log n) 線性對數階 和上面概念相似 就是n乘以logn,當數據增大256倍時,耗時增大256*8=2048倍。這個複雜度高於線性低於平方。歸併排序就是O(nlogn)的時間複雜度。舉個例子

for (i = 0; i< n ; i ++)
    {
        int j = 1;
        while (j <= n)
        {
            j *= 2;
        }
    }
複製代碼

解釋:和上面對數階多了一層循環 就是乘以n學習

O( n2 ) 平方階 表明數據量增大n倍時,耗時增大n的平方倍,這是比線性更高的時間複雜度。好比冒泡排序,就是典型的O( n2 )的算法,對n個數排序,須要掃描n×n次。舉個例子

for (int i = 0; i < n ; i++)
    {
        for (int j = i ; j < n; j++)
        {
            
        }
    }
複製代碼

注意:上面的內層循環j = i ;而不是0 由於i = 0時,內層循環執行了n次,當i=1時,執行了n-1次……當i=n-1時,執行了1次,因此總的執行次數爲: n+(n-1)+(n-1)+...+1 = n(n+1)/2 = n2/2 + n/2 根據大O推導方法,保留最高階項,n2/2 ,而後去掉這個項相乘的常數,1/2 所以,這段代碼的時間複雜度爲O( n2 )ui

O(n3) 立方階 這個性能比較差 也就是說表數據量增大n倍時,耗時增大n的立方倍

for(i=0;i<n;i++)  
   {    
      for(j=0;j<i;j++)    
      {  
         for(k=0;k<j;k++)  
            x=x+2;    
      }  
   }  
複製代碼

解釋:當i=m, j=k的時候,內層循環的次數爲k當i=m時, j 能夠取 0,1,...,m-1 , 因此這裏最內循環共進行了0+1+...+m-1=(m-1)m/2次因此,i從0取到n, 則循環共進行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6因此時間複雜度爲O ( n3 ).spa

總結:

一般你不須要推算,能夠大概估算下。若是一層循環是O (n) ,若是是兩層循環就是 O( n2 ) ,三層循環就是 O( n3 ) , 以此類推。這個大O也是估計,和這個n 有很大的關係。例如,最壞的狀況下(插入排序)算法的O( n2 ) , 在理論上, 比運行時間(歸併排序) 算法的 O(n log n)要差。但對於少許的數據,插入排序是更快。

GithubDemo:

github.com/renmoqiqi/1…

相關文章
相關標籤/搜索