2.9.1 演算法時間複雜度定義
在進行演算法分析時,語句總的執行次數t(n)是關於問題規模n的函式,進而分析t(n)隨n的變化情況並確定t(n)的數量級。演算法的時間複雜度,也就是演算法的時間量度,記作:t(n) = o(f(n))。它表示隨問題規模n的增大,演算法執行時間的增長率和f(n)的增長率相同,稱作演算法的漸近時間複雜度,簡稱為時間複雜度。其中f(n)是問題規模n的某個函式。
這樣用大寫o()來體現演算法時間複雜度的記法,我們稱之為大o記法。
一般情況下,隨著n的增大,t(n)增長最慢的演算法為最優演算法。
顯然,由此演算法時間複雜度的定義可知,我們的三個求和演算法的時間複雜度分別為o(n),o(1),o(n2)。我們分別給它們取了非官方的名稱,o(1)叫常數階,o(n)叫線性階,o(n2)叫平方階,當然,還有其他的一些階,我們之後會介紹。
2.9.2 推導大o階方法
那麼如何分析乙個演算法的時間複雜度呢?即如何推導大o階呢?
用常數1取代執行時間中的所有加法常數。
在修改後的執行次數函式中,只保留最高端項。
如果最高端項存在且不是1,則去除與這個項相乘的常數。
得到的最後結果就是大o階。
2.9.3 常數階
首先順序結構的時間複雜度。下面這個演算法,也就是剛才的第二種演算法,為什麼時間複雜度不是o(3),而是o(1)。
int sum = 0,n = 100; /*執行一次*/
sum = (1+n)*n/2; /*執行一次*/
printf("%d", sum); /*執行一次*/
這個演算法的執行次數函式是f(n)=3。根據我們推導大o階的方法,第一步就是把常數項3改為1。在保留最高端項時發現,它根本沒有最高端項,所以這個演算法的時間複雜度為o(1)。
另外,我們試想一下,如果這個演算法當中的語句sum=(1+n)*n/2有10句,即:
int sum=0,n=100; /* 執行1次 */
sum = (1+n)*n/2; /* 執行第1次 */
sum = (1+n)*n/2; /* 執行第2次 */
sum = (1+n)*n/2; /* 執行第3次 */
sum = (1+n)*n/2; /* 執行第4次 */
sum = (1+n)*n/2; /* 執行第5次 */
sum = (1+n)*n/2; /* 執行第6次 */
sum = (1+n)*n/2; /* 執行第7次 */
sum = (1+n)*n/2; /* 執行第8次 */
sum = (1+n)*n/2; /* 執行第9次 */
sum = (1+n)*n/2; /* 執行第10次 */
printf("%d",sum); /* 執行1次 */
事實上無論n為多少,上面的兩段**就是3次和12次執行的差異,這種與問題的大小無關(n的多少),執行時間恆定的演算法,我們稱之為具有o(1)的時間複雜度,又叫常數階。
注意,不管這個常數是多少,我們都記作o(1),而不能是o(3)、o(12)等其他任何數字。這是初學者常常犯的錯誤。
對於分支結構而言,無論是真,還是假,執行的次數都是恆定的,不會隨著n的變大而發生變化,所以單純的分支結構(不包含在迴圈結構中),其時間複雜度也是o(1)。
2.9.4 線性階
迴圈結構就會複雜很多。要確定某個演算法的階次,我們常常需要確定某個特定語句或某個語句集執行的次數。因此,我們要分析演算法的複雜度,關鍵就是要分析迴圈結構的運**況。
下面這段**,它的迴圈的時間複雜度為o(n)。因為迴圈體中的**須要執行n次。
int i,n=100,sum=0;
for( i=0; i < n; i++)
上面這段**,它的迴圈的時間複雜度為o(n),因為迴圈體中的**需要執行n次。其實,線性階就是乙個簡單的for迴圈,條件在i2.9.5 對數階
那麼下面的這段**,時間複雜度又是多少呢?
int i=1,n=100;
while( i由於每次count乘以2之後,就距離n更近了一分。也就是說,有多少個2相乘後大於n,則會退出迴圈。由2x=n得到x=log2n。所以這個迴圈的時間複雜度為o(logn)。
2.9.6 平方階
n等於100,也就是說外層迴圈每執行一次, 內層迴圈就執行100次 那總共程式想要從這兩個迴圈出來,需要執行100*100次, 也就是n的平方。 所以這段**的時間複雜度為0(n^2)。那如果有三個這樣的巢狀迴圈呢? 沒錯,那就是n^3。所以我們很容易總結得出,迴圈的時間複雜度等於迴圈體的複雜度乘以該迴圈執行的次數。
例一:下面的例子是乙個迴圈巢狀,它的內迴圈剛才2.9.4中分析過,內迴圈時間複雜度為o(n)。
int i,j;
for(i=0; i而對於外層的迴圈,不過是內部這個時間複雜度為o(n)的語句,再迴圈n次。所以這段**的時間複雜度為o(n²)。
例二:如果外迴圈的迴圈次數改為了m,時間複雜度則變為o(m×n)。
int i,j;
for(i=0; i例三:那麼下面這個迴圈巢狀,它的時間複雜度是多少呢?
int i,j;
for(i=0; i由於
當i=0時,內迴圈執行了n次,
當i=1時,執行了n-1次,
當i=n-1時,執行了1次。
n + (n-1) + (n-2) + …… +1 = n(n+1)/2 = n²/2 + n/2
用我們推導大o階的方法
最終這段**的時間複雜度為o(n²)。
例四:
int i,j;
for(i=0; i函式體是列印這個引數。其實function函式的時間複雜度是o(1)。
所以整體的時間複雜度是o(n)。
例五:
int i,j;
for(i=0; i其實這和剛剛的例子是一樣的,只是把巢狀內迴圈放到了函式中,所以最終的時間複雜度還是為o(n²)。
例六:
n++; /* 執行次數為1 */
function(n); /* 執行次數為n */
int i,j;
for(i=0; i它的執行次數f(n)=1+n+n²+n(n+1)/2=3n²/2+3n/2+1
根據推導大0階的方法,最終這段**的時間複雜度也是0(n²)。
大話資料結構 之時間複雜度
程式猿可以讓步,卻不可以退縮,可以羞澀,卻不可以軟弱,總之,程式設計師必須是勇敢的。時間複雜度序言 當前兩天我寫完 大話資料結構 的序言的時候,我就在想,我該如何把從大話資料結構中對應用開發人員有用的知識提煉出來?我是該如同課本一樣把所有的知識羅列個遍?還是如何如何,我想如果我把所有的東西都羅列出來...
資料結構,演算法,時間複雜度
什麼是資料結構 data structure 中文維基百科 維基百科中文版 在電腦科學中 是計算機中儲存 組織資料的方式。資料結構意味著介面或封裝 乙個資料結構可被視為兩個函式之間的介面,或者是由資料型別聯合組成的儲存內容的訪問方法封裝。不同種類的資料結構適合不同種類的應用,部分資料結構甚至是為了解...
資料結構 時間複雜度 空間複雜度
1.演算法效率 演算法效率可以用來衡量乙個演算法的好壞 演算法效率分析分為兩種 第一種是時間效率,第二種是空間效率.時間效率被稱為時間複雜度,空間效率被稱為空間複雜度.時間複雜度主要衡量的是乙個演算法的執行速度,而空間複雜度主要衡量乙個演算法所需要的額外空間,在計算機發展的早期,計算機的儲存容量很小...