復(fù)雜度分析(上)

+文本內(nèi)容是對(duì)王爭《數(shù)據(jù)結(jié)構(gòu)與算法之美》課程的筆記得院, 如果有任何侵權(quán)行為, 請(qǐng)聯(lián)系博主刪除

為什么需要復(fù)雜度分析倡蝙?

很多人對(duì)復(fù)雜度分析有疑問, 認(rèn)為直接在機(jī)器上跑一遍, 就可以得出時(shí)間和空間復(fù)雜度. 對(duì)于這種說法, 我們認(rèn)為是正確的, 并且很多書籍將其稱為事后統(tǒng)計(jì). 但是, 這種方法有很大的局限性.

  • 測(cè)試結(jié)果依賴于測(cè)試環(huán)境

    不同的硬件對(duì)測(cè)試結(jié)果影響較大

  • 測(cè)試結(jié)果受數(shù)據(jù)規(guī)模的影響很大

    數(shù)據(jù)規(guī)模的大小和有序度, 對(duì)測(cè)試結(jié)果影響較大

所以, 我們需要一個(gè)不用具體的測(cè)試數(shù)據(jù)來測(cè)試, 就可以粗略地估計(jì)算法的執(zhí)行效率的方法.

O復(fù)雜度表示法

以一段代碼為例來估計(jì)算法的執(zhí)行時(shí)間

int cal(int n) {
    int sum = 0;
    int i = 1;
    for(; i <= n; ++i){
        sum = sum + i;
    }
    return sum;
}

由于是粗略估計(jì), 假設(shè)每行代碼執(zhí)行的時(shí)間都一樣, 為t. 第2晃痴、3行代碼分別需要1個(gè)t的執(zhí)行時(shí)間, 第4妓局、5行都運(yùn)行了n遍, 所以需要2 n * t的執(zhí)行時(shí)間, 所以這段代碼總的執(zhí)行時(shí)間就是(2 n + 2) * t. 可以看出來, 所有的代碼執(zhí)行時(shí)間T(n)與每行代碼的執(zhí)行次數(shù)成正比.

再看一段代碼

int cal(int n) {
    int sum = 0;
    int i = 1;
    int j = 1;
    for(; i <= n; ++i){
        j = 1;
        for(; j <= n; ++j){
            sum = sum + i * j;
        }
    }
}

根據(jù)以上思路, 可以得出T(n) = (2n^2 + 2n + 3) * t.

從中我們可以總結(jié)得到一個(gè)非常重要的規(guī)律, 所有代碼的執(zhí)行時(shí)間T(n)與每行代碼的執(zhí)行次數(shù)n成正比
T(n) = O(f(n))
其中T(n)表示代碼執(zhí)行的時(shí)間; n表示數(shù)據(jù)規(guī)模的大小; f(n)表示每行代碼執(zhí)行的次數(shù)總和. 公式中的O, 表示代碼的執(zhí)行時(shí)間T(n)f(n)表達(dá)式成正比.

所以T(n) = O(2n + 2), T(n) = O(2n^2 + 2n + 3), 這就是大O時(shí)間復(fù)雜度表示法. 大O時(shí)間復(fù)雜度實(shí)際表示的是代碼執(zhí)行時(shí)間隨數(shù)據(jù)規(guī)模增長的變化趨勢(shì), 所以, 也叫做漸進(jìn)時(shí)間復(fù)雜度, 簡稱時(shí)間復(fù)雜度.

當(dāng)n很大的時(shí)候, 我們只需記錄一個(gè)最大量級(jí)就可以了, 例如T(n) = O(n); T(n) = O(n^2).

時(shí)間復(fù)雜度分析

  • 只關(guān)注循環(huán)次數(shù)最多的一段代碼
    int cal(int n) {
        int sum = 0;
        int i = 1;
        for(; i <= n; ++i){
            sum = sum + i;
        }
        return sum;
    }

總的時(shí)間復(fù)雜度為O(n)

  • 加法法則: 總復(fù)雜度等于量級(jí)最大的那段代碼的復(fù)雜度
    int cal(int n){
        int sum_1 = 0;
        int p = 1;
        for(; p < 100; ++p){
            sum_1 = sum_1 + p;
        }
  
        int sum_2 = 0;
        int q = 1;
        for(; q<n; ++q){
            sum_2 = sum_2 + q;
        }
  
        int sum_3 = 0;
        int i = 1;
        int j = 1;
        for(; i<=n; ++i){
            for(; j<=n; ++j){
                sum_3 = sum_3 + i * j;
            }
        }
  
    return sum_1 + sum_2 + sum_3;
    }

總的時(shí)間復(fù)雜度為O(n^2)

  • 乘法法則: 嵌套代碼的復(fù)雜度等于嵌套內(nèi)外代碼復(fù)雜度的乘積
    int cal(int n){
        int ret = 0;
        int i = 1;
        for(; i<n; ++i){
            ret = ret + f(i);
        }
    }

    int f(int n){
        int sum = 0;
        int i = 1;
        for(; i<n; ++i){
            sum = sum + i;
        }
        return sum;
    }

總的時(shí)間復(fù)雜度為O(n^2)

幾種常見時(shí)間復(fù)雜度實(shí)例分析

復(fù)雜度量級(jí)(按數(shù)量級(jí)遞增)

  • 常量階O(1)
  • 對(duì)數(shù)階O(logn)
  • 線性階O(n)
  • 線性對(duì)數(shù)階O(nlogn)
  • 平方階O(n^2)、立方階O(n^3) \cdots k次方階O(n^k)
  • 指數(shù)階O(2^n)
  • 階乘階O(n!)

