算法設計與分析之線性時間選擇(C++)

線性時間選擇算法

  • 模仿快速排序算法
  • 對數組使用分治策略

例題一

問題描述

對於給定的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);
}

本博客其他文章推薦

算法設計與分析之分治策略練習(下)

算法設計與分析之分治策略練習(上)

算法設計與分析之分治策略

算法設計與分析之遞歸算法練習(下)

算法設計與分析之遞歸算法練習(上)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章