(学习6)特殊的分治策略算法——BFPTR

Posted pipihoudewo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(学习6)特殊的分治策略算法——BFPTR相关的知识,希望对你有一定的参考价值。

问题引出:给出一个集合N,求出其中第k小的数,K小的元素指对集合L中的元素升序排列好后第K的元素。

1:惯性思维是对该集合中的每个数进行排序,然后找到索引为k的元素,最好的情况应该是O(nlogn)

2:BFPTR算法,一个即使是最坏情况下,也能达到O(n)的算法,通过对这个算法的学习,很直观的感受到,这个算法首先将集合中的数分5个一组,然后找到每组的中位数,将中位数放入一个集合,然后再求这个集合的中位数m,然后按照该中位数对数组进行划分,左边放比m小的,右边放比m大的,然后观察m的位置与k的关系进行相应递归,按照以前学过的快速排序算法,其实这个算法改变了枢纽(pivot)的选择方式,并且能明显提高算法的效率

个人的一点理解:

将元素分为5个一组,(这样做能使后续的划分更加合理,是由五位数学家研究出来的)对每组进行排序,则在找到中位数的中位数m后进行划分时,集合大部分已经处于有序状态,此时采取快速排序的思路,对集合进行划分,即修改快速排序的主元选取规则,将中位数集合的中位数作为主元,最坏情况下的时间复杂度也是O(n)

伪代码(纯属个人理解,希望得到批评指正):

1:Find(){ //找集合N中第k小的数
      FindMid();//将集合N分为5个一组,共n/5组,将每组进行插入排序,找出每组的中位数,并将其纳入集合M。
    //找出M中的中位数Mid
      FindIndex();//在集合N找到Mid的下标
      //以Mid为界,将N中比Mid小的放到Mid左边,比Mid大的放到Mid右边,并且记录划分后Mid的下标
//若Mid的下标<k,则在Mid右边的集合找
//若Mid的下标>k,则在Mid左边的集合找
//若Mid的下标=k,则找到第k小的数
}

代码(参考了一些博主)

技术图片
//
//  main.cpp
//  作业6
//
//  Created by yizhihenpidehou on 2020/3/31.
//  Copyright © 2020 yizhihenpidehou. All rights reserved.
//
#include <iostream>
#include <stdio.h>
using namespace std;
const int maxen=100;
void InsertSort(int n[],int low,int high){ //插入排序
    int i,j;
    for(i=low+1;i<=high;i++){
        j=i-1;
        int tmp=n[i];
        while(j>=low&&tmp<n[j]){
            n[j+1]=n[j];
            j--;
        }
        n[j+1]=tmp;
    }
}
int FindMid(int n[],int low,int high){ //找出中位数
    if(low==high) return n[low];
    int i,k;
    for(i=low;i+4<=high;i+=5){
        InsertSort(n, i, i+4);
        k=i-low;
        swap(n[low+k/5],n[i+2]);
    }
    int cnt=high-i+1;
    if(cnt>0){
        InsertSort(n, i, high);
        k=i-low;
        swap(n[low+k/5],n[i+cnt/2]);
    }
    k=k/5;
    if(k==0) return n[low];
    return FindMid(n,low,low+k);
}
int FindMidIndex(int n[],int low,int high,int midd){ //找出中位数的下标
    for(int i=low;i<=high;i++){
        if(n[i]==midd){
            return i;
        }
    }
    return -1;
}
int Partition(int n[],int low,int high,int index){ //根据求出的中位数进行划分,求出划分后中位数的位置
    if(low<=high){
        int i=low,j=high;
        swap(n[index],n[low]);
        int tmp=n[low];
        while(i!=j){
            while(i<j&&n[j]>=tmp){
                j--;}
            n[i]=n[j];
            while(i<j&&n[i]<=tmp){
                i++;}
            n[j]=n[i];
        }
        n[i]=tmp;
        return i;
    }
        return -1;
}
int BFPTR(int n[],int low,int high,int k){
    int midd=FindMid(n,low,high);
    int indexx=FindMidIndex(n,low,high,midd);
    int newIndex=Partition(n,low,high,indexx);
    int rank=newIndex-low+1;
    if(rank==k) return newIndex;
    else if(rank>k) return BFPTR(n,low,newIndex-1,k);
    return BFPTR(n,newIndex+1,high,k-rank);
}
int main(int argc, const char * argv[]) {
    int num[maxen]={-1,12,1,8,10,6,2,5,9,11,3,4,7};
    int k;
    scanf("%d",&k);
    int low=1;
    int high=12;
    int index=BFPTR(num,low,high,k);
    printf("%d
",num[index]);
    for(int i=low;i<high;i++){
        printf("%d ",num[i]);
    }
    printf("
");
    return 0;
}
View Code

 

以上是关于(学习6)特殊的分治策略算法——BFPTR的主要内容,如果未能解决你的问题,请参考以下文章

五大经典算法思想之分治策略

求众数问题算法的思路(用递归与分治策略)

五大常见算法策略——递归与分治策略

数据结构与算法(12)—分治策略

如何快速正确的写出各种分治算法的实现代码

算法设计之分治法策略