線性時間選擇算法
- 模仿快速排序算法
- 對數組使用分治策略
例題一
問題描述
對於給定的n個元素的數組a[0:n—1],要求從中找出第k小的元素
輸入:輸入有多組測試例。
對每一個測試例有2行
第一行是整數n和k(1≤k<n≤1000)
第二行是n個整數
輸出:第k小的元素
問題分析
我們知道,快速排序算法的一次排序的思想是:
找到一個數字作爲標準,把比該數小的放左邊,比該數大的放右邊
要找到第k小的元素,最粗暴的就是全部排序,但這樣做了很多多餘的工作,借鑑快速排序算法的一次排序思想,我們可以以數組首位元素作爲一個標準,把小於它的放左邊,大於它的放右邊:
- 當這個標準左邊的元素和它加起來爲k的話,就找到第k小的數了
- 當這個標準左邊的元素和它加起來小於k的話,就向右邊繼續找第(k-1-該數下標)小的數
- 當這個標準左邊的元素和它加起來大於k的話,就向左邊繼續找第k小的數
算法實現
#include <iostream>
#include <algorithm>
#define N 100
using namespace std;
//一維數組容器
int a[N];
//線性選擇算法尋找第k小的元素
int linearTimeSelection(int,int,int);
int main()
{
int n,k;
cout<<"輸入數組大小:";
cin>>n;
if(n>N || n<1) {
cout<<"預留空間不足或數組大小非法!";
exit(0);
}
cout<<"輸入數組元素:";
for(int i=0;i<n;i++) cin>>a[i];
cout<<"查找第幾小的元素:";
cin>>k;
if(k > n || k < 1){
cout<<"查找位置非法!";
exit(0);
}
cout<<linearTimeSelection(0,n-1,k);
return 0;
}
/*
left 進行線性選擇的首位下標
right 進行線性選擇的末尾下標
k 尋找第k位小的元素
*/
int linearTimeSelection(int left,int right,int k){
if(left >= right) return a[left];
int point = a[left];
int i = left,
j = right+1;
while(1){
do{i++;}while(a[i] < point);
do{j--;}while(a[j] > point);
if(i>=j) break;
swap(a[i],a[j]);
}
if(j-left+1 == k) return point;
a[left] = a[j];
a[j] = point;
if(j-left+1 < k) return linearTimeSelection(j+1,right,k-(j+1-left)); //向右找
return linearTimeSelection(left,j-1,k); //向左找
}
例題二
問題描述
某石油公司計劃建造一條由東向西的主輸油管道。該管道要穿過一個有n口油井的油田。從每口油井都要有一條輸油管道沿最短路經(或南或北)與主管道相連。
如果給定n口油井的位置,即它們的x座標(東西向)和y座標(南北向),編程計算各油井到主管道之間的輸油管道最小長度總和。
輸入
第1行是一個整數n,表示油井的數量(1≤n≤10 000)
接下來n行是油井的位置,每行兩個整數x和y(-10 000≤x,y≤10 000)
輸出
各油井到主管道之間的輸油管道最小長度總和
輸入樣例
5
1 2
2 2
1 3
3 -2
3 3
輸出樣例
6
樣例圖
問題分析
- 如何確定主輸油管道位置?
由上面我對輸入樣例畫出的分析圖,可以清晰的看到這個主管道只由y座標確定位置,而且主管道存在於y值的值域內(即所輸入的y座標的最大最小值之間),因爲要找油井到主管道間的距離和最小,所以找到油井位置的y值的中位數即可。 - 如何計算最小距離和?
找到主管道位置後,我們對每個油井都計算到主管道的距離,加起來即可 - 關於中位數求法
1、直接排序,找n/2下標的元素;
2、使用線性時間選擇算法選擇第n/2小的元素;(我們使用這種方法)
算法實現
#include <iostream>
#include <algorithm>
#define N 10000
using namespace std;
//存儲油井的y值
int a[N];
//線性時間選擇算法
int linearTimeSelection(int,int,int);
int main()
{
int n,temp;
cout<<"請輸入油井的數量:";
cin>>n;
cout<<"請輸入油井座標:";
for(int i=0;i<n;i++)
cin>>temp>>a[i];
//主管道位置
int mid = linearTimeSelection(0,n-1,n/2);
int sum = 0;
for(int i=0;i<n;i++){
sum += abs(a[i]-mid);
}
cout<<sum<<endl;
return 0;
}
int linearTimeSelection(int left,int right,int k){
if(left >= right) return a[left];
int i = left,
j = right+1;
int point = a[left];
while(1){
do{i++;}while(a[i]<point);
do{j--;}while(a[j]>point);
if(i>=j) break;
swap(a[i],a[j]);
}
if(j-left+1 == k) return point;
a[left] = a[j];
a[j] = point;
if(j-left+1 < k) return linearTimeSelection(j+1,right,k-(j-left+1));
return linearTimeSelection(left,j-1,k);
}