將上述時(shí)間復(fù)雜度錯(cuò)略的分為兩類:多項(xiàng)式量級(jí)非多項(xiàng)式量級(jí). 其中, 非多項(xiàng)式量級(jí)只有兩個(gè): O(2^n)O(n!).

我們把時(shí)間復(fù)雜度為非多項(xiàng)式量級(jí)的算法問題叫做NP問題(Non-Deterministic Polynomial, 非確定多項(xiàng)式).

當(dāng)數(shù)據(jù)規(guī)模n越來越大時(shí), 非多項(xiàng)式量級(jí)算法的執(zhí)行時(shí)間會(huì)急劇增加.

因此, NP問題不是我們討論的重點(diǎn). 接下來, 我們主要來看幾種常見的多項(xiàng)式時(shí)間復(fù)雜度.

  1. O(1)

    O(1)只是常量級(jí)時(shí)間復(fù)雜度的一種表示方法, 并不是指只執(zhí)行了一行代碼.

int i = 8;
int j = 6;
int sum = i + j;

只要代碼的執(zhí)行時(shí)間不隨n的增長而增長, 這樣代碼的時(shí)間復(fù)雜度都記作O(1). 一般情況下, 只要算法中不存在循環(huán)語句哺眯、遞歸語句, 即使有成千上萬行代碼, 其時(shí)間復(fù)雜度也是O(1).

  1. O(logn)谷浅、O(nlogn)
    i = 1;
    while(i<=n){
        i = i * 2;
    }

從代碼中可以看出, 變量i的值為:
2^0\ \ 2^1\ \ 2^2\ \cdots \ 2^k\ \cdots \ 2^x = n
通過求解2^x = n, 就可以知道代碼的執(zhí)行次數(shù). 所以其為O(\log_2n).

因?yàn)?img class="math-inline" src="https://math.jianshu.com/math?formula=%5Clog_3n" alt="\log_3n" mathimg="1">就等于\log_32 * \log_2n, 所以O(\log_3n) = O(C * \log_2n), 其中C = \log_32是一個(gè)常量. 因此, 在對(duì)數(shù)時(shí)間復(fù)雜度的表示方法里, 忽略對(duì)數(shù)的"底", 統(tǒng)一表示為O(\log n).

如果一段代碼的時(shí)間復(fù)雜度是O(\log n), 循環(huán)n遍, 時(shí)間復(fù)雜度就是O(n\log n).

  1. O(m+n)O(m*n)
    int call(int m, int n){
        int sum_1 = 0;
        int i = 1;
        for(; i<m; ++i){
            sum_1 = sum_1 + 1;
        }
   
        int sum_2 = 0;
        int j = 1;
        for(; j<n; ++j){
            sum_2 = sum_2 + j;
        }
        return sum_1 + sum_2;
    }

從代碼中可以看出, mn是表示兩個(gè)數(shù)據(jù)規(guī)模, 我們無法評(píng)判誰的數(shù)量級(jí)大, 所以, 時(shí)間復(fù)雜度就為O(m+n).

乘法類似.

空間復(fù)雜度

空間復(fù)雜度全程就是漸進(jìn)空間復(fù)雜度, 表示算法的存儲(chǔ)空間與數(shù)據(jù)規(guī)模之間的增長關(guān)系.

void print(int n){
    int i = 0;
    int[] a = new int[n];
    for(i; i<n; ++i){
        a[i] = i*i;
    }

    for(i=n-1; i>=0; --i){
        print out a[i];
    }
}

2行代碼中, 我們申請(qǐng)了一個(gè)空間存儲(chǔ)變量i, 但是它是常量階, 跟數(shù)據(jù)規(guī)模n沒有關(guān)系, 所以忽略. 第3行申請(qǐng)了一個(gè)大小為nint類型數(shù)組, 除此之外, 剩下的代碼都沒有占用更多的空間, 所以整段代碼的空間O(n).

常見的空間復(fù)雜度就是O(1)O(n)一疯、O(n^2).

學(xué)習(xí)關(guān)鍵

多練

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末撼玄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子墩邀,更是在濱河造成了極大的恐慌掌猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眉睹,死亡現(xiàn)場(chǎng)離奇詭異荔茬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辣往,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門兔院,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人站削,你說我怎么就攤上這事坊萝。” “怎么了许起?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵十偶,是天一觀的道長。 經(jīng)常有香客問我园细,道長惦积,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任猛频,我火速辦了婚禮狮崩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鹿寻。我一直安慰自己睦柴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布毡熏。 她就那樣靜靜地躺著坦敌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痢法。 梳的紋絲不亂的頭發(fā)上狱窘,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音财搁,去河邊找鬼蘸炸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尖奔,可吹牛的內(nèi)容都是我干的幻馁。 我是一名探鬼主播洗鸵,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼仗嗦!你這毒婦竟也來了膘滨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤稀拐,失蹤者是張志新(化名)和其女友劉穎火邓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體德撬,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铲咨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜓洪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纤勒。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖隆檀,靈堂內(nèi)的尸體忽然破棺而出摇天,到底是詐尸還是另有隱情,我是刑警寧澤恐仑,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布泉坐,位于F島的核電站,受9級(jí)特大地震影響裳仆,放射性物質(zhì)發(fā)生泄漏腕让。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一歧斟、第九天 我趴在偏房一處隱蔽的房頂上張望纯丸。 院中可真熱鬧,春花似錦静袖、人聲如沸液南。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至统扳,卻和暖如春喘帚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咒钟。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國打工吹由, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朱嘴。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓倾鲫,卻偏偏與公主長得像粗合,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乌昔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

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