(轉載請注明出處,http://www.cnblogs.com/fangpei/p/3538331.html )
以前寫過的一篇,搬過來。
上算法課的時候聽到老師講這個問題,覺得還是蠻有意思的。已知數組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++實現了一下:
#include<iostream> using namespace std; int r = 5; //定義全局變量r, r個元素一段 void InSort( int A[], int m, int p ) //插入排序 { int i; for( i = m + 1; i <= p; ++i ) { int t; t = A[i]; int j; for( j = i - 1; j >= m; --j ) { if( t < A[j] ) A[j+1] = A[j]; else break; } A[j+1] = t; } } void Swap( int &a, int &b ) //兩數交換 { int temp = 0; temp = a; a = b; b = temp; } int Partition( int A[], int m, int p ) //一次划分函數 { int i = m, j = p + 1; int x = A[m]; while( 1 ) { while( A[++i] > x ); while( A[--j] < x ); if( i >= j) break; Swap( A[i], A[j] ); } A[m] = A[j]; A[j] = x; return j; } int Select( int A[], int m, int p, int k ) //返回一個i值,使得A[i]是A[m..p]中第k小元素 { int n = 0, i = 1, j = 0; if( p - m + 1 <= r ) { InSort( A, m, p ); return m + k - 1; } while( 1 ) { n = p - m + 1; for ( i = 1; i <= int(n/r); ++i ) { //計算中間值 InSort( A, m + (i - 1) * r, m + i * r - 1 ); //將中間值收集到A[m..p]的前部 Swap( A[m+i-1], A[m+(i-1)*r+int(r/2)] ); } 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 { k = k - ( j - m + 1 ); m = j + 1; } } } int main() { int A[24] = { 1, 3, 6, 33, 4, 1, 5, 2, 9, 8, 50, 22, 2, 23, 22, 45, 7, 18, 20, 40, 36, 22, 23, 10}; int find_out = Select( A, 0, 23, 7 ); int i; for( i = 0; i <= 23; ++i ) cout << A[i] <<" "; cout << endl; cout << A[find_out] << endl; return 0; }
另外:
1、上面說的都是在內存夠用的前提下。
2、調這個程序的時候發現了一個問題:
void Swap( int &a, int &b ) { a = a ^ b; b = a ^ b; a = a ^ b; }
才發現如果a和b表示同一個地址的時候,就是錯的(不管是什么都變成0了)。
void Swap( int &a, int &b ) { int temp = 0; temp = a; a = b; b = temp; }