準(zhǔn)備刷算法題了,才發(fā)現(xiàn)自己連時(shí)間復(fù)雜度和空間復(fù)雜度都忘了

前言

最近打算好好刷刷算法題,然鵝發(fā)現(xiàn)自己對(duì)這個(gè)算法復(fù)雜度的知識(shí)記憶已全部返還給數(shù)據(jù)結(jié)構(gòu)老師了

一、算法

算法(Algorithm)是指解題方案的準(zhǔn)確而完整的描述,是一系列解決問(wèn)題的清晰指令,算法代表著用系統(tǒng)的方法描述解決問(wèn)題的策略機(jī)制。通俗地說(shuō),數(shù)據(jù)結(jié)構(gòu)就是指存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)。算法就是操作數(shù)據(jù)的方法。

二、算法的特征

  • 有窮性(Finiteness):算法的有窮性是指算法必須能在執(zhí)行有限個(gè)步驟之后終止
  • 確切性(Definiteness):算法的每一步驟必須有確切的定義
  • 輸入項(xiàng)(Input):一個(gè)算法有0個(gè)或多個(gè)輸入,以刻畫運(yùn)算對(duì)象的初始情況,所謂0個(gè)輸入是指算法本身定出了初始條件
  • 輸出項(xiàng)(Output):一個(gè)算法有一個(gè)或多個(gè)輸出,以反映對(duì)輸入數(shù)據(jù)加工后的結(jié)果,沒(méi)有輸出的算法是毫無(wú)意義的
  • 可行性(Effectiveness):算法中執(zhí)行的任何計(jì)算步驟都是可以被分解為基本的可執(zhí)行的操作步,即每個(gè)計(jì)算步都可以在有限時(shí)間內(nèi)完成(也稱之為有效性)

三、算法效率的度量

對(duì)于同一個(gè)問(wèn)題,使用不同的算法,也許最終得到的結(jié)果是一樣的,但在過(guò)程中消耗的資源和時(shí)間卻會(huì)有很大的區(qū)別。那么我們應(yīng)該如何去衡量不同算法之間的優(yōu)劣呢?主要還是從算法所占用的「時(shí)間」和「空間」兩個(gè)維度去考量。

時(shí)間維度:是指執(zhí)行當(dāng)前算法所消耗的時(shí)間,我們通常用「時(shí)間復(fù)雜度」來(lái)描述。

空間維度:是指執(zhí)行當(dāng)前算法需要占用多少內(nèi)存空間,我們通常用「空間復(fù)雜度」來(lái)描述。

評(píng)價(jià)一個(gè)算法的效率主要是看它的時(shí)間復(fù)雜度和空間復(fù)雜度情況。有的時(shí)候時(shí)間和空間卻又是「魚和熊掌」不可兼得,那么我們就需要從中去取一個(gè)平衡點(diǎn)。

四、時(shí)間復(fù)雜度

【4.1】時(shí)間頻度 一個(gè)算法執(zhí)行所耗費(fèi)的時(shí)間,從理論上是不能算出來(lái)的,必須上機(jī)運(yùn)行測(cè)試才能知道。但我們不可能也沒(méi)有必要對(duì)每個(gè)算法都上機(jī)測(cè)試,只需知道哪個(gè)算法花費(fèi)的時(shí)間多,哪個(gè)算法花費(fèi)的時(shí)間少就可以了。并且一個(gè)算法花費(fèi)的時(shí)間與算法中語(yǔ)句的執(zhí)行次數(shù)成正比例,哪個(gè)算法中語(yǔ)句執(zhí)行次數(shù)多,它花費(fèi)時(shí)間就多。一個(gè)算法中的語(yǔ)句執(zhí)行次數(shù)稱為語(yǔ)句頻度或時(shí)間頻度,記為T(n)。

【4.2】時(shí)間復(fù)雜度: 在剛才提到的時(shí)間頻度T(n)中,n稱為問(wèn)題的規(guī)模,當(dāng)n不斷變化時(shí),時(shí)間頻度T(n)也會(huì)不斷變化。但有時(shí)我們想知道它變化時(shí)呈現(xiàn)什么規(guī)律。為此,我們引入時(shí)間復(fù)雜度概念。 算法的時(shí)間復(fù)雜度也就是算法的時(shí)間度量,記作:T(n) = O(f(n))。它表示隨問(wèn)題規(guī)模n的增大,算法執(zhí)行時(shí)間的增長(zhǎng)率和f(n)的增長(zhǎng)率相同,稱作算法的漸進(jìn)時(shí)間復(fù)雜度,簡(jiǎn)稱時(shí)間復(fù)雜度

