알고리즘 설계 및 분석의 선형 시간 선택 (C ++)

선형 시간 선택 알고리즘

  • 빠른 정렬 알고리즘 모방
  • 어레이에서 분할 및 정복 전략 사용

예 1

문제 설명

주어진 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);	//向左找
}

예 2

문제 설명

  한 석유 회사는 동쪽에서 서쪽으로 주요 송유관을 건설 할 계획입니다. 파이프 라인은 n 개의 유정이있는 유전을 통과합니다. 각 유정에는 최단 경로 (또는 남쪽 또는 북쪽)를 따라 주 파이프 라인에 연결된 송유관이 있어야합니다.
  n 개의 유정의 위치, 즉 x 좌표 (동서)와 y 좌표 (북-남)가 주어지면 프로그램은 각 유정에서 주 파이프 라인까지의 최소 송유관 길이의 합계를 계산합니다.

입력
  첫번째 라인의 정수, 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);
}

이 블로그의 다른 기사에서 추천

알고리즘 설계 및 분석을위한 전략 연습 분할 및 정복 (2 부)

알고리즘 설계 및 분석을위한 전략 연습 분할 및 정복 (on)

알고리즘 설계 및 분석을위한 전략 분할 및 정복

재귀 알고리즘 연습의 알고리즘 설계 및 분석 (2 부)

재귀 알고리즘 연습의 알고리즘 설계 및 분석 (on)

추천

출처blog.csdn.net/L333333333/article/details/102646289