演算法效能指標的衡量
1. 時間複雜度
概念:時間複雜度實際上就是一個函式,用來計算一個演算法執行基本操作的次數。基本操作顧名思義,即就是一個演算法的最基本的運算,如:計算1+2+...+100,它的基本操作為’+’。
時間複雜度的計算:在實際操作中我們通常關注的是一個演算法的最壞執行情況,即對於任意輸入規模N,演算法執行的最長時間。一般情況下使用O漸進法來計算。
f(N):N為演算法規模,f(N)為演算法時間複雜度表達函式,則該演算法的時間複雜度用O漸進表示法可表示為O(f(N)),此時的f(N)是表達函式中增長最快的一項。常數型增長可表示為O(1),1並不是表示一次,而是表示常數次,忽略常數及常係數。
eg:氣泡排序:f(n)=(n-1)+(n-2)+...+1=(n-1)(n-2)/2
由氣泡排序的時間複雜度表達函式可得用O漸進表示法可表示氣泡排序的時間複雜度為O(n^2)。
2. 空間複雜度
概念:一個表示演算法開闢空間大小的函式,表示:O(f(N))。f(N)為演算法的空間複雜度表達函式O(1)則表示演算法每執行一次開闢常數個空間。
遞迴呼叫的空間複雜度 = 遞迴的深度 * 每次遞迴開闢的空間大小
eg: 斐波那契數列:1,1,2,3,5,8,13,21...
每次遞迴開闢常數個空間,遞迴N次,其空間複雜度為O(N)。
3. 例項分析
1.二分查詢(升序陣列中查詢元素x,找到了返回該元素下標,未找到返回-1)
二分查詢的的遞迴及非遞迴實現如下:
#include<stdio.h> #include<windows.h> //遞迴演算法 int binarySearch1(int *a,int key,int len) { int end = len-1,start = 0,mid = 0; while(start <= end) { mid=(end+start)/2; if(key<a[mid]) { end = mid - 1; } else if(key>a[mid]) { start = mid + 1; } else { return mid; } } return -1; }
//非遞迴演算法
int binarySearch2(int *arr,int key,int a,int b)
{
int mid = 0;
if(a>b)
{
return -1;
}
else
{
mid = (a+b)/2;
if(arr[mid]==key)
{
return mid;
}
else if(arr[mid]<key)
{
search2(arr,key,mid+1,b);
}
else
{
search2(arr,key,a,mid-1);
}
}
}
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9};
int len = sizeof(a)/sizeof(a[0]);
printf("%d\n",binarySearch1(a,5,len));
printf("%d\n",binarySearch2(a,2,0,len-1));
system("pause");
return 0;
}
在二分查詢演算法中,若用x表示查詢次數,n表示陣列中元素個數,則x和n滿足表示式2^x=n,即二分查詢的時間複雜度函式為x=log2(n);時間複雜度為O(log(n))。其中遞迴演算法的空間複雜度為O(log(n)),非遞迴空間複雜度為O(1)。
(2)斐波那契數列(從第三項開始後一項等於前兩項之和)
程式碼實現如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
//遞迴
int Fibonaci1(int input)
{
if((input==1)||(input==2))
{
return 1;
}
return Fibonaci(input-1)+Fibonaci(input-2);
}
//非遞迴
int Fibonaci(int input)
{
int input1 = 1;
int input2 = 1;
int result = 0;
if((input == 1)||(input == 2))
{
return 1;
}
while(input>2)
{
result=input1+input2;
input1=input2;
input2=result;
input--;
}
return result;
}
int main()
{
int input = 0;
scanf("%d",&input);
printf("非遞迴輸出第%d個Fibonacci數為:%d\n",input,Fibonaci(input));
printf(" 遞迴輸出第%d個Fibonacci數為:%d\n",input,Fibonaci1(input));
system("pause");
return 0;
}
斐波那契數列演算法的時間複雜度計算:演算法規模:n,則有時間複雜度函式f(n)=2^n-a(常數),故有 斐波那契數列的時間複雜度為O(2^n)。遞迴演算法中每次開闢常數個空間,遞迴深度為n,所以遞迴演算法的空間複雜度為O(n),非遞迴演算法的空間複雜度為O(1)。4.尾遞迴
概念:函式返回之前的最後一個操作若是遞迴呼叫,則該函式進行了尾遞迴。
優點:在遞迴呼叫中,系統不斷的進行壓棧、退棧操作,一旦處理的資料過多,很有可能會導致棧溢位,但尾遞迴是在呼叫之前已將所有計算任務完成,無需有其他儲存,只需將得到的結果交給子函式即可,因此子函式可以利用當前棧幀,將原先資料覆蓋即可,運算相對一般遞迴更節省空間,防止了棧溢位,減少了建立棧幀時的開銷。