目錄
1.什么是算法?
1.1算法的復(fù)雜度
2.算法的時(shí)間復(fù)雜度
2.1 時(shí)間復(fù)雜度的概念
計(jì)算Func1中++count語句總共執(zhí)行了多少次
2.2 大O的漸進(jìn)表示法
2.3常見時(shí)間復(fù)雜度計(jì)算舉例?
實(shí)例1:執(zhí)行2N+10次
實(shí)例2:執(zhí)行M+N次
實(shí)例3:執(zhí)行了100000000次
實(shí)例4:計(jì)算strchr的時(shí)間復(fù)雜度
實(shí)例5:計(jì)算BubbleSort的時(shí)間復(fù)雜度
實(shí)例6:計(jì)算BinarySearch的時(shí)間復(fù)雜度
實(shí)例7:?計(jì)算階乘遞歸Fac的時(shí)間復(fù)雜度
實(shí)例8:計(jì)算斐波那契遞歸Fib的時(shí)間復(fù)雜度
3.算法的空間復(fù)雜度
實(shí)例1:計(jì)算BubbleSort的空間復(fù)雜度
實(shí)例2:計(jì)算Fibonacci的空間復(fù)雜度
實(shí)例3:計(jì)算階乘遞歸Fac的空間復(fù)雜度
4.常見復(fù)雜度對比
1.什么是算法?
算法:
算法 (Algorithm):就是定義良好的計(jì)算過程,他取一個(gè)或一組的值為輸入,并產(chǎn)生出一個(gè)或一組值作為 輸出。簡單來說算法就是一系列的計(jì)算步驟,用來將輸入數(shù)據(jù)轉(zhuǎn)化成輸出結(jié)果。常見應(yīng)用于排序/ 二分查找
算法特點(diǎn):1.有窮性。一個(gè)算法應(yīng)包含有限的操作步驟,而不能是無限的。事實(shí)上“有窮性”往往指“在合理的范圍之內(nèi)”。如果讓計(jì)算機(jī)執(zhí)行一個(gè)歷時(shí)1000年才結(jié)束的算法,這雖然是有窮的,但超過了合理的限度,人們不把他視為有效算法。
2. 確定性。算法中的每一個(gè)步驟都應(yīng)當(dāng)是確定的,而不應(yīng)當(dāng)是含糊的、模棱兩可的。算法中的每一個(gè)步驟應(yīng)當(dāng)不致被解釋成不同的含義,而應(yīng)是十分明確的。也就是說,算法的含義應(yīng)當(dāng)是唯一的,而不應(yīng)當(dāng)產(chǎn)生“歧義性”。
3. 有零個(gè)或多個(gè)輸入、所謂輸入是指在執(zhí)行算法是需要從外界取得必要的信息。
4. 有一個(gè)或多個(gè)輸出。算法的目的是為了求解,沒有輸出的算法是沒有意義的。
5.有效性。 算法中的每一個(gè) 步驟都應(yīng)當(dāng)能有效的執(zhí)行。并得到確定的結(jié)果。
1.1算法的復(fù)雜度
算法在編寫成可執(zhí)行程序后,運(yùn)行時(shí)需要耗費(fèi)時(shí)間資源和空間 ( 內(nèi)存 ) 資源 。因此 衡量一個(gè)算法的好壞,一般是從時(shí)間和空間兩個(gè)維度來衡量的 ,即時(shí)間復(fù)雜度和空間復(fù)雜度。時(shí)間復(fù)雜度主要衡量一個(gè)算法的 運(yùn)行快慢 ,而空間復(fù)雜度主要衡量一個(gè)算法運(yùn)行 所需要的額外空間(需要多少內(nèi)存) 。在計(jì)算 機(jī)發(fā)展的早期,計(jì)算機(jī)的存儲(chǔ)容量很小。所以對空間復(fù)雜度很是在乎。但是經(jīng)過計(jì)算機(jī)行業(yè)的迅速發(fā)展,計(jì)算機(jī)的存儲(chǔ)容量已經(jīng)達(dá)到了很高的程度。所以我們?nèi)缃褚呀?jīng)不需要再特別關(guān)注一個(gè)算法的空間復(fù)雜度。
2.算法的時(shí)間復(fù)雜度
2.1 時(shí)間復(fù)雜度的概念
時(shí)間復(fù)雜度的定義:在計(jì)算機(jī)科學(xué)中, 算法的時(shí)間復(fù)雜度是一個(gè)函數(shù)( 數(shù)學(xué)函數(shù)式,不是c語言的那些嵌套函數(shù)) ,它定量描述了該算法的運(yùn)行時(shí)間。一個(gè)算法執(zhí)行所耗費(fèi)的時(shí)間,從理論上說,是不能算出來的,只有你把你的程序放在機(jī)器上跑起來,才能知道。但是我們需要每個(gè)算法都上機(jī)測試嗎?是可以都上機(jī)測試,但是這很麻煩,所以才有了時(shí)間復(fù)雜度這個(gè)分析方式。一個(gè)算法所花費(fèi)的時(shí)間與其中語句的執(zhí)行次數(shù)成正比例,算法中的基本操作的 執(zhí)行次數(shù) ,為算法 的時(shí)間復(fù)雜度。
計(jì)算Func1中++count語句總共執(zhí)行了多少次
// 請計(jì)算一下Func1中++count語句總共執(zhí)行了多少次?
void Func1(int N)
{
int count = 0;
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < N; ++j)
{
++count;
}
}
for (int k = 0; k < 2 * N; ++k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
時(shí)間復(fù)雜度的函數(shù)式(也就是Func1 執(zhí)行的基本操作次數(shù)):
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? F(N)=N^2+2N+10
但是這個(gè)表達(dá)式,太準(zhǔn)確,太細(xì)節(jié),太繁瑣了。時(shí)間復(fù)雜度不是準(zhǔn)確地計(jì)算出這個(gè)數(shù)學(xué)函數(shù)式的執(zhí)行次數(shù),而是給它分一個(gè)級別,它到底是哪個(gè)量級的。
舉例:就像馬云和馬化騰,不需要關(guān)心他們的賬戶具體幾分幾毛,只需要知道他們是富豪就行了。
準(zhǔn)確值F(N)=N^2+2N+10 | 估算值O(N^2) | |
N = 10
|
F(N) = 130 | 100 |
N = 100
|
F(N) = 10210
|
10000 |
N = 1000
|
F(N) = 1002010
|
1000000 |
實(shí)際中我們計(jì)算時(shí)間復(fù)雜度時(shí),我們其實(shí)并不一定要計(jì)算精確的執(zhí)行次數(shù),而只需要 大概執(zhí)行次數(shù),那么這 里我們使用大 O 的漸進(jìn)表示法。只要用大O這個(gè)東西來表示就說明它是一個(gè)估算的值。
2.2 大O的漸進(jìn)表示法
大 O 符號( Big O notation ):是用于描述函數(shù)漸進(jìn)行為的數(shù)學(xué)符號。
1 、用常數(shù) 1 取代運(yùn)行時(shí)間中的所有加法 常數(shù) 。2 、在修改后的運(yùn)行次數(shù)函數(shù)中, 只保留最高階項(xiàng) 。3 、如果最高階項(xiàng)存在且不是 1 ,則 去除 與這個(gè)項(xiàng)目 相乘的常數(shù) 。得到的結(jié)果就是大 O 階。
最壞情況:任意輸入規(guī)模的最大運(yùn)行次數(shù) ( 上界 )平均情況:任意輸入規(guī)模的期望運(yùn)行次數(shù)最好情況:任意輸入規(guī)模的最小運(yùn)行次數(shù) ( 下界 )
最好情況: 1 次找到最壞情況: N 次找到平均情況: N/2 次找到
2.3常見時(shí)間復(fù)雜度計(jì)算舉例?
實(shí)例1:執(zhí)行2N+10次
// 計(jì)算Func2的時(shí)間復(fù)雜度?
void Func2(int N)
{
int count = 0;
for (int k = 0; k < 2 * N ; ++ k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
基本操作執(zhí)行了2N+10次,通過推導(dǎo)大O階方法知道,時(shí)間復(fù)雜度為 O(N)
實(shí)例2:執(zhí)行M+N次
// 計(jì)算Func3的時(shí)間復(fù)雜度?
void Func3(int N, int M)
{
int count = 0;
for (int k = 0; k < M; ++ k)
{
++count;
}
for (int k = 0; k < N ; ++ k)
{
++count;
}
printf("%d\n", count);
}
實(shí)例 2 基本操作執(zhí)行了 M+N 次,有兩個(gè)未知數(shù) M 和 N ,時(shí)間復(fù)雜度為 O(N+M)不能說N無限大了,M就不重要了。除非說會(huì)給出一個(gè)關(guān)系:N遠(yuǎn)大于M,則時(shí)間復(fù)雜度為O(N)M遠(yuǎn)大于N,則時(shí)間復(fù)雜度為O(M)M等于N或二者相差不大時(shí),則時(shí)間復(fù)雜度為O(M+N)
實(shí)例3:執(zhí)行了100000000次
void Func4(int N)
{
int count = 0;
for (int k = 0; k < 100000000; ++k)
{
++count;
}
printf("%d\n", count + N);
}
int main()
{
Func4(100000);
Func4(1);
return 0;
}
執(zhí)行:實(shí)際上cpu的速度是非??斓模嗖畹膱?zhí)行次數(shù)可以忽略,所以時(shí)間復(fù)雜度依舊為O(1)
O(1)不是代表1次,而是代表常數(shù)次,就算k<10億,它也是O(1)
我們平時(shí)能寫到的常數(shù)最大也就是40多億左右(整型能表示的范圍),cpu是可以承受的。
實(shí)例3基本操作執(zhí)行了100000000次,通過推導(dǎo)大O階方法,時(shí)間復(fù)雜度為 O(1)
實(shí)例4:計(jì)算strchr的時(shí)間復(fù)雜度
// 計(jì)算strchr的時(shí)間復(fù)雜度?
const char * strchr ( const char * str, int character );
這是關(guān)于strchr的模擬實(shí)現(xiàn)
#include<stdio.h>
#include<assert.h>
char* my_strchr(const char* str, const char ch)
{
assert(str);
const char* dest = str;
while (dest != '\0' && *dest != ch)
{
dest++;
}
if (*dest == ch)
return (char*)dest;
return NULL;
}
int main()
{
char* ret = my_strchr("hello", 'l');
if (ret == NULL)
printf("不存在");
else
printf("%s\n", ret);
return 0;
}
我的理解就是strchr和strstr的區(qū)別:就是strstr是輸入一個(gè)字符串,在主串中查找,而strchr是輸入一個(gè)字符,然后在主串中查找。這個(gè)鏈接有關(guān)于strstr的知識點(diǎn):http://t.csdn.cn/NEaip
若查找失敗,返回NULL。查找成功則返回首字符的地址,然后打印的時(shí)候一直到'\0'結(jié)束
所以說:
指明了這個(gè)數(shù)組的長度然后去查找它的時(shí)間復(fù)雜度才是O(1),長度不明確的話,長度就是N,那么需要遞歸N次,時(shí)間復(fù)雜度就是O(N)實(shí)例 4 基本操作執(zhí)行最好 1 次,最壞 N 次,時(shí)間復(fù)雜度一般看最壞,時(shí)間復(fù)雜度為 O(N)
實(shí)例5:計(jì)算BubbleSort的時(shí)間復(fù)雜度
// 計(jì)算BubbleSort的時(shí)間復(fù)雜度?
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; --end)
{
int exchange = 0;
for (size_t i = 1; i < end; ++i)
{
if (a[i-1] > a[i])
{
Swap(&a[i-1], &a[i]);
exchange = 1;
}
}
if (exchange == 0)
break;
}
}