【4.3】大O表示法:像前面用O( )來(lái)體現(xiàn)算法時(shí)間復(fù)雜度的記法,我們稱之為大O表示法。 算法復(fù)雜度可以從最理想情況、平均情況和最壞情況三個(gè)角度來(lái)評(píng)估,由于平均情況大多和最壞情況持平,而且評(píng)估最壞情況也可以避免后顧之憂,因此一般情況下,我們?cè)O(shè)計(jì)算法時(shí)都要直接估算最壞情況的復(fù)雜度。

【4.4】常見的時(shí)間復(fù)雜度量級(jí):

  • 常數(shù)階O(1)
  • 線性階O(n)
  • 平方階O(n2)
  • 立方階O(n3)
  • 對(duì)數(shù)階O(logn)
  • 線性對(duì)數(shù)階O(nlogn)
  • 指數(shù)階O(2?)

【4.5】計(jì)算時(shí)間復(fù)雜度時(shí)的程序分析法則:

⑴. 對(duì)于一些簡(jiǎn)單的輸入輸出語(yǔ)句或賦值語(yǔ)句,近似認(rèn)為需要O(1)時(shí)間

⑵. 對(duì)于順序結(jié)構(gòu),需要依次執(zhí)行一系列語(yǔ)句所用的時(shí)間可采用大O求和法則

  • 例一:算法的2個(gè)部分時(shí)間復(fù)雜度分別為 T1(n)=O(f(n)) 和 T2(n)=O(g(n)),則時(shí)間復(fù)雜度 T1(n)+T2(n)=O(max(f(n), g(n)))
  • 例二:算法的2個(gè)部分時(shí)間復(fù)雜度分別為T1(m)=O(f(m)) 和 T2(n)=O(g(n)),則時(shí)間復(fù)雜度為 T1(m)+T2(n)=O(f(m) + g(n))

⑶. 對(duì)于選擇結(jié)構(gòu),如if語(yǔ)句,它的主要時(shí)間耗費(fèi)是在執(zhí)行then字句或else字句所用的時(shí)間,需注意的是檢驗(yàn)條件也需要O(1)時(shí)間

⑷. 對(duì)于循環(huán)結(jié)構(gòu),循環(huán)語(yǔ)句的運(yùn)行時(shí)間主要體現(xiàn)在多次迭代中執(zhí)行循環(huán)體以及檢驗(yàn)循環(huán)條件的時(shí)間耗費(fèi),一般可用大O乘法法則

  • 例一:算法的2個(gè)部分時(shí)間復(fù)雜度分別為 T1(n)=O(f(n)) 和 T2(n)=O(g(n)),則時(shí)間復(fù)雜度為 T1T2=O(f(n)g(n))

⑸. 對(duì)于復(fù)雜的算法,可以將它分成幾個(gè)容易估算的部分,然后利用求和法則和乘法法則技術(shù)求出整個(gè)算法的時(shí)間復(fù)雜度

⑹. 另外還有以下2個(gè)運(yùn)算法則

  • 若 g(n)=O(f(n)),則O(f(n))+ O(g(n))= O(f(n))
  • O(Cf(n)) = O(f(n)),其中C是一個(gè)正常數(shù)

【4.6】常見的時(shí)間復(fù)雜度示例:

常數(shù)階 O(1)

    let j = temp;
    let i = j;
    let Temp = i;

解:以上三條單個(gè)語(yǔ)句的頻度均為1,該程序段的執(zhí)行時(shí)間是一個(gè)與問(wèn)題規(guī)模n無(wú)關(guān)的常數(shù)。算法的時(shí)間復(fù)雜度為常數(shù)階,記作T(n)=O(1)。注意:如果算法的執(zhí)行時(shí)間不隨著問(wèn)題規(guī)模n的增加而增長(zhǎng),即使算法中有上千條語(yǔ)句,其執(zhí)行時(shí)間也不過(guò)是一個(gè)較大的常數(shù)。此類算法的時(shí)間復(fù)雜度是O(1)。

線性階 **O(n) **

    let a = 0;
    let b = 1; // 語(yǔ)句1  
    for (let i = 1; i <= n; i++) { // 語(yǔ)句2
      let s = a + b; // 語(yǔ)句3
      let b = a; // 語(yǔ)句4
      let a = s; // 語(yǔ)句5
    }

解: 語(yǔ)句1的頻度為2;語(yǔ)句2的頻度為n;語(yǔ)句3的頻度為 n-1;語(yǔ)句4的頻度為n-1;語(yǔ)句5的頻度為n-1;T(n)=2+n+3(n-1)=4n-1=O(n)

