問題描述:給定線性序集中n個元素和乙個整數k,1<=k<=n.要求找出這n個元素中第k小的元素,即如果將這個n個元素依其線性序排列時,排在第k個位置的元素就是要找的元素,當k== 1時,要找的就是最小的元素;當k==n,就是最大的元素;當k=(n+1)/2,稱為中位數。
問題分析:
在某些特殊的情況下,我們可以實現線性時間選擇,對於找最大最小的元素o(n)內可以實現;當k<=n/logn,通過堆排序演算法可以在o(n+klogn)=o(n)內實現;當k>=n-n/logn時也一樣。
下面是給出的一般的選擇問題,從漸近階的意義上看,這個也可以在o(n)時間內完成。
下面的演算法實現參考了《計算機演算法與分析》和一些部落格,是對其的乙個整理。
方法一:
演算法描述:用乙個隨機的序列中的數作為樞紐,用快速排序演算法,進行一次快排,然後將樞紐值和k值進行比較,以此來確定k值,我並沒有做任何的對比所以並不是清楚這種演算法的效率有多少,但是搜到的結果表明,這種演算法的最壞時間複雜度是o(n^2),相對與另一種是不太理想的。
解法:1.首先大家都會想到的解法是排序,之後找出第k個元素,但是排序的時間複雜度不符合要求,或者需要額外的空間。
2.利用快排的思想,以樞紐(隨機得到)為界,將陣列分為2部分,一部分小於等於這個樞紐值,一部分大於這個樞紐值,與快排不同的是,我們只處理一部分,另一部分捨棄。
#
include
using
namespace std;
intpartition
(vector<
int>
&vec,
int left,
int right)
while
(vec[i]
<=temp&&i
swap
(vec[i]
,vec[j]);
} vec[left]
=vec[j]
; vec[j]
=temp;
return j;
}int
quicksort
(vector<
int>
&vec,
int left,
int right,
int k)
void
print
(int x)
intmain()
;for_each
(vec.
begin()
,vec.
end(
),print)
; cout<
int k;
cout<<
"請輸入k(要求找出這n個元素中第k小的元素):"
方法二:
這裡我們就利用中位數來進行線性時間的選擇演算法!
中位數就是指將資料按大小順序排列起來,形成乙個數列,居於數列中間位置的那個資料就是中位數。
演算法思路
(1)將輸入的n個數劃分成 ⌈n5⌉⌈n5⌉ 個組,當然最後一組的數目可能是小於5的!
(2)用任意的排序方法對他們進行排序,並取出一共 ⌈n5⌉⌈n5⌉ 個中位數。
(3)找出該 ⌈n5⌉⌈n5⌉ 個中位數中的中位數。(如果 ⌈n5⌉⌈n5⌉ 是偶數則取相對大的那個數)
(4)將全部的數劃分為兩個部分,小於基準的在左邊,大於等於基準的放右邊。
我們用小圓點表示元素,得到如下圖:
說明:圖中中間白色圈表示各組資料的中位數,最中間灰色表示中位數的中位數! 箭頭是從較小的數指向較大的數!
故我們可以使用該數作為劃分的基準(比上乙個隨機基準的方法會好很多)!
圖中
當n≥75時,3a1大於等於 14n14n。所以按此基準劃分所得的左右2個子陣列的長度都至少縮短 1414。
#
include
using
namespace std;
intpartition
(vector<
int>
&vec,
int left,
int right,
int k)
while
(vec[i]
<=temp&&i
swap
(vec[i]
,vec[j]);
} vec[k]
=vec[j]
; vec[j]
=temp;
return j;
}void
print
(int x)
intselect
(vector<
int>
&vec,
int left,
int right,
int k)
for(
int i=
0;i<=
(right-left-4)
/5;i++
)int x=
select
(vec,left,left+
(right-left-4)
/5,(right-left-4)
/10);
int i=
partition
(vec,left,right,x)
;int j=i-left+1;
if(k<=j)
return
select
(vec,left,i,k)
;else
return
select
(vec,i+
1,right,k-j);}
intmain()
;for_each
(vec.
begin()
,vec.
end(
),print)
; cout<
int k;
cout<<
"請輸入k(要求找出這n個元素中第k小的元素):"
演算法分析與設計 遞迴與分治策略
直接或間接地呼叫自身的演算法稱為遞迴演算法。用函式自身給出定義的函式稱為遞迴函式。在計算機演算法設計與分析中,使用遞迴技術往往使函式的定義和演算法的描述簡潔且易於理解。例1 階乘函式 可遞迴地定義為 其中 n 0 時,n 1為邊界條件 n 0 時,n n n 1 為遞迴方程 邊界條件與遞迴方程是遞迴...
遞迴演算法與分治策略
關於遞迴的學習 1 遞迴演算法的基本思想是 把規模大的 較難解決的問題變成規模較小的的問題。規模較小的問題又變成規模更小的問題,並且小到一定程度可以直接得出它的解,從而得到原來問題的解。遞迴是一種直接或間接呼叫自身的函式的一種演算法,很常用,一般用於解決三類問題 資料的定義按遞迴定義的。fibona...
遞迴與分治策略
1 全排列問題 設r n 是要進行排列的n個元素。集合x中元素的全排列記為perm x 求r n 的全排列perm r n 用遞迴演算法求解 1 找出遞迴子結構性質 即原問題的解包含了子問題的解,且子問題的描述與原問題相同。這就可以用子問題的解來構造原問題的解。設r i r n 這是乙個子問題。設 ...