算法的復(fù)雜度計(jì)算

算法的復(fù)雜度分為兩大類: 時(shí)間復(fù)雜度空間復(fù)雜度谚鄙。時(shí)間復(fù)雜度指的是該算法完成需要執(zhí)行運(yùn)算的次數(shù)各拷;空間復(fù)雜度指執(zhí)行算法需要消耗的內(nèi)存空間大小。

對(duì)一個(gè)算法而言闷营,時(shí)間復(fù)雜度空間復(fù)雜度 都是越小越好撤逢。因?yàn)閮?yōu)秀算法的核心是通過最短的時(shí)間和最小的空間完成指定的目標(biāo)運(yùn)算。

這篇文章粮坞,將介紹算法的時(shí)間復(fù)雜度和空間復(fù)雜度的計(jì)算方式蚊荣,內(nèi)容很基礎(chǔ),大神繞了吧莫杈。

時(shí)間復(fù)雜度的計(jì)算

通常互例,我們不會(huì)直接將算法執(zhí)行的總次數(shù)作為它的時(shí)間復(fù)雜度,因?yàn)轶菽郑⒉皇敲恳粋€(gè)算法都能算出一個(gè)確切的總執(zhí)行次數(shù)媳叨,根據(jù)不同的計(jì)算對(duì)象腥光,相同的算法的總執(zhí)行次數(shù)是不固定的,那么計(jì)算執(zhí)行的總次數(shù)意義不大糊秆。相反武福,定性的分析同一個(gè)算法對(duì)規(guī)模增加的影響更有意義:我們只想知道,隨著規(guī)模 N 的增長(zhǎng)痘番,算法的執(zhí)行次數(shù)的增長(zhǎng)曲線(對(duì)數(shù)增長(zhǎng)捉片? 常數(shù)增長(zhǎng)或者是指數(shù)增長(zhǎng)?)是怎樣的汞舱,這個(gè)增長(zhǎng)能夠給算法的性能做一個(gè)定性的分析伍纫,從而判斷一個(gè)算法的優(yōu)劣。一般我們會(huì)使用大 O 記錄昂芜。下面介紹幾種常見的大 O 記錄時(shí)間復(fù)雜度的例子莹规。

常見的幾個(gè)時(shí)間復(fù)雜度算法

O(1):表示的是該算法的執(zhí)行次數(shù)和規(guī)模 N 無(wú)關(guān),永遠(yuǎn)都會(huì)執(zhí)行某個(gè)常數(shù)次的元素泌神。
比如高斯函數(shù):

// 計(jì)算 1 + 2 + 3 + 4 + ...... + 100 + ... + N
// 規(guī)模為 N

 func addTo(_ n: Int)  {  
    sum = (1 + n) * n/2;    /* 執(zhí)行1次 */  
    printf("%d", sum);      /* 執(zhí)行1次 */  
 }  

這個(gè)高斯函數(shù)永遠(yuǎn)都只會(huì)執(zhí)行2次良漱,不管N有多大。也即是欢际,算法永遠(yuǎn)執(zhí)行一個(gè)常數(shù)次數(shù)债热,這種時(shí)間復(fù)雜度,用大O表示為O(1)幼苛。

O(LogN):對(duì)數(shù)遞增,表示隨著規(guī)模的增大焕刮,執(zhí)行次數(shù)按照規(guī)模N的對(duì)數(shù)函數(shù)遞增舶沿。

比如: 常見的對(duì)分查找。

// 給定一個(gè)有序的整型數(shù)組 Arr, 從中隨機(jī)挑選出一個(gè)元素 X配并,在數(shù)組中找到 X 的元素的下標(biāo)值
func findElmentIndex(_ arr: [Int], _ x: Int ) -> Int {
        var low = 0, hight = arr.count, index = (low + hight)/2
        while x != arr[index] {
            if x > arr[index] {
                low = index
            }else if x < arr[index]{
                hight = index
            }else{
                return index
            }
            index = (low + hight)/2
        }
        return index 
}

這里并不好算出算法的實(shí)際次數(shù)(根據(jù)X數(shù)字的位置會(huì)不斷改變總執(zhí)行次數(shù))括荡。但是我們可以給出一個(gè)大致的范圍,假如 X 剛好在數(shù)組的中間溉旋,那么一次就可以找到X的位置畸冲。如果X在最前面,也就是index = 0的位置,那么需要次數(shù)最多观腊,為 logN邑闲。這里使用大 O 記錄就是 O(logN)。