平方階 O(n2)

  let sum = 0; // 1次
  for(let i = 1; i <= n; i++) { // n+1次
    for (j = 1; j <= n; j++) { // n2次
      sum ++ ; // n2次
    }
  }

解:因?yàn)镺(2n2+n+1)=n2(即:去低階項(xiàng),去掉常數(shù)項(xiàng),去掉高階項(xiàng)的常參得到),所以T(n)=O(n2);

  for (let i = 1; i < n; i++) {
    y = y + 1; // 語(yǔ)句1
    for (let j = 0; j <= (2 * n); j++)
      x++; // 語(yǔ)句2
  }

解: 語(yǔ)句1的頻度是n-1, 語(yǔ)句2的頻度是(n-1)*(2n+1)=2n2-n-1,即 f(n)=2n2-n-1+(n-1)=2n2-2;又O(2n2-2)=n2,該程序的時(shí)間復(fù)雜度T(n)=O(n2)。一般情況下,對(duì)步進(jìn)循環(huán)語(yǔ)句只需考慮循環(huán)體中語(yǔ)句的執(zhí)行次數(shù),忽略該語(yǔ)句中步長(zhǎng)加1、終值判別、控制轉(zhuǎn)移等成分,當(dāng)有若干個(gè)循環(huán)語(yǔ)句時(shí),算法的時(shí)間復(fù)雜度是由嵌套層數(shù)最多的循環(huán)語(yǔ)句中最內(nèi)層語(yǔ)句的頻度f(wàn)(n)決定的。

對(duì)數(shù)階 O(log2?)

  let i = 1; //語(yǔ)句1
  while (i <= n) {
    i = i * 2; // 語(yǔ)句2
  }

解: 上面的代碼,在while循環(huán)里面,每次都將 i 乘以 2,乘完之后,i 距離 n 就越來(lái)越近了,直到i不小于n退出。我們?cè)囍蠼庖幌拢僭O(shè)循環(huán)次數(shù)為x,也就是說(shuō) 2 的 x 次方等于 n,則由2^x=n得出x=log2n。因此這個(gè)代碼的時(shí)間復(fù)雜度為T(n)=O(log2? )

【4.7】復(fù)雜度的比較

其中x軸代表n值,y軸代表T(n)值(時(shí)間復(fù)雜度)。T(n)值隨著n的值的變化而變化,其中可以看出O(n!)和O(2?)隨著n值的增大,它們的T(n)值上升幅度非常大,而O(logn)、O(n)、O(1)隨著n值的增大,T(n)值上升幅度則很小。
常用的時(shí)間復(fù)雜度按照耗費(fèi)的時(shí)間從小到大依次是:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2?)<O(n!)

五、空間復(fù)雜度

時(shí)間復(fù)雜度,換句話說(shuō),就是它們運(yùn)行得有多快。但有些時(shí)候,我們還得以另一種名為空間復(fù)雜度的度量方式,去估計(jì)它們會(huì)消耗多少內(nèi)存。當(dāng)內(nèi)存有限時(shí),空間復(fù)雜度便會(huì)成為選擇算法的一個(gè)重要的參考因素。比如說(shuō),在給小內(nèi)存的小型設(shè)備寫程序時(shí),或是處理一些會(huì)迅速占滿大內(nèi)存的大數(shù)據(jù)時(shí)都會(huì)考慮空間復(fù)雜度。

【5.1】空間復(fù)雜度:空間復(fù)雜度是執(zhí)行算法的空間成本,是對(duì)一個(gè)算法在運(yùn)行過(guò)程中臨時(shí)占用存儲(chǔ)空間大小的量度,它同樣適用了大O表示法。程序占用空間大小的計(jì)算公式記作S(n)=O(f(n)),其中n為問(wèn)題的規(guī)模,f(n)為算法所占儲(chǔ)存空間的函數(shù)

【5.2】算法存儲(chǔ)量包括:

  1. 程序本身所占空間
  2. 輸入數(shù)據(jù)所占空間
  3. 輔助變量所占空間

輸入數(shù)據(jù)所占空間只取決于問(wèn)題本身,和算法無(wú)關(guān),空間復(fù)雜度只需分析除了輸入數(shù)據(jù)所占空間和程序本身所占空間之外的輔助變量所占空間。

【5.3】常見空間復(fù)雜度:

O(1):算法執(zhí)行所需要的臨時(shí)空間不隨著某個(gè)變量n的大小而變化,即此算法空間復(fù)雜度為一個(gè)常量

