演算法復雜度比較
㈠ 演算法復雜度是什麼概念
同一問題可用不同演算法解決,而一個演算法的質量優劣將影響到演算法乃至程序的效率。演算法分析的目的在於選擇合適演算法和改進演算法。一個演算法的評價主要從時間復雜度和空間復雜度來考慮。
1、時間復雜度
(1)時間頻度
一個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只需知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。並且一個演算法花費的時間與演算法中語句的執行次數成正比例,哪個演算法中語句執行次數多,它花費時間就多。一個演算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。
(2)時間復雜度
在剛才提到的時間頻度中,n稱為問題的規模,當n不斷變化時,時間頻度T(n)也會不斷變化。但有時我們想知道它變化時呈現什麼規律。為此,我們引入時間復雜度概念。
一般情況下,演算法中基本操作重復執行的次數是問題規模n的某個函數,用T(n)表示,若有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)是T(n)的同數量級函數。記作T(n)=O(f(n)),稱O(f(n)) 為演算法的漸進時間復雜度,簡稱時間復雜度。
在各種不同演算法中,若演算法中語句執行次數為一個常數,則時間復雜度為O(1),另外,在時間頻度不相同時,時間復雜度有可能相同,如T(n)=n2+3n+4與T(n)=4n2+2n+1它們的頻度不同,但時間復雜度相同,都為O(n2)。
按數量級遞增排列,常見的時間復雜度有:
常數階O(1),對數階O(log2n),線性階O(n),
線性對數階O(nlog2n),平方階O(n2),立方階O(n3),...,
k次方階O(nk),指數階O(2n)。隨著問題規模n的不斷增大,上述時間復雜度不斷增大,演算法的執行效率越低。
2、空間復雜度
與時間復雜度類似,空間復雜度是指演算法在計算機內執行時所需存儲空間的度量。記作:
S(n)=O(f(n))
說到底,就是程序執行效率和佔用空間的問題.
㈡ 不同演算法的時間復雜度可以比較嗎
這個不一定。
可以看出時間復雜度是衡量一個量級上的差距,這個量級上的差距表現在當n突破到一個點的時候,時間復雜度低的演算法就一定要比時間復雜度高的演算法快,而且n越大,這個優勢越明顯。而我們研究演算法就是要處理大規模的數據,如果數據比較小的話,時間總是夠用的。
不過我們可以看到,復雜度高的演算法可能它會有前面常數小的優勢,所以在我們數據規模比較小的時候,它是有意義的,比如所有的高級排序演算法,當數據規模小到一定程度時,我們都可以轉而使用插入排序法,來進行優化,而這個優化一般有百分之十到十五的,在一些情況下還是很有意義的,但是這是細節上的優化,但整體上我們還是要追求復雜度低的演算法。
㈢ 比較兩個代碼的演算法復雜度
演算法復雜度是指演算法在編寫成可執行程序後,運行時所需要的資源,資源包括時間資源和內存資源。應用於數學和計算機導論。
㈣ 演算法時間復雜度怎麼算
一、概念
時間復雜度是總運算次數表達式中受n的變化影響最大的那一項(不含系數)
比如:一般總運算次數表達式類似於這樣:
a*2^n+b*n^3+c*n^2+d*n*lg(n)+e*n+f
a ! =0時,時間復雜度就是O(2^n);
a=0,b<>0 =>O(n^3);
a,b=0,c<>0 =>O(n^2)依此類推
eg:
(1) for(i=1;i<=n;i++) //循環了n*n次,當然是O(n^2)
for(j=1;j<=n;j++)
s++;
(2) for(i=1;i<=n;i++)//循環了(n+n-1+n-2+...+1)≈(n^2)/2,因為時間復雜度是不考慮系數的,所以也是O(n^2)
for(j=i;j<=n;j++)
s++;
(3) for(i=1;i<=n;i++)//循環了(1+2+3+...+n)≈(n^2)/2,當然也是O(n^2)
for(j=1;j<=i;j++)
s++;
(4) i=1;k=0;
while(i<=n-1){
k+=10*i; i++; }//循環了n-1≈n次,所以是O(n)(5) for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
for(k=1;k<=j;k++)
x=x+1;
//循環了(1^2+2^2+3^2+...+n^2)=n(n+1)(2n+1)/6(這個公式要記住哦)≈(n^3)/3,不考慮系數,自然是O(n^3)
另外,在時間復雜度中,log(2,n)(以2為底)與lg(n)(以10為底)是等價的,因為對數換底公式:
log(a,b)=log(c,b)/log(c,a)
所以,log(2,n)=log(2,10)*lg(n),忽略掉系數,二者當然是等價的
二、計算方法1.一個演算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但我們不可能也沒有必要對每個演算法都上機測試,只需知道哪個演算法花費的時間多,哪個演算法花費的時間少就可以了。並且一個演算法花費的時間與演算法中語句的執行次數成正比例,哪個演算法中語句執行次數多,它花費時間就多。
一個演算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。
2.一般情況下,演算法的基本操作重復執行的次數是模塊n的某一個函數f(n),因此,演算法的時間復雜度記做:T(n)=O(f(n))。隨著模塊n的增大,演算法執行的時間的增長率和f(n)的增長率成正比,所以f(n)越小,演算法的時間復雜度越低,演算法的效率越高。
在計算時間復雜度的時候,先找出演算法的基本操作,然後根據相應的各語句確定它的執行次數,再找出T(n)的同數量級(它的同數量級有以下:1,Log2n ,n ,nLog2n ,n的平方,n的三次方,2的n次方,n!),找出後,f(n)=該數量級,若T(n)/f(n)求極限可得到一常數c,則時間復雜度T(n)=O(f(n))。
3.常見的時間復雜度
按數量級遞增排列,常見的時間復雜度有:
常數階O(1), 對數階O(log2n), 線性階O(n), 線性對數階O(nlog2n), 平方階O(n^2), 立方階O(n^3),..., k次方階O(n^k), 指數階O(2^n) 。
其中,1.O(n),O(n^2), 立方階O(n^3),..., k次方階O(n^k) 為多項式階時間復雜度,分別稱為一階時間復雜度,二階時間復雜度。。。。2.O(2^n),指數階時間復雜度,該種不實用3.對數階O(log2n), 線性對數階O(nlog2n),除了常數階以外,該種效率最高
例:演算法:
for(i=1;i<=n;++i)
{
for(j=1;j<=n;++j)
{
c[ i ][ j ]=0; //該步驟屬於基本操作 執行次數:n^2
for(k=1;k<=n;++k)
c[ i ][ j ]+=a[ i ][ k ]*b[ k ][ j ]; //該步驟屬於基本操作 執行次數:n^3
}
}
則有 T(n)= n^2+n^3,根據上面括弧里的同數量級,我們可以確定 n^3為T(n)的同數量級
則有f(n)= n^3,然後根據T(n)/f(n)求極限可得到常數c
則該演算法的 時間復雜度:T(n)=O(n^3)
四、
定義:如果一個問題的規模是n,解這一問題的某一演算法所需要的時間為T(n),它是n的某一函數
T(n)稱為這一演算法的「時間復雜性」。
當輸入量n逐漸加大時,時間復雜性的極限情形稱為演算法的「漸近時間復雜性」。
我們常用大O表示法表示時間復雜性,注意它是某一個演算法的時間復雜性。大O表示只是說有上界,由定義如果f(n)=O(n),那顯然成立f(n)=O(n^2),它給你一個上界,但並不是上確界,但人們在表示的時候一般都習慣表示前者。
此外,一個問題本身也有它的復雜性,如果某個演算法的復雜性到達了這個問題復雜性的下界,那就稱這樣的演算法是最佳演算法。
「大O記法」:在這種描述中使用的基本參數是
n,即問題實例的規模,把復雜性或運行時間表達為n的函數。這里的「O」表示量級 (order),比如說「二分檢索是 O(logn)的」,也就是說它需要「通過logn量級的步驟去檢索一個規模為n的數組」記法 O ( f(n) )表示當 n增大時,運行時間至多將以正比於 f(n)的速度增長。
這種漸進估計對演算法的理論分析和大致比較是非常有價值的,但在實踐中細節也可能造成差異。例如,一個低附加代價的O(n2)演算法在n較小的情況下可能比一個高附加代價的 O(nlogn)演算法運行得更快。當然,隨著n足夠大以後,具有較慢上升函數的演算法必然工作得更快。
O(1)
Temp=i;i=j;j=temp;
以上三條單個語句的頻度均為1,該程序段的執行時間是一個與問題規模n無關的常數。演算法的時間復雜度為常數階,記作T(n)=O(1)。如果演算法的執行時間不隨著問題規模n的增加而增長,即使演算法中有上千條語句,其執行時間也不過是一個較大的常數。此類演算法的時間復雜度是O(1)。
O(n^2)
2.1.
交換i和j的內容
sum=0;(一次)
for(i=1;i<=n;i++)(n次 )
for(j=1;j<=n;j++)
(n^2次 )
sum++;(n^2次 )
解:T(n)=2n^2+n+1 =O(n^2)
2.2.
for (i=1;i<n;i++)
{
y=y+1;①
for
(j=0;j<=(2*n);j++)
x++;②
}
解:
語句1的頻度是n-1
語句2的頻度是(n-1)*(2n+1)=2n^2-n-1
f(n)=2n^2-n-1+(n-1)=2n^2-2
該程序的時間復雜度T(n)=O(n^2).
O(n)
2.3.
a=0;
b=1;①
for
(i=1;i<=n;i++) ②
{
s=a+b;③
b=a;④
a=s;⑤
}
解:語句1的頻度:2,
語句2的頻度:
n,
語句3的頻度: n-1,
語句4的頻度:n-1,
語句5的頻度:n-1,
T(n)=2+n+3(n-1)=4n-1=O(n).
O(log2n
)
2.4.
i=1;①
while (i<=n)
i=i*2; ②
解: 語句1的頻度是1,
設語句2的頻度是f(n),則:2^f(n)<=n;f(n)<=log2n
取最大值f(n)=
log2n,
T(n)=O(log2n )
O(n^3)
2.5.
for(i=0;i<n;i++)
{
for(j=0;j<i;j++)
{
for(k=0;k<j;k++)
x=x+2;
}
}
解:當i=m,
j=k的時候,內層循環的次數為k當i=m時, j 可以取 0,1,...,m-1 , 所以這里最內循環共進行了0+1+...+m-1=(m-1)m/2次所以,i從0取到n, 則循環共進行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6所以時間復雜度為O(n^3).
我們還應該區分演算法的最壞情況的行為和期望行為。如快速排序的最
壞情況運行時間是 O(n^2),但期望時間是 O(nlogn)。通過每次都仔細 地選擇基準值,我們有可能把平方情況 (即O(n^2)情況)的概率減小到幾乎等於 0。在實際中,精心實現的快速排序一般都能以 (O(nlogn)時間運行。
下面是一些常用的記法:
訪問數組中的元素是常數時間操作,或說O(1)操作。一個演算法如 果能在每個步驟去掉一半數據元素,如二分檢索,通常它就取 O(logn)時間。用strcmp比較兩個具有n個字元的串需要O(n)時間。常規的矩陣乘演算法是O(n^3),因為算出每個元素都需要將n對
元素相乘並加到一起,所有元素的個數是n^2。
指數時間演算法通常來源於需要求出所有可能結果。例如,n個元 素的集合共有2n個子集,所以要求出所有子集的演算法將是O(2n)的。指數演算法一般說來是太復雜了,除非n的值非常小,因為,在 這個問題中增加一個元素就導致運行時間加倍。不幸的是,確實有許多問題 (如著名的「巡迴售貨員問題」 ),到目前為止找到的演算法都是指數的。如果我們真的遇到這種情況,通常應該用尋找近似最佳結果的演算法替代之。
㈤ 如何分析演算法的復雜度
演算法的復雜性
演算法的復雜性是演算法效率度量,是評價演算法優劣的重要依據。一個演算法的復雜性的高低體現在運行該演算法所需要的計算機資源的多少上面,所需的資源越多,我們就說該演算法的復雜性越高;反之,所需的資源越低,則該演算法的復雜性越低。
計算機的資源,最重要的是時間和空間(即存儲器)資源。因而,演算法的復雜性有時間復雜性和空間復雜性之分。
不言而喻,對於任意給定的問題,設計出復雜性盡可能低的演算法是我們在設計演算法時追求的一個重要目標;另一方面,當給定的問題已有多種演算法時,選擇其中復雜性最低者,是我們在選用演算法適應遵循的一個重要准則。因此,演算法的復雜性分析對演算法的設計或選用有著重要的指導意義和實用價值。
簡言之,在演算法學習過程中,我們必須首先學會對演算法的分析,以確定或判斷演算法的優劣。
1.時間復雜性:
例1:設一程序段如下(為討論方便,每行前加一行號)
(1) for i:=1 to n do
(2) for j:=1 to n do
(3) x:=x+1
......
試問在程序運行中各步執行的次數各為多少?
解答:
行號 次數(頻度)
(1) n+1
(2) n*(n+1)
(3) n*n
可見,這段程序總的執行次數是:f(n)=2n2+2n+1。在這里,n可以表示問題的規模,當n趨向無窮大時,如果 f(n)的值很小,則演算法優。作為初學者,我們可以用f(n)的數量級O來粗略地判斷演算法的時間復雜性,如上例中的時間復雜性可粗略地表示為T(n)=O(n2)。
㈥ 用代碼實現幾種排序演算法的時間復雜度比較
一、簡單排序演算法
由於程序比較簡單,所以沒有加什麼注釋。所有的程序都給出了完整的運行代碼,並在我的VC環境
下運行通過。因為沒有涉及MFC和WINDOWS的內容,所以在BORLAND C++的平台上應該也不會有什麼
問題的。在代碼的後面給出了運行過程示意,希望對理解有幫助。
1.冒泡法:
這是最原始,也是眾所周知的最慢的演算法了。他的名字的由來因為它的工作看來象是冒泡:
#include <iostream>
using namespace std;
void BubbleSort(int * pData, int Count)
{
int iTemp;
for (int i=0; i<Count; i++)
{
for (int j=Count-1; j>i; j--)
{
if (pData[j]<pData[j-1])
{
iTemp = pData[j-1];
pData[j-1] = pData[j];
pData[j] = iTemp;
}
}
}
}
void main()
{
int data[7] = {10,9,8,7,6,5,4};
BubbleSort(data,7);
for (int i=0; i<7; i++)
{
cout<<data[i]<<" ";
}
cout<<endl;
system("PAUSE");
}
倒序(最糟情況)
第一輪:10,9,8,7->10,9,7,8->10,7,9,8->7,10,9,8(交換3次)
第二輪:7,10,9,8->7,10,8,9->7,8,10,9(交換2次)
第一輪:7,8,10,9->7,8,9,10(交換1次)
循環次數:6次
交換次數:6次
其他:
第一輪:8,10,7,9->8,10,7,9->8,7,10,9->7,8,10,9(交換2次)
第二輪:7,8,10,9->7,8,10,9->7,8,10,9(交換0次)
第一輪:7,8,10,9->7,8,9,10(交換1次)
循環次數:6次
交換次數:3次
上面我們給出了程序段,現在我們分析它:這里,影響我們演算法性能的主要部分是循環和交換,
顯然,次數越多,性能就越差。從上面的程序我們可以看出循環的次數是固定的,為1+2+...+n-1。
寫成公式就是1/2*(n-1)*n。
現在注意,我們給出O方法的定義:
若存在一常量K和起點n0,使當n>=n0時,有f(n)<=K*g(n),則f(n) = O(g(n))。(呵呵,不要說沒學好數學呀,對於編程數學是非常重要的!!!)
現在我們來看1/2*(n-1)*n,當K=1/2,n0=1,g(n)=n*n時,1/2*(n-1)*n<=1/2*n*n=K*g(n)。所以f(n)
=O(g(n))=O(n*n)。所以我們程序循環的復雜度為O(n*n)。
再看交換。從程序後面所跟的表可以看到,兩種情況的循環相同,交換不同。其實交換本身同數據源的
有序程度有極大的關系,當數據處於倒序的情況時,交換次數同循環一樣(每次循環判斷都會交換),
復雜度為O(n*n)。當數據為正序,將不會有交換。復雜度為O(0)。亂序時處於中間狀態。正是由於這樣的
原因,我們通常都是通過循環次數來對比演算法。
2.交換法:
交換法的程序最清晰簡單,每次用當前的元素一一的同其後的元素比較並交換。
#include <iostream.h>
void ExchangeSort(int* pData,int Count)
{
int iTemp;
for(int i=0;i<Count-1;i++)
{ //共(count-1)輪,每輪得到一個最小值
for(int j=i+1;j<Count;j++)
{ //每次從剩下的數字中尋找最小值,於當前最小值相比,如果小則交換
if(pData[j]9,10,8,7->8,10,9,7->7,10,9,8(交換3次)
第二輪:7,10,9,8->7,9,10,8->7,8,10,9(交換2次)
第一輪:7,8,10,9->7,8,9,10(交換1次)
循環次數:6次
交換次數:6次
其他:
第一輪:8,10,7,9->8,10,7,9->7,10,8,9->7,10,8,9(交換1次)
第二輪:7,10,8,9->7,8,10,9->7,8,10,9(交換1次)
第一輪:7,8,10,9->7,8,9,10(交換1次)
循環次數:6次
交換次數:3次
從運行的表格來看,交換幾乎和冒泡一樣糟。事實確實如此。循環次數和冒泡一樣
也是1/2*(n-1)*n,所以演算法的復雜度仍然是O(n*n)。由於我們無法給出所有的情況,所以
只能直接告訴大家他們在交換上面也是一樣的糟糕(在某些情況下稍好,在某些情況下稍差)。
3.選擇法:
現在我們終於可以看到一點希望:選擇法,這種方法提高了一點性能(某些情況下)
這種方法類似我們人為的排序習慣:從數據中選擇最小的同第一個值交換,在從省下的部分中
選擇最小的與第二個交換,這樣往復下去。
#include <iostream.h>
void SelectSort(int* pData,int Count)
{
int iTemp;
int iPos;
for(int i=0;i<Count-1;i++)
{
iTemp = pData;
iPos = i;
for(int j=i+1;j<Count;j++)
{
if(pData[j]<iTemp)
{
iTemp = pData[j];
iPos = j;
}
}
pData[iPos] = pData;
pData = iTemp;
}
}
void main()
{
int data[] = {10,9,8,7,6,5,4};
SelectSort(data,7);
for (int i=0;i<7;i++)
cout<<data<<" ";
cout<<"\n";
}
倒序(最糟情況)
第一輪:10,9,8,7->(iTemp=9)10,9,8,7->(iTemp=8)10,9,8,7->(iTemp=7)7,9,8,10(交換1次)
第二輪:7,9,8,10->7,9,8,10(iTemp=8)->(iTemp=8)7,8,9,10(交換1次)
第一輪:7,8,9,10->(iTemp=9)7,8,9,10(交換0次)
循環次數:6次
交換次數:2次
其他:
第一輪:8,10,7,9->(iTemp=8)8,10,7,9->(iTemp=7)8,10,7,9->(iTemp=7)7,10,8,9(交換1次)
第二輪:7,10,8,9->(iTemp=8)7,10,8,9->(iTemp=8)7,8,10,9(交換1次)
第一輪:7,8,10,9->(iTemp=9)7,8,9,10(交換1次)
循環次數:6次
交換次數:3次
遺憾的是演算法需要的循環次數依然是1/2*(n-1)*n。所以演算法復雜度為O(n*n)。
我們來看他的交換。由於每次外層循環只產生一次交換(只有一個最小值)。所以f(n)<=n
所以我們有f(n)=O(n)。所以,在數據較亂的時候,可以減少一定的交換次數。
4.插入法:
插入法較為復雜,它的基本工作原理是抽出牌,在前面的牌中尋找相應的位置插入,然後繼續下一張
#include <iostream.h>
void InsertSort(int* pData,int Count)
{
int iTemp;
int iPos;
for(int i=1;i<Count;i++)
{
iTemp = pData[i]; //保存要插入的數
iPos = i-1; //被插入的數組數字個數
while((iPos>=0) && (iTemp9,10,8,7(交換1次)(循環1次)
第二輪:9,10,8,7->8,9,10,7(交換1次)(循環2次)
第一輪:8,9,10,7->7,8,9,10(交換1次)(循環3次)
循環次數:6次
交換次數:3次
其他:
第一輪:8,10,7,9->8,10,7,9(交換0次)(循環1次)
第二輪:8,10,7,9->7,8,10,9(交換1次)(循環2次)
第一輪:7,8,10,9->7,8,9,10(交換1次)(循環1次)
循環次數:4次
交換次數:2次
上面結尾的行為分析事實上造成了一種假象,讓我們認為這種演算法是簡單演算法中最好的,其實不是,
因為其循環次數雖然並不固定,我們仍可以使用O方法。從上面的結果可以看出,循環的次數f(n)<=
1/2*n*(n-1)<=1/2*n*n。所以其復雜度仍為O(n*n)(這里說明一下,其實如果不是為了展示這些簡單
排序的不同,交換次數仍然可以這樣推導)。現在看交換,從外觀上看,交換次數是O(n)(推導類似
選擇法),但我們每次要進行與內層循環相同次數的『=』操作。正常的一次交換我們需要三次『=』
而這里顯然多了一些,所以我們浪費了時間。
最終,我個人認為,在簡單排序演算法中,選擇法是最好的。
二、高級排序演算法
高級排序演算法中我們將只介紹這一種,同時也是目前我所知道(我看過的資料中)的最快的。
它的工作看起來仍然象一個二叉樹。首先我們選擇一個中間值middle程序中我們使用數組中間值,然後
把比它小的放在左邊,大的放在右邊(具體的實現是從兩邊找,找到一對後交換)。然後對兩邊分別使
用這個過程(最容易的方法——遞歸)。
1.快速排序:
#include <iostream.h>
void run(int* pData,int left,int right)
{
int i,j;
int middle,iTemp;
i = left;
j = right;
middle = pData[left];
do{
while((pData[i]<middle) && (i<right))//從左掃描大於中值的數
i++;
while((pData[j]>middle) && (j>left))//從右掃描大於中值的數
j--;
if(i<=j)//找到了一對值
{
//交換
iTemp = pData[i];
pData[i] = pData[j];
pData[j] = iTemp;
i++;
j--;
}
}while(i<=j);//如果兩邊掃描的下標交錯,就停止(完成一次)
//當左邊部分有值(left<j),遞歸左半邊
if(left<j)
run(pData,left,j);
//當右邊部分有值(right>i),遞歸右半邊
if(right>i)
run(pData,i,right);
}
void QuickSort(int* pData,int Count)
{
run(pData,0,Count-1);
}
void main()
{
int data[] = {10,9,8,7,6,5,4};
QuickSort(data,7);
for (int i=0;i<7;i++)
cout<<data<<" ";
cout<<"\n";
}
這里我沒有給出行為的分析,因為這個很簡單,我們直接來分析演算法:首先我們考慮最理想的情況
1.數組的大小是2的冪,這樣分下去始終可以被2整除。假設為2的k次方,即k=log2(n)。
2.每次我們選擇的值剛好是中間值,這樣,數組才可以被等分。
第一層遞歸,循環n次,第二層循環2*(n/2)......
所以共有n+2(n/2)+4(n/4)+...+n*(n/n) = n+n+n+...+n=k*n=log2(n)*n
所以演算法復雜度為O(log2(n)*n)
其他的情況只會比這種情況差,最差的情況是每次選擇到的middle都是最小值或最大值,那麼他將變
成交換法(由於使用了遞歸,情況更糟)。但是你認為這種情況發生的幾率有多大??呵呵,你完全
不必擔心這個問題。實踐證明,大多數的情況,快速排序總是最好的。
如果你擔心這個問題,你可以使用堆排序,這是一種穩定的O(log2(n)*n)演算法,但是通常情況下速度要慢
㈦ 數據結構中各種排序的時間復雜度與空間復雜度比較!
冒泡排序是穩定的,演算法時間復雜度是O(n ^2)。 2.2 選擇排序(Selection Sort) 選擇排序的基本思想是對待排序的記錄序列進行n-1遍的處理,第i遍處理是將L[i..n]中最小者與L[i]交換位置。這樣,經過i遍處理之後,前i個記錄的位置已經是正確的了。 選擇排序是不穩定的,演算法復雜度是O(n ^2 )。 2.3 插入排序 (Insertion Sort) 插入排序的基本思想是,經過i-1遍處理後,L[1..i-1]己排好序。第i遍處理僅將L[i]插入L[1..i-1]的適當位置,使得L[1..i] 又是排好序的序列。要達到這個目的,我們可以用順序比較的方法。首先比較L[i]和L[i-1],如果L[i-1]≤ L[i],則L[1..i]已排好序,第i遍處理就結束了;否則交換L[i]與L[i-1]的位置,繼續比較L[i-1]和L[i-2],直到找到某一個位置j(1≤j≤i-1),使得L[j] ≤L[j+1]時為止。圖1演示了對4個元素進行插入排序的過程,共需要(a),(b),(c)三次插入。 直接插入排序是穩定的,演算法時間復雜度是O(n ^2) 。 2.4 堆排序 堆排序是一種樹形選擇排序,在排序過程中,將A[n]看成是完全二叉樹的順序存儲結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關系來選擇最小的元素。 堆排序是不穩定的,演算法時間復雜度O(nlog n)。 2.5 歸並排序 設有兩個有序(升序)序列存儲在同一數組中相鄰的位置上,不妨設為A[l..m],A[m+1..h],將它們歸並為一個有序數列,並存儲在A[l..h]。 其時間復雜度無論是在最好情況下還是在最壞情況下均是O(nlog2n)。 2.6 快速排序 快速排序是對冒泡排序的一種本質改進。它的基本思想是通過一趟掃描後,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次掃描只能確保最大數值的數移到正確位置,而待排序序列的長度可能只減少1。快速排序通過一趟掃描,就能確保某個數(以它為基準點吧)的左邊各數都比它小,右邊各數都比它大。然後又用同樣的方法處理它左右兩邊的數,直到基準點的左右只有一個元素為止。 快速排序是不穩定的,最理想情況演算法時間復雜度O(nlog2n),最壞O(n ^2)。 2.7 希爾排序 在直接插入排序演算法中,每次插入一個數,使有序序列只增加1個節點,並且對插入下一個數沒有提供任何幫助。如果比較相隔較遠距離(稱為 增量)的數,使得數移動時能跨過多個元素,則進行一次比較就可能消除多個元素交換。D.L.shell於1959年在以他名字命名的排序演算法中實現了這一思想。演算法先將要排序的一組數按某個增量d分成若干組,每組中記錄的下標相差d.對每組中全部元素進行排序,然後再用一個較小的增量對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成一組,排序完成。 希爾排序是不穩定的,其時間復雜度為O(n ^2)。 排序類別 時間復雜度 空間復雜度 穩定 1 插入排序 O(n2) 1 √ 2 希爾排序 O(n2) 1 × 3 冒泡排序 O(n2) 1 √ 4 選擇排序 O(n2) 1 × 5 快速排序 O(Nlogn) O(logn) × 6 堆排序 O(Nlogn) 1 × 7 歸並排序 O(Nlogn) O(n) √
0
順序查找, O(n) 二分, O(logn)需要排序分塊 分塊查找? 不知道..英文是什麼? 直接插入 O(n^2) 快速排序 最壞情況O(n^2) 平均O(nlogn) 起泡 和插入很像吧 O(n^2) 希爾,O(n^x) 1<x<2 需要比較復雜的分析方法選擇 沒聽過堆排序 最壞情況和平均都是O(nlogn) 其他:歸並(merge) O(nlogn) radix() (看怎麼來理解n,也可以說O(n)也可以O(nlogn),需要調用穩定的子排序演算法) basket O(n) 這兩個屬於非比較排序。 給予比較操作(> 或< )的排序演算法理論最低復雜度是O(nlogn) 證明: 所有可能情況為n! 構造決策樹需要n!子節點 <為二分操作,所以樹為二叉樹,高度為O(logn!)=O(nlogn)