O(N):表示算法的執(zhí)行次數(shù)以 N 線性增長(zhǎng)梧油。

比如苫耸,累加函數(shù)。

// 計(jì)算 1 + 2 + 3 + 4 + ...... + 100 + ... + N  
// 除了使用高斯函數(shù)計(jì)算儡陨,我們也可以使用逐個(gè)相加的方法
    func addTo(_ n: Int) -> {
        var sum = 0
        for i in 0..<n+1{
            sum += i
        }
        return sum
    }

這個(gè)算法的執(zhí)行次數(shù)很明朗褪子,就是N次量淌。因?yàn)殡S著規(guī)模 N 線性增長(zhǎng),因此計(jì)作 O(N)嫌褪。

其余常見的還有 O(N^2)呀枢、 O(N^3)。以及 O(2^n)笼痛、O(N!)裙秋、 O(N^N)這些不常用到的,因?yàn)樵脚旁诤竺鏁r(shí)間復(fù)雜度越大晃痴,我們一般很少用到残吩。

計(jì)算時(shí)間復(fù)雜度的大 O 的方法

如何給一個(gè)算法定義大 O 呢?其實(shí)很簡(jiǎn)單倘核,只需要遵循一個(gè)規(guī)律就行泣侮。
假若給定一個(gè)函數(shù),內(nèi)部是一個(gè)累加算法紧唱,我們來(lái)想計(jì)算它的時(shí)間復(fù)雜度活尊,可以通過下面的順序來(lái)操作:

  • 計(jì)算算法執(zhí)行的總時(shí)間
  • 用常數(shù)1取代運(yùn)行時(shí)間中的所有加法常數(shù)。
  • 在修改后的運(yùn)行次數(shù)函數(shù)中漏益,只保留最髙階項(xiàng)蛹锰。
  • 如果最高階項(xiàng)存在且不是1,則去除與這個(gè)項(xiàng)相乘的常數(shù)。

首先看函數(shù)原型:

    func addTo(_ n: Int) -> Int {
        var sum = 0    
        for i in 0..<n {
            sum += i       //  執(zhí)行n次
        }
        return sum
    }
  1. 很容易計(jì)算得出绰疤,總次數(shù)為 n
  2. 沒有常數(shù)
  3. 本身只有一項(xiàng)铜犬,最高階即為本身
  4. 最高階存在為1, 不需要做其他的處理

這個(gè)算法的大 O 記做: O(n)轻庆,也就是線性執(zhí)行癣猾。這個(gè)例子還不夠體現(xiàn)上面四個(gè)步驟的所有流程。

再來(lái)看一個(gè)經(jīng)常用到的:

// 冒泡排序
    func sortByBubble<T: Comparable>(_ arr: [T]) -> [T] {
        var mutArr = arr;      // 1次
        for i in 0..<mutArr.count {
            // n 次
            for j in (i+1)..<mutArr.count {
                if mutArr[i] > mutArr[j] {
                    (mutArr[i],mutArr[j]) =  (mutArr[j],mutArr[i])   // n-1 余爆、 n -2 .... 1
                }
            }
        }
        return mutArr
    }
    

最快的時(shí)間: 在數(shù)組本身已經(jīng)排好序的情況纷宇,為n次。
最慢的時(shí)間: 完全倒序的情況下蛾方,執(zhí)行 n-1 + n-2 + .... + 1 = n^2/2 + n/2 (注意這里是交換兩個(gè)元素的值像捶,很多編程語(yǔ)言中應(yīng)該為3次,也就是這里的值應(yīng)該乘以3)
我們以最壞的情況計(jì)算大 O:

1 + n^2/2 + n/2 ---- > n^2/2 + n/2 + 1
首先去除常數(shù)項(xiàng): ----> n^2/2 + n/2
只保留最高次冪項(xiàng): ----> n^2/2
去除最高次冪的常數(shù): ----> n^2
所以桩砰,冒泡排序的大 O 為:O(n^2)

空間復(fù)雜度的計(jì)算

