算法的時間復(fù)雜度和空間復(fù)雜度

一、算法效率的度量方法

1.事后統(tǒng)計方法

這種方法主要是通過設(shè)計好的測試程序和數(shù)據(jù)胆萧,利用計算機計時器對不同算法編制的程序的運行時間進行比較,從而確定算法效率的高低。

2.事前分析估算方法
在計算機程序編寫前运吓,依據(jù)統(tǒng)計方法對算法進行估算。
經(jīng)過總結(jié)疯趟,我們發(fā)現(xiàn)一個高級語言編寫的程序在計算機上運行時所消耗的時間取決于下列因素:

算法采用的策略拘哨,方案
編譯產(chǎn)生的代碼質(zhì)量
問題的輸入規(guī)模
機器執(zhí)行指令的速度

由此可見,拋開這些與計算機硬件信峻、軟件有關(guān)的因素倦青,一個程序的運行時間依賴于算法的好壞和問題的輸入規(guī)模。(所謂的問題輸入規(guī)模是指輸入量的多少)

實現(xiàn):1+2+…+99+100
第一種算法:

int i, sum = 0, n = 100;   // 執(zhí)行1次
for( i=1; i <= n; i++ )    // 執(zhí)行了n+1次
{
    sum = sum + i;          // 執(zhí)行n次
}

第二種算法:

int sum = 0, n = 100;     // 執(zhí)行1次
sum = (1+n)*n/2;          // 執(zhí)行1次

第一種算法執(zhí)行了1+(n+1)+n=2n+2次盹舞。
第二種算法产镐,是1+1=2次
如果我們把循環(huán)看做一個整體,忽略頭尾判斷的開銷踢步,那么這兩個算法其實就是n和1的差距癣亚。

分析一個算法的運行時間時,重要的是把基本操作的數(shù)量和輸入模式關(guān)聯(lián)起來获印。


算法效率的度量方法

二述雾、函數(shù)的漸近增長

例1:
假設(shè)兩個算法的輸入規(guī)模都是n,
算法A要做2n+3次操作,即:先執(zhí)行n次的循環(huán)玻孟,執(zhí)行完成后再有一個n次的循環(huán)唆缴,最后有3次運算。
算法B要做3n+1次操作取募,理解同上
哪一個更快些琐谤?

算法比較

當(dāng)n=1時,算法A1效率不如算法B1玩敏,
當(dāng)n=2時斗忌,兩者效率相同;
當(dāng)n>2時旺聚,算法A1就開始優(yōu)于算法B1了织阳,隨著n的繼續(xù)增加,算法A1比算法B1 逐步拉大差距砰粹。所以總體上算法A1比算法B1優(yōu)秀唧躲。

線性圖

函數(shù)的漸近增長:給定兩個函數(shù)f(n)和g(n),如果存在一個整數(shù)N碱璃,使得對于所有的n>N弄痹,f(n)總是比g(n)大,那么嵌器,我們說f(n)的增長漸近快于g(n)肛真。

注:隨著n的增大,后面的+3和+1其實是不影響最終的算法變化曲線的爽航。所以蚓让,我們可以忽略這些加法常數(shù)。

例2:算法C是4n+8讥珍,算法D是2n^2+1历极。

算法比較2
線性圖2

去掉與n相乘的常數(shù),兩者的結(jié)果還是沒有改變衷佃,算法C2的次數(shù)隨著n的增長趟卸,還是遠小于算法D2。也就是說氏义,與最高次項相乘的常數(shù)并不重要衰腌,也可以忽略。

例3:算法E是2n2+3n+1觅赊,算法F是2n3+3n+1

算法比較3
線性圖3

例4:算法G是2n^2,算法H是3n+1琼稻,算法I是 2n^2+3n+1

算法比較4
線性圖4

當(dāng)他們數(shù)據(jù)很小的時候是這樣的:

線性圖4-小

這組數(shù)據(jù)我們看得很清楚吮螺,當(dāng)n的值變得非常大的時候,3n+1已經(jīng)沒法和2n^2的結(jié)果相比較,最終幾乎可以忽略不計鸠补。而算法G在跟算法I基本已經(jīng)重合了萝风。

于是我們可以得到這樣一個結(jié)論,判斷一個算法的效率時紫岩,函數(shù)中的常數(shù)和其他次要項常彻娑瑁可以忽略,而更應(yīng)該關(guān)注主項(最高項)的階數(shù)泉蝌。

三歇万、算法時間復(fù)雜度