O(n):當(dāng)一個(gè)算法的空間復(fù)雜度與n成線性比例關(guān)系時(shí),可表示為O(n)。若形參為數(shù)組,則只需要為它分配一個(gè)存儲(chǔ)由實(shí)參傳送來(lái)的一個(gè)地址指針的空間,即一個(gè)機(jī)器字長(zhǎng)空間;若形參為引用方式,則也只需要為其分配存儲(chǔ)一個(gè)地址的空間,用它來(lái)存儲(chǔ)對(duì)應(yīng)實(shí)參變量的地址,以便由系統(tǒng)自動(dòng)引用實(shí)參變量。

O(log2n):當(dāng)一個(gè)算法的空間復(fù)雜度與以2為底的n的對(duì)數(shù)成正比時(shí)

【5.3】空間復(fù)雜度示例:

空間復(fù)雜度 O(1)

  function makeUpperCase(arr) {
    for (let i = 0; i < arr.length; i++) {
      arr[i] = arr[i].toUpperCase();
    }
    return arr;
  }

因?yàn)樵摵瘮?shù)并不消耗額外的內(nèi)存空間,所以我們把它的空間復(fù)雜度描述為O(1)

空間復(fù)雜度 O(n)


  function makeUpperCase(arr) { // makeUpperCase函數(shù)接收一個(gè)數(shù)組作為參數(shù)arr。
    let newArr = []; // 然后它創(chuàng)建了一個(gè)全新的數(shù)組,名為newArr
    for (let i = 0; i < arr.length; i++) {
      newArr[i] = arr[i].toUpperCase(); // 并將原數(shù)組arr里的字符串的大寫形式填進(jìn)去。
    }
    return newArr;
  }

分析該函數(shù)的話,你會(huì)發(fā)現(xiàn)它接收一個(gè)n元素的數(shù)組,就會(huì)產(chǎn)生另一個(gè)新的n元素?cái)?shù)組。因此,我們會(huì)說(shuō)這個(gè)makeUpperCase函數(shù)的空間復(fù)雜度是O(n)

**注意 **

① 空間復(fù)雜度相比時(shí)間復(fù)雜度分析要少

② 對(duì)于遞歸算法來(lái)說(shuō),代碼一般都比較簡(jiǎn)短,算法本身所占用的存儲(chǔ)空間較少,但運(yùn)行時(shí)需要占用較多的臨時(shí)工作單元;若寫成非遞歸算法,代碼一般可能比較長(zhǎng),算法本身占用的存儲(chǔ)空間較多,但運(yùn)行時(shí)將可能需要較少的存儲(chǔ)單元

六、常用的時(shí)間復(fù)雜度和空間復(fù)雜度

一個(gè)經(jīng)驗(yàn)規(guī)則:其中c是一個(gè)常量,如果一個(gè)算法的復(fù)雜度為c 、 log2? 、n 、 n*log2? ,那么這個(gè)算法時(shí)間效率比較高 ,如果是2? 、3? 、n!,那么稍微大一些的n就會(huì)令這個(gè)算法不能動(dòng)了,居于中間的幾個(gè)則差強(qiáng)人意。

| 排序法| 最差時(shí)間分析 | 平均時(shí)間復(fù)雜度 | 穩(wěn)定度 | 空間復(fù)雜度 |
| 冒泡排序 | O(n2) | O(n2) | 穩(wěn)定 | O(1) |
| 快速排序 | O(n2) | O(nlog2?) | 不穩(wěn)定 | O(log2?)~O(n) |
| 選擇排序 | O(n2) | O(n2) | 穩(wěn)定 | O(1) |
| 二叉樹排序 | O(n2) | O(n
log2?) | 不一定 | O(n) |
| 插入排序| O(n2) | O(n2) | 穩(wěn)定 | O(1) |
| 堆排序 | O(nlog2?) | O(nlog2?) | 不穩(wěn)定 | O(1) |
| 希爾排序 | O | O | 不穩(wěn)定 | O(1) |

七、時(shí)間復(fù)雜度和空間復(fù)雜度的關(guān)系

對(duì)于一個(gè)算法,其時(shí)間復(fù)雜度和空間復(fù)雜度往往是相互影響的。當(dāng)追求一個(gè)較好的時(shí)間復(fù)雜度時(shí),可能會(huì)使空間復(fù)雜度的性能變差,即可能導(dǎo)致占用較多的存儲(chǔ)空間;反之,當(dāng)追求一個(gè)較好的空間復(fù)雜度時(shí),可能會(huì)使時(shí)間復(fù)雜度的性能變差,即可能導(dǎo)致占用較長(zhǎng)的運(yùn)行時(shí)間。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,069評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,595評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評(píng)論 2 372