以前寫過的一篇,搬過來。
上演算法課的時候聽到老師講這個問題,覺得還是蠻有意思的。已知陣列a,找出a[m]...a[p]中的第k大值。
很容易想到快排和冒泡。
第一種方法:用快排的分治方法,是先任意找陣列中的乙個元素a(a用陣列的第乙個元素比較方便),然後進行一次劃分,就是將陣列中所有大於a的數都移到a的一邊,所有小於等於a的數都移到a的另一邊。然後選擇在哪邊繼續進行劃分,最後找到第k大的值。
第二種方法:用冒泡的方法,是每個元素挨著比,第一趟找出最大的數,第二趟找出第2大的數,一直到找到第k大的數結束。
其實第一種方法的平均複雜度能到o(n),但是它的複雜度依賴於劃分元素,最壞的時間複雜度是o(n^2)。
如果在第一種方法之上,加上乙個篩選劃分元素的過程,就能把最壞時間複雜度降到o(n)。篩選的過程就是把所有的數等分成很多小段,然後求所有小段的中間值。構成乙個由所有中間值組成的段,然後再取中間值,作為劃分元素。即中間值的中間值作為劃分元素。取中間值可以先任選一種排序方法排序之後選擇,因為每一小段的長度很短,不是影響複雜度的主要因素;取中間值的中間值,利用遞迴的方法呼叫自身即可。
這樣就可以把最壞時間複雜度降到o(n)了,複雜度證明比較繁瑣。
用c++實現了一下:
#includeusingnamespace
std;
int r = 5; //
定義全域性變數r, r個元素一段
void insort( int a, int m, int p ) //
插入排序
a[j+1] =t;
}}void swap( int &a, int &b ) //
兩數交換
int partition( int a, int m, int p ) //
一次劃分函式
a[m] =a[j];
a[j] =x;
returnj;}
int select( int a, int m, int p, int k ) //
返回乙個i值,使得a[i]是a[m..p]中第k小元素
while( 1
) j = select( a, m, m + int(n/r) -1, int(int(n/r)/2) + 1
); swap( a[m], a[j] );
//產生劃分元素
j =partition( a, m, p );
if( j - m + 1 ==k)
return
j;
else
if( j - m + 1 >k )
p = j - 1
;
else
}}int
main()
;
int find_out = select( a, 0, 23, 7
);
inti;
for( i = 0; i <= 23; ++i )
cout
<< a[i] <<"";
cout
<< a[find_out]
}
另外:1、上面說的都是在記憶體夠用的前提下。
2、調這個程式的時候發現了乙個問題:
以前我以為下面這樣交換兩個數比較好。
void swap( int &a, int &b )
才發現如果a和b表示同乙個位址的時候,就是錯的(不管是什麼都變成0了)。
所以如果可能出現a、b是同乙個位址上的數的時候,為了避免有隱藏的bug,還是下面這樣保險。(雖然多了乙個臨時變數的空間)
void swap( int &a, int &b )
找第k大數,最壞時間複雜度O n
第一種方法 用快排的分治方法,是先任意找陣列中的乙個元素a a用陣列的第乙個元素比較方便 然後進行一次劃分,就是將陣列中所有大於a的數都移到a的一邊,所有小於等於a的數都移到a的另一邊。然後選擇在哪邊繼續進行劃分,最後找到第k大的值。第二種方法 用冒泡的方法,是每個元素挨著比,第一趟找出最大的數,第...
最好,最壞和平均時間複雜度
在查詢成功的情況下,若待查詢的資料元素恰好是陣列的第乙個元素,則只需比較一次即可找到,這就是最好情況,t n o 1 稱最好時間複雜度。若是最後乙個元素,則要比較n次才能找到。t n o n 稱最壞時間複雜度。在查詢不成功的情況下,無論何時進行不成功的查詢都需要進行n次比較,t n o n 成功查詢...
時間複雜度分析 最好 最壞 均攤
array 陣列.n array長度.x 需要查詢的值 int searchposition int array,int n,int x return pos 根據上面的演算法來說的話,如果我x的位置在array 0 那麼時間複雜度就是o 1 如果x的位置在array n 1 或者不在array中....