數(shù)據(jù)結(jié)構(gòu)與算法之美(三)復(fù)雜度分析(下)

04 | 復(fù)雜度分析(下):淺析最好蕉陋、最壞浓冒、平均彪笼、均攤時(shí)間復(fù)雜度

  • 最好情況時(shí)間復(fù)雜度(best case time complexity)
  • 最壞情況時(shí)間復(fù)雜度(worst case time complexity)
  • 平均情況時(shí)間復(fù)雜度(average case time complexity)
  • 均攤時(shí)間復(fù)雜度(amortized time complexity)

最好、最壞情況時(shí)間復(fù)雜度

// n 表示數(shù)組 array 的長度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) pos = i;
  }
  return pos;
}

以上代碼篮撑,是在一個(gè)無序數(shù)組 array 中屁使,查找變量 x 出現(xiàn)的位置在岂,找不到則返回 -1。
在數(shù)組中查找一個(gè)數(shù)據(jù)蛮寂,并不需要每次都把整個(gè)數(shù)組遍歷一遍蔽午,因此這段代碼可優(yōu)化為:

// n 表示數(shù)組 array 的長度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) {
      pos = i;
      break; // 找到即可提前結(jié)束循環(huán)
    } 
  }
  return pos;
}

現(xiàn)在這段代碼的時(shí)間復(fù)雜度還是 O(n) 么?

  • 當(dāng)數(shù)組中第一個(gè)元素剛好是要查找的變量 x 共郭,那此時(shí)的時(shí)間復(fù)雜度就是 O(1)
  • 當(dāng)數(shù)組中不存在變量 x 祠丝,那么整個(gè)數(shù)組都需要遍歷一遍疾呻,時(shí)間復(fù)雜度是 O(n)
    為了表示代碼在不同情況下的不同時(shí)間復(fù)雜度,引入三個(gè)概念:最好情況時(shí)間復(fù)雜度写半、最壞情況時(shí)間復(fù)雜度岸蜗、平均情況時(shí)間復(fù)雜度

平均情況時(shí)間復(fù)雜度

從上面的例子看,想要查找變量 x 在數(shù)組中的位置叠蝇,有 n + 1 種情況:在數(shù)組的 0 ~ n - 1 位置不在數(shù)組中璃岳。每種情況下,查找所需遍歷元素的個(gè)數(shù)累加 1 + 2 + 3 + ... + (n - 2) + (n - 1) + n + n 悔捶,除以 n + 1铃慷,就能得到需要遍歷元素個(gè)數(shù)的平均值,即 n(n+3)/2(n+1)蜕该,時(shí)間復(fù)雜度的大O表示法中犁柜,忽略系數(shù)、低階堂淡、常量馋缅,所以這個(gè)公式簡化后得到的平均情況時(shí)間復(fù)雜度為O(n)

但是,上述計(jì)算過程沒有將概率考慮進(jìn)去绢淀。??????考慮概率怎么計(jì)算呢......

設(shè)變量 x 在數(shù)組中的概率是 p 萤悴,則不在數(shù)組中的概率為 1-p,那么皆的,在數(shù)組中各個(gè)位置的概率應(yīng)均為 p/n
那么覆履,平均時(shí)間復(fù)雜度的計(jì)算過程應(yīng)該是這樣的:
1 * p/n + 2 * p/n + ... + (n-1) * p/n + n * p/n + n * (1-p)
經(jīng)計(jì)算,可簡化為:n(2-p)/2 + p/2费薄,這就是概率論中的加權(quán)平均值硝全,也叫作期望值,所以平均時(shí)間復(fù)雜度的全稱應(yīng)該是加權(quán)平均時(shí)間復(fù)雜度或者期望時(shí)間復(fù)雜度楞抡。

??注意:這里和王爭老師講的不一樣啦柳沙,王爭老師的計(jì)算結(jié)果是(3n+1)/4,他的計(jì)算過程可理解為:為了計(jì)算方便拌倍,假設(shè) p=1/2,將其代入我的公式也可得出相同結(jié)果噪径。??