1.算法時間復(fù)雜度的定義:
在進行算法分析時,語句總的執(zhí)行次數(shù)T(n)是關(guān)于問題規(guī)模n的函數(shù)勋陪,進而分析T(n)隨n的變化情況并確定T(n)的數(shù)量級贪磺。算法的時間復(fù)雜度,也就是算法的時間量度诅愚,記作:T(n)= O(f(n))寒锚。它表示隨問題規(guī)模n的增大,算法執(zhí)行時間的增長率和f(n)的增長率相同违孝,稱作算法的漸近時間復(fù)雜度刹前,簡稱為時間復(fù)雜度。其中f(n)是問題規(guī)模n的某個函數(shù)雌桑。
用大寫O()來體現(xiàn)算法時間復(fù)雜度的記法喇喉,我們稱之為大O記法。
一般情況下筹燕,隨著輸入規(guī)模n的增大轧飞,T(n)增長最慢的算法為最優(yōu)算法。
顯然撒踪,由此算法時間復(fù)雜度的定義可知过咬,我們的三個求和算法的時間復(fù)雜度分別為O(1),O(n)制妄,O(n^2)掸绞。


算法時間復(fù)雜度

2.推導(dǎo)大O階方法

如何分析一個算法的時間復(fù)雜度呢?即如何推導(dǎo)大O階呢耕捞?

  • 用常數(shù)1取代運行時間中的所有加法常數(shù)衔掸。
  • 在修改后的運行次數(shù)函數(shù)中,只保留最高階項俺抽。
  • 如果最高階項存在且不是1敞映,則去除與這個項相乘的常數(shù)。
  • 得到的最后結(jié)果就是大O階磷斧。

①常數(shù)階

例:段代碼的大O是多少振愿?

int sum = 0, n = 100;
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
sum = (1+n)*n/2;

第一條就說明了所有加法常數(shù)給他個O(1)即可

②線性階:一般含有非嵌套循環(huán)涉及線性階捷犹,線性階就是隨著問題規(guī)模n的擴大,對應(yīng)計算次數(shù)呈直線增長冕末。

int i , n = 100, sum = 0;
for( i=0; i < n; i++ )
{
    sum = sum + I;
}

上面這段代碼萍歉,它的循環(huán)的時間復(fù)雜度為O(n),因為循環(huán)體中的代碼需要執(zhí)行n次档桃。

③平方階

int i, j, n = 100;
for( i=0; i < n; i++ )
{
    for( j=0; j < n; j++ )
    {
        printf(“I love FishC.com\n”);
    }
}

n等于100枪孩,也就是說外層循環(huán)每執(zhí)行一次,內(nèi)層循環(huán)就執(zhí)行100次藻肄,那總共程序想要從這兩個循環(huán)出來蔑舞,需要執(zhí)行100*100次,也就是n的平方仅炊。所以這段代碼的時間復(fù)雜度為O(n^2)斗幼。

總結(jié):如果有三個這樣的嵌套循環(huán)就是n^3。所以總結(jié)得出抚垄,循環(huán)的時間復(fù)雜度等于循環(huán)體的復(fù)雜度乘以該循環(huán)運行的次數(shù)蜕窿。

int i, j, n = 100;
for( i=0; i < n; i++ )
{
    for( j=i; j < n; j++ )
    {
        printf(“I love FishC.com\n”);
    }
}

由于當(dāng)i=0時,內(nèi)循環(huán)執(zhí)行了n次呆馁,當(dāng)i=1時桐经,內(nèi)循環(huán)則執(zhí)行n-1次……當(dāng)i=n-1時,內(nèi)循環(huán)執(zhí)行1次浙滤,所以總的執(zhí)行次數(shù)應(yīng)該是:
n+(n-1)+(n-2)+…+1 = n(n+1)/2
n(n+1)/2 = n^2/2+n/2
用我們推導(dǎo)大O的攻略阴挣,第一條忽略,因為沒有常數(shù)相加纺腊。第二條只保留最高項畔咧,所以n/2這項去掉。第三條揖膜,去除與最高項相乘的常數(shù)誓沸,最終得O(n^2)。

④對數(shù)階

int i = 1, n = 100;
while( i < n )
{
    i = i * 2;
}

由于每次i*2之后壹粟,就距離n更近一步拜隧,假設(shè)有x個2相乘后大于或等于n,則會退出循環(huán)趁仙。
于是由2^x = n得到x = log(2)n洪添,所以這個循環(huán)的時間復(fù)雜度為O(logn)。

四雀费、函數(shù)調(diào)用的時間復(fù)雜度分析

例:

int i, j;
for(i=0; i < n; i++) {
    function(i);
}
void function(int count) {
    printf(“%d”, count);
}

函數(shù)體是打印這個參數(shù)干奢,這很好理解。function函數(shù)的時間復(fù)雜度是O(1)盏袄,所以整體的時間復(fù)雜度就是循環(huán)的次數(shù)O(n)律胀。

假如function是下面這樣:

void function(int count) {
    int j;
    for(j=count; j < n; j++) {
        printf(“%d”, j);
    }
}