?那么比較的次數(shù)構(gòu)成等差數(shù)列:用等差數(shù)列求和公式得到最后的執(zhí)行次數(shù)是F(N)=(N-1)*N/2;
這題關(guān)于循環(huán)的不是說有兩層循環(huán)嵌套就直接判斷它的時(shí)間復(fù)雜度是O(N^2),因?yàn)槿绻容^次數(shù)是已知的(外層循環(huán)n<10,內(nèi)層循環(huán)n<1000000)那就是O(1) ,而且冒泡排序會(huì)有優(yōu)化版本,在有序的情況下,他的時(shí)間復(fù)雜度是O(N),只走外層循環(huán)。實(shí)例 5 基本操作執(zhí)行最好 N 次,最壞執(zhí)行了 (N*(N+1)/2 次,通過推導(dǎo)大 O 階方法 + 時(shí)間復(fù)雜度一般看最壞,時(shí)間復(fù)雜度為 O(N^2)
實(shí)例6:計(jì)算BinarySearch的時(shí)間復(fù)雜度
// 計(jì)算BinarySearch的時(shí)間復(fù)雜度?
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin = 0;
int end = n-1;
// [begin, end]:begin和end是左閉右閉區(qū)間,因此有=號
while (begin <= end)
{
int mid = begin + ((end-begin)>>1);
if (a[mid] < x)
begin = mid+1;
else if (a[mid] > x)
end = mid-1;
else
return mid;
}
return -1;
}
圖解:
假設(shè)找了x次,那么除了x個(gè)2
2^x =N? -->?x = log2N? ?
所以說可以從最后一次查找一直乘2,乘到原數(shù)組的長度
實(shí)例6基本操作執(zhí)行最好1次,最壞O(log2N)次,時(shí)間復(fù)雜度為 O(log2N) ps:logN在算法分析中表示是底數(shù)為2,對數(shù)為N。有些地方會(huì)寫成lgN。
實(shí)例7:?計(jì)算階乘遞歸Fac的時(shí)間復(fù)雜度
//實(shí)例7:
// 計(jì)算階乘遞歸Fac的時(shí)間復(fù)雜度?
long long Fac(size_t N)
{
if (0 == N)
return 1;
return Fac(N - 1) * N;
}
long long Fac(size_t N)
{
if (0 == N)
return 1;
for (size_t i = 0; i < N; i++)
{
}
return Fac(N - 1) * N;
}
圖解:
左邊的每一次函數(shù)調(diào)用里面的for循環(huán)語句(如果有其它循環(huán)語句也會(huì)算上)的執(zhí)行次數(shù),左邊的1表示它是常數(shù)次,而不是1次(就說如果函數(shù)里面沒什么循環(huán)語句,有幾個(gè)if語句,那時(shí)間復(fù)雜度也是O(1))。
右邊的簡單來說就說有N+1個(gè)函數(shù)調(diào)用,而每一個(gè)函數(shù)調(diào)用里面的循環(huán)語句都執(zhí)行了N+1次,所以應(yīng)該把每次的遞歸調(diào)用的函數(shù)里面的循環(huán)語句都加起來。
補(bǔ)充的點(diǎn):
時(shí)間是加起來的,不是乘起來的,就比如說上圖的return Fac(N-1)*N:表示的是上一次的結(jié)果乘N,但是執(zhí)行次數(shù)也是一次,因?yàn)檫@個(gè)地方的*對于計(jì)算機(jī)來說僅僅是一個(gè)指令。時(shí)間復(fù)雜度算的是這個(gè)程序在走的過程中這個(gè)指令的執(zhí)行次數(shù)。
實(shí)例8:計(jì)算斐波那契遞歸Fib的時(shí)間復(fù)雜度
// 計(jì)算斐波那契遞歸Fib的時(shí)間復(fù)雜度?
long long Fib(size_t N)
{
if (N < 3)
return 1;
return Fib(N - 1) + Fib(N - 2);
}
圖解:
?執(zhí)行次數(shù)之和符合等比數(shù)列:使用錯(cuò)位相減法
這題可以這樣理解:
關(guān)于那個(gè)三角形,白色的區(qū)域在N越大的情況下,就會(huì)遠(yuǎn)遠(yuǎn)大于黑色區(qū)域,而時(shí)間復(fù)雜度是用來計(jì)算大致計(jì)算某個(gè)數(shù)學(xué)函數(shù)是的量級的,給它分一個(gè)級別,所以可以看作是滿項(xiàng)的狀態(tài)下計(jì)算,然后執(zhí)行次數(shù)之和構(gòu)成等比數(shù)列,用大O漸進(jìn)表示法去算時(shí)間復(fù)雜度為O(2^N)。
3.算法的空間復(fù)雜度
空間復(fù)雜度也是一個(gè)數(shù)學(xué)表達(dá)式,是對一個(gè)算法在運(yùn)行過程中 臨時(shí)占用存儲(chǔ)空間大小的量度 。空間復(fù)雜度不是程序占用了多少 bytes 的空間,因?yàn)檫@個(gè)也沒太大意義,所以空間復(fù)雜度算的是 變量的個(gè)數(shù) 。空間復(fù)雜度計(jì)算規(guī)則基本跟實(shí)踐復(fù)雜度類似,也使用 大O漸進(jìn)表示法 。注意: 函數(shù)運(yùn)行時(shí)所需要的棧空間 ( 存儲(chǔ)參數(shù)、局部變量、一些寄存器信息等 ) 在編譯期間已經(jīng)確定好了,因 此 空間復(fù)雜度 主要通過函數(shù)在運(yùn)行時(shí)候顯式申請的 額外空間 來確定。
實(shí)例1:計(jì)算BubbleSort的空間復(fù)雜度
// 計(jì)算BubbleSort的空間復(fù)雜度?
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; --end)
{
int exchange = 0;
for (size_t i = 1; i < end; ++i)
{
if (a[i - 1] > a[i])
{
Swap(&a[i - 1], &a[i]);
exchange = 1;
}
}
if (exchange == 0)
break;
}
}
因?yàn)檫@里只創(chuàng)建了一個(gè)end,exchange,i三個(gè)變量,只計(jì)算變量個(gè)數(shù),不管變量類型也不算空間具體的字節(jié)數(shù),而且都是在循環(huán)里創(chuàng)建的,所以空間復(fù)雜度為O(1)。
而關(guān)于形參int *a,和int n,它們不會(huì)被算在空間復(fù)雜度中。
實(shí)例2:計(jì)算Fibonacci的空間復(fù)雜度
// 計(jì)算Fibonacci的空間復(fù)雜度?
// 返回斐波那契數(shù)列的前n項(xiàng)
long long* Fibonacci(size_t n)
{
if(n==0)
return NULL;
long long * fibArray = (long long *)malloc((n+1) * sizeof(long long));
fibArray[0] = 0;
fibArray[1] = 1;
for (int i = 2; i <= n ; ++i)
{
fibArray[i] = fibArray[i - 1] + fibArray [i - 2];
}
return fibArray;
}
空間復(fù)雜度,它計(jì)算的是你在這個(gè)函數(shù)內(nèi)部開辟了多少額外空間,如果是常數(shù)個(gè)的話,就是O1,如果開辟的大小不確定,一般就是O(N)。
所以說空間復(fù)雜度為O(N)。
實(shí)例3:計(jì)算階乘遞歸Fac的空間復(fù)雜度
// 計(jì)算階乘遞歸Fac的空間復(fù)雜度?
long long Fac(size_t N)
{
if(N == 0)
return 1;
return Fac(N-1)*N;
}
圖解:
遞歸調(diào)用了N層每次調(diào)用建立一個(gè)棧幀,每個(gè)棧幀使用了常數(shù)個(gè)空間O(1)
由于這里調(diào)用了N個(gè)函數(shù),同時(shí)沒有返回,所以合起來就是O(N)
實(shí)例4:計(jì)算斐波那契遞歸Fib的空間復(fù)雜度(兩個(gè)遞歸)
// 計(jì)算斐波那契遞歸Fib的空間復(fù)雜度?
long long Fib(size_t N)
{
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}
前言:
空間的銷毀不是整沒了那塊空間,是歸還使用權(quán),歸還給操作系統(tǒng)。
因?yàn)閮?nèi)存空間是屬于操作系統(tǒng)進(jìn)程的,比如說讓你malloc一塊空間,就獲得這塊空間的使用權(quán),free一下就把空間使用權(quán)還給操作系統(tǒng)了
時(shí)間是一去不復(fù)返時(shí)間是累積計(jì)算的,空間是可以重復(fù)利用不累積計(jì)算
簡單的說,右邊的函數(shù)和左邊的函數(shù)共用一個(gè)棧幀。
代碼運(yùn)行:棧是向下生長的,調(diào)用Func1和Func2相當(dāng)于共用一塊空間,因?yàn)镕unc1銷毀之后,到Func2創(chuàng)建,位置還是那個(gè)位置,地址也是那個(gè)地址。
因?yàn)橹骱瘮?shù)的a和Func1的a在不同棧幀里面,所以可以同名。
實(shí)例4遞歸調(diào)用了N次,開辟了N個(gè)棧幀,每個(gè)棧幀使用了常數(shù)個(gè)空間??臻g復(fù)雜度為O(N)
調(diào)用時(shí),建立棧幀;
返回時(shí),銷毀。(歸還給操作系統(tǒng))
4.常見復(fù)雜度對比

圖解:
文章來源:http://www.zghlxwxcb.cn/news/detail-547936.html
?本章完,如有不足之處,歡迎大佬指正。文章來源地址http://www.zghlxwxcb.cn/news/detail-547936.html
到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】算法的時(shí)間和空間復(fù)雜度的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!