引入概率之后柱恤,用大O表示法表示,去掉公式n(2-p)/2 + p/2中的系數(shù) (2-p)/2 和常量 p/2 找爱,上例的加權(quán)平均時(shí)間復(fù)雜度仍然是 O(n)梗顺。

均攤時(shí)間復(fù)雜度

// array 表示一個(gè)長度為 n 的數(shù)組
// 代碼中的 array.length 就是 n
int[] array = new int[n];
int count = 0;

void insert(int val) {
  if (count == array.length) {
    int sum = 0;
    for (int i = 0; i < array.length; ++i) {
      sum = sum + array[i];
    }
    array[0] = sum;
    count = 1;
  }

  array[count] = val;
  ++count;
}

這段代碼實(shí)現(xiàn)了往數(shù)組中插入數(shù)據(jù)的功能。如果數(shù)組滿了车摄,即 count == array.length 時(shí)寺谤,遍歷數(shù)組求各項(xiàng)和仑鸥,將 sum 放到數(shù)組第一個(gè)位置,然后將新的數(shù)據(jù)插入变屁。如果數(shù)組有空閑空間眼俊,則直接將數(shù)據(jù)插入數(shù)組。

  • 最好情況時(shí)間復(fù)雜度:數(shù)組中有空閑空間粟关,只需將數(shù)據(jù)插入到數(shù)組下標(biāo)為 count 的位置疮胖,此時(shí)時(shí)間復(fù)雜度 O(1)
  • 最壞情況時(shí)間復(fù)雜度:數(shù)組中沒有空閑空間,需要遍歷數(shù)組求各項(xiàng)和闷板,再將數(shù)據(jù)插入澎灸,此時(shí)時(shí)間復(fù)雜度為 O(n)
  • 平均情況時(shí)間復(fù)雜度:數(shù)組長度是 n ,根據(jù)數(shù)據(jù)插入位置的不同遮晚,有n 種可能性性昭,這些情況的時(shí)間復(fù)雜度均為 O(1) ;還有一種數(shù)組沒有空閑空間的情況县遣,這種情況的時(shí)間復(fù)雜度是 O(n) 糜颠。這 n+1 種情況發(fā)生的概率均為 1/(n+1)。所以艺玲,根據(jù)加權(quán)平均的計(jì)算方法括蝠,求得平均時(shí)間復(fù)雜度是:
    1 * 1/(n+1) + 1 * 1/(n+1) + ... + 1 * 1/(n+1) + n * 1/(n+1),即 O(1)

對比以上 find()insert() 兩個(gè)函數(shù):

  1. find() 函數(shù)最好情況下時(shí)間復(fù)雜度才為 O(1) 饭聚,其平均情況時(shí)間復(fù)雜度為 O(n) 忌警;而 insert() 函數(shù)的平均情況時(shí)間復(fù)雜度就是 O(1) ,只有個(gè)別情況下復(fù)雜度較高秒梳,為 O(n) 法绵。
  2. 對于 insert() 來說, O(1) 時(shí)間復(fù)雜度的插入和 O(n) 時(shí)間復(fù)雜度的插入酪碘,出現(xiàn)的頻率是有規(guī)律可循的朋譬,并且有一定的先后時(shí)序關(guān)系。一般是 O(n) 插入之后兴垦,緊跟著 n - 1 個(gè) O(1) 的插入操作徙赢,循環(huán)往復(fù)。

所以探越,針對 insert() 函數(shù)這種特殊場景的復(fù)雜度分析狡赐,引入了更加簡單的分析方法:攤還分析法,通過攤還分析得到的時(shí)間復(fù)雜度叫作均攤時(shí)間復(fù)雜度钦幔。

均攤時(shí)間復(fù)雜度就是一種特殊的平均時(shí)間復(fù)雜度枕屉。

