アルゴリズムの設計と分析の線形時間選択(C ++)

線形時間選択アルゴリズム

  • クイックソートアルゴリズムの模倣
  • 配列に分割と征服戦略を使用する

例1

問題の説明

与えられたn要素配列a [0:n-1]について、そこからk番目に小さい要素を見つける必要があります

入力:複数のテストケースセットを入力します。
 テストケースごとに2つの行があります。
 最初の行は整数nおよびk(1≤k<n≤1000)で、
 2番目の行はn個の整数です。

出力:k番目に小さい要素

問題分析

ある種のクイックソートアルゴリズムのアイデアは次のとおりです:
 標準として数値を見つけ、左側に小さい数値を配置し、右側に大きい数値を配置します

k番目に小さい要素を見つけるために、最も失礼な方法はそれらすべてを並べ替えることですが、これは多くの余分な作業を行いました。クイック並べ替えアルゴリズムの1回限りの並べ替えのアイデアを利用して、配列の最初の要素を標準として使用し、それよりも小さい要素を左側に配置できます。右側にそれよりも大きく配置します。

  • この規格の左側にある要素とそれを合計して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);	//向左找
}

例2

問題の説明

  石油会社は、東から西への主要な石油パイプラインを建設することを計画しています。パイプラインは、n個の油井がある油田を通過します。各油井から、最短経路(または南または北)に沿ってメインパイプラインに接続されたオイルパイプラインが必要です。
  n個の油井の位置、つまりx座標(東西)とy座標(南北)が与えられると、プログラムは各油井からメインパイプラインまでの油パイプラインの最小長の合計を計算します。


  最初の行を入力すると、油井の数を示す整数nになります(1≤n≤10000)。
  次のn行は油井の位置であり、各行には2つの整数xとy(-10000≤x、y≤10000)が含まれます。   各油井からメインパイプラインまでのオイルパイプラインの最小長の合計を
出力
します

入力例
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);
}

このブログの他の記事で推奨されています

アルゴリズムの設計と分析のための戦略演習を分割して征服する(パート2)

アルゴリズムの設計と分析のための戦略演習を分割して征服する(上)

アルゴリズムの設計と分析の戦略を分割して征服する

再帰的アルゴリズム演習のアルゴリズム設計と分析(パート2)

再帰的アルゴリズム演習のアルゴリズム設計と分析(上)

おすすめ

転載: blog.csdn.net/L333333333/article/details/102646289