一個(gè)程序的空間復(fù)雜度是指運(yùn)行完一個(gè)程序所需內(nèi)存的大小拓春。利用程序的空間復(fù)雜度,可以對(duì)程序的運(yùn)行所需要的內(nèi)存多少有個(gè)預(yù)先估計(jì)亚隅。一個(gè)程序執(zhí)行時(shí)除了需要存儲(chǔ)空間和存儲(chǔ)本身所使用的指令邮利、常數(shù)淘衙、變量和輸入數(shù)據(jù)外丘薛,還需要一些對(duì)數(shù)據(jù)進(jìn)行操作的工作單元和存儲(chǔ)一些為現(xiàn)實(shí)計(jì)算所需信息的輔助空間。程序執(zhí)行時(shí)所需存儲(chǔ)空間包括以下兩部分渐尿。

所以,相對(duì)來(lái)說(shuō)矾瑰,空間復(fù)雜度經(jīng)常不會(huì)太大砖茸,如果太大,很可能算法會(huì)執(zhí)行不下去殴穴。一般的空間復(fù)雜度為 O (1)凉夯,O(n)也很常見,另外像涉及到遞歸算法中采幌,空間復(fù)雜度可能達(dá)到O(n^2)劲够。

空間復(fù)雜的大 O 計(jì)算其實(shí)和時(shí)間復(fù)雜度類似。但是還需要根據(jù)實(shí)際情況來(lái)算休傍,任何脫離實(shí)際的算法都毫無(wú)意義征绎。

排序算法的穩(wěn)定性

排序算法的穩(wěn)定性指的是同一個(gè)算法,相同大小的結(jié)果在每一次的算法中的順序都是一樣的磨取。我們前面舉例的冒泡排序就是如此人柿,就算其中有大小相同的值,但是排序在前面的值忙厌,排序后還是在前凫岖。

對(duì)于不穩(wěn)定的排序算法,只要舉出一個(gè)實(shí)例逢净,即可說(shuō)明它的不穩(wěn)定性哥放;而對(duì)于穩(wěn)定的排序算法,必須對(duì)算法進(jìn)行分析從而得到穩(wěn)定的特性爹土。需要注意的是甥雕,排序算法是否為穩(wěn)定的是由具體算法決定的,不穩(wěn)定的算法在某種條件下可以變?yōu)榉€(wěn)定的算法着饥,而穩(wěn)定的算法在某種條件下也可以變?yōu)椴环€(wěn)定的算法。

比如還是之前的冒泡排序惰赋,改成下面這樣就變成了不穩(wěn)定的排序算法:

 // 冒泡排序
    func sortByBubble<T: Comparable>(_ arr: [T]) -> [T] {
        var mutArr = arr;
        for i in 0..<mutArr.count {
            for j in (i+1)..<mutArr.count {
                if mutArr[i] >= mutArr[j] {  // 這里的條件進(jìn)行了更改
                    (mutArr[i],mutArr[j]) =  (mutArr[j],mutArr[i])    
                }
            }
        }
        print(mutArr)
        return mutArr
    }

這個(gè)算法最終變成了不穩(wěn)定的排序算法宰掉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赁濒,隨后出現(xiàn)的幾起案子轨奄,更是在濱河造成了極大的恐慌,老刑警劉巖拒炎,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挪拟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡击你,警方通過查閱死者的電腦和手機(jī)玉组,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門谎柄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人惯雳,你說(shuō)我怎么就攤上這事朝巫。” “怎么了石景?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵劈猿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我潮孽,道長(zhǎng)揪荣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任往史,我火速辦了婚禮仗颈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怠堪。我一直安慰自己揽乱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布粟矿。 她就那樣靜靜地躺著凰棉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陌粹。 梳的紋絲不亂的頭發(fā)上撒犀,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音掏秩,去河邊找鬼或舞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蒙幻,可吹牛的內(nèi)容都是我干的映凳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼邮破,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诈豌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起抒和,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤矫渔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后摧莽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庙洼,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了油够。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚁袭。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖叠聋,靈堂內(nèi)的尸體忽然破棺而出撕阎,到底是詐尸還是另有隱情,我是刑警寧澤碌补,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布虏束,位于F島的核電站,受9級(jí)特大地震影響厦章,放射性物質(zhì)發(fā)生泄漏镇匀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一袜啃、第九天 我趴在偏房一處隱蔽的房頂上張望汗侵。 院中可真熱鬧,春花似錦群发、人聲如沸晰韵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雪猪。三九已至,卻和暖如春起愈,著一層夾襖步出監(jiān)牢的瞬間只恨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工抬虽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留官觅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓阐污,卻偏偏與公主長(zhǎng)得像休涤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笛辟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容