還看 insert() 函數(shù)這個(gè)例子,每一次 O(n) 插入之后鲤氢,都會跟著 n - 1 個(gè) O(1) 的插入操作搀擂,所以把耗時(shí)多的那次操作均攤到接下來的 n - 1 次耗時(shí)少的操作上西潘,均攤下來,這一組連續(xù)操作的均攤時(shí)間復(fù)雜度就是 O(1) 哨颂。這就是均攤分析的大致思路喷市。

均攤時(shí)間復(fù)雜度和攤還分析應(yīng)用場景比較特殊:

  1. 對一個(gè)數(shù)據(jù)結(jié)構(gòu)進(jìn)行一組連續(xù)操作中,大部分情況下時(shí)間復(fù)雜度都很低咆蒿,只有個(gè)別情況下時(shí)間復(fù)雜度比較高
  2. 這些操作之間存在前后連貫的時(shí)序關(guān)系

符合上面兩條的場景下东抹,可以將這一組操作一塊兒分析,看是否能將時(shí)間復(fù)雜度較高的那次操作的耗時(shí)分?jǐn)偟狡渌麜r(shí)間復(fù)雜度較低的操作上沃测。

在能夠應(yīng)用均攤時(shí)間復(fù)雜度分析的場合缭黔,一般均攤時(shí)間復(fù)雜度就等于最好情況時(shí)間復(fù)雜度。

課后思考

用今天學(xué)到的來分析一下下面代碼中 add() 函數(shù)的時(shí)間復(fù)雜度

// 全局變量蒂破,大小為 10 的數(shù)組 array馏谨,長度 len,下標(biāo) i 附迷。
int array[] = new int[10];
int len = 10;
int i = 0;

// 往數(shù)組中添加一個(gè)元素
void add(int element) {
  if (i >= len) { // 數(shù)組空間不夠了
    // 重新申請一個(gè) 2 倍大小的數(shù)組空間
    int new_array[] = new int[len * 2];
    // 把原來 array 數(shù)組中的數(shù)據(jù)依次 copy 到 new_array
    for (int j = 0; j < len; ++j) {
      new_array[j] = array[j];
    }
    // new_array 復(fù)制給 array 惧互, array 現(xiàn)在大小就是 2 倍 len 了
    array = new_array;
    len = 2 * len;
  }
  // 將 element 放到下標(biāo)為 i 的位置,下標(biāo) i 加一
  array[i] = element;
  ++i;
}

思考思考吧~??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喇伯,一起剝皮案震驚了整個(gè)濱河市喊儡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稻据,老刑警劉巖艾猜,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捻悯,居然都是意外死亡匆赃,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門今缚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來算柳,“玉大人,你說我怎么就攤上這事姓言∷蚕睿” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵何荚,是天一觀的道長滥壕。 經(jīng)常有香客問我,道長兽泣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任胁孙,我火速辦了婚禮唠倦,結(jié)果婚禮上称鳞,老公的妹妹穿的比我還像新娘。我一直安慰自己稠鼻,他們只是感情好冈止,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著候齿,像睡著了一般熙暴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上慌盯,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天周霉,我揣著相機(jī)與錄音,去河邊找鬼亚皂。 笑死俱箱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灭必。 我是一名探鬼主播狞谱,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼禁漓!你這毒婦竟也來了跟衅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤播歼,失蹤者是張志新(化名)和其女友劉穎伶跷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荚恶,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撩穿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谒撼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食寡。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖廓潜,靈堂內(nèi)的尸體忽然破棺而出抵皱,到底是詐尸還是另有隱情,我是刑警寧澤辩蛋,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布呻畸,位于F島的核電站,受9級特大地震影響悼院,放射性物質(zhì)發(fā)生泄漏伤为。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绞愚。 院中可真熱鬧叙甸,春花似錦、人聲如沸位衩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糖驴。三九已至僚祷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贮缕,已是汗流浹背辙谜。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跷睦,地道東北人筷弦。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像抑诸,于是被迫代替她去往敵國和親烂琴。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

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