事實上宋光,這和之前平方階的時候舉的第二個例子一樣:function內(nèi)部的循環(huán)次數(shù)隨count的增加(接近n)而減少,所以根據(jù)游戲攻略算法的時間復(fù)雜度為O(n^2)炭菌。

例:

n++; //1
function(n); //1
for(i=0; i < n; i++) { //n
    function(i);
}
for(i=0; i < n; i++) { //n^2
    for(j=i; j < n; j++) {
        printf(“%d”, j);
    }
}
void function(int count) {
    printf(“%d”, count);
}

為:1+1+n+n2,所以最后是O(n2)

常見的時間復(fù)雜度

對應(yīng)的線性圖:

常見時間復(fù)雜度線性圖
時間復(fù)雜度線性圖

常用的時間復(fù)雜度所耗費的時間從小到大依次是:
O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

五逛漫、最壞情況與平均情況

我們查找一個有n個隨機數(shù)字數(shù)組中的某個數(shù)字黑低,最好的情況是第一個數(shù)字就是,那么算法的時間復(fù)雜度為O(1)酌毡,但也有可能這個數(shù)字就在最后一個位置克握,那么時間復(fù)雜度為O(n)。
平均運行時間是期望的運行時間枷踏。
最壞運行時間是一種保證菩暗。在應(yīng)用中,這是一種最重要的需求旭蠕,通常除非特別指定停团,我們提到的運行時間都是最壞情況的運行時間。

五掏熬、算法的空間復(fù)雜度

我們在寫代碼時佑稠,完全可以用空間來換去時間。
舉個例子說旗芬,要判斷某年是不是閏年舌胶,你可能會花一點心思來寫一個算法,每給一個年份疮丛,就可以通過這個算法計算得到是否閏年的結(jié)果幔嫂。
另外一種方法是,事先建立一個有2050個元素的數(shù)組誊薄,然后把所有的年份按下標(biāo)的數(shù)字對應(yīng)履恩,如果是閏年,則此數(shù)組元素的值是1暇屋,如果不是元素的值則為0似袁。這樣,所謂的判斷某一年是否為閏年就變成了查找這個數(shù)組某一個元素的值的問題咐刨。

第一種方法相比起第二種來說很明顯非常節(jié)省空間昙衅,但每一次查詢都需要經(jīng)過一系列的計算才能知道是否為閏年。第二種方法雖然需要在內(nèi)存里存儲2050個元素的數(shù)組定鸟,但是每次查詢只需要一次索引判斷即可而涉。

這就是通過一筆空間上的開銷來換取計算時間開銷的小技巧。到底哪一種方法好联予?其實還是要看你用在什么地方啼县。

定義:算法的空間復(fù)雜度通過計算算法所需的存儲空間實現(xiàn)材原,算法的空間復(fù)雜度的計算公式記作:S(n)=O(f(n)),其中季眷,n為問題的規(guī)模余蟹,f(n)為語句關(guān)于n所占存儲空間的函數(shù)。

通常子刮,我們都是用“時間復(fù)雜度”來指運行時間的需求威酒,是用“空間復(fù)雜度”指空間需求。
當(dāng)直接要讓我們求“復(fù)雜度”時挺峡,通常指的是時間復(fù)雜度葵孤。
顯然對時間復(fù)雜度的追求更是屬于算法的潮流!

原文repost

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末橱赠,一起剝皮案震驚了整個濱河市尤仍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狭姨,老刑警劉巖宰啦,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異送挑,居然都是意外死亡绑莺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門惕耕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纺裁,“玉大人,你說我怎么就攤上這事司澎∑墼担” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵挤安,是天一觀的道長谚殊。 經(jīng)常有香客問我,道長蛤铜,這世上最難降的妖魔是什么嫩絮? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮围肥,結(jié)果婚禮上剿干,老公的妹妹穿的比我還像新娘。我一直安慰自己穆刻,他們只是感情好置尔,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著氢伟,像睡著了一般榜轿。 火紅的嫁衣襯著肌膚如雪幽歼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天谬盐,我揣著相機與錄音甸私,去河邊找鬼。 笑死设褐,一個胖子當(dāng)著我的面吹牛颠蕴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播助析,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼椅您!你這毒婦竟也來了外冀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤掀泳,失蹤者是張志新(化名)和其女友劉穎雪隧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體员舵,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡脑沿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了犁罩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坚踩。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡粗截,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出措近,到底是詐尸還是另有隱情,我是刑警寧澤女淑,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布瞭郑,位于F島的核電站,受9級特大地震影響鸭你,放射性物質(zhì)發(fā)生泄漏屈张。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一袱巨、第九天 我趴在偏房一處隱蔽的房頂上張望阁谆。 院中可真熱鬧,春花似錦瓣窄、人聲如沸笛厦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裳凸。三九已至贱鄙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姨谷,已是汗流浹背逗宁。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梦湘,地道東北人瞎颗。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像捌议,于是被迫代替她去往敵國和親哼拔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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