(学习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\n",num[index]);
    for(int i=low;i<high;i++){
        printf("%d ",num[i]);
    }
    printf("\n");
    return 0;
}

原文地址:https://www.cnblogs.com/pipihoudewo/p/12629480.html

时间: 2024-10-27 10:47:05

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

分治策略&mdash;&mdash;算法导论(3)

1. 从一个股价的问题说起     假如你获得了一种可以预测未来某公司股价的能力.下图是你预测的股价情况,那么你会在哪一天买入,哪一天卖出呢?       你可能认为可以在这17天当中的股价最低的那天(第7天)买入,然后在之后的股价最高的那天(第11天)卖出:或者反过来在整段时间内股价最高的那天卖出,然后在之前的股价最低的那天买入.如果这种策略是可行的,那么确定最大收益将非常简单.但这种策略不是经常奏效的.比如下图的这种情况: 很容易看出在第2天买入,第3天卖出,将获得最大的收益.但第二天并非最

【从零学习经典算法系列】分治策略实例——二分查找

1.二分查找算法简介 二分查找算法是一种在有序数组中查找某一特定元素的搜索算法.搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束:如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较.如果在某一步骤数组 为空,则代表找不到.这种搜索算法每一次比较都使搜索范围缩小一半.折半搜索每次把搜索区域减少一半,时间复杂度为Ο(logn). 二分查找的优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且

【从零学习经典算法系列】分治策略实例——快速排序(QuickSort)

在前面的博文(http://blog.csdn.net/jasonding1354/article/details/37736555)中介绍了作为分治策略的经典实例,即归并排序,并给出了递归形式和循环形式的c代码实例.但是归并排序有两个特点,一是在归并(即分治策略中的合并步骤)上花费的功夫较多,二是排序过程中需要使用额外的存储空间(异地排序算法<out of place sort>). 为了节省存储空间,出现了快速排序算法(原地排序in-place sort).快速排序是由东尼·霍尔所发展的一

算法导论第四章分治策略编程实践(二)

在上一篇中,通过一个求连续子数组的最大和的例子讲解,想必我们已经大概了然了分治策略和递归式的含义,可能会比较模糊,知道但不能用语言清晰地描述出来.但没关系,我相信通过这篇博文,我们会比较清楚且容易地用自己的话来描述. 通过前面两章的学习,我们已经接触了两个例子:归并排序和子数组最大和.这两个例子都用到了分治策略,通过分析,我们可以得出分治策略的思想:顾名思义,分治是将一个原始问题分解成多个子问题,而子问题的形式和原问题一样,只是规模更小而已,通过子问题的求解,原问题也就自然出来了.总结一下,大致

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

摘要:递归与分治策略是五大常见算法策略之一,分治策略的思想就是分而治之,即先将一个规模较大的大问题分解成若干个规模较小的小问题,再对这些小问题进行解决,得到的解,在将其组合起来得到最终的解.而分治与递归很多情况下都是一起结合使用的,能发挥出奇效(1+1>2),这篇文章我们将先从递归说起,再逐渐向分治过渡,主要讲解方式是通过9个例题来说明问题的,问题都是根据难度由简到难,由浅入深,对递归与分治能有个大概的了解雏形,当然最重要还是要做大量练习才能掌握. 1.递归 我们第一次接触递归一般都是在初学C语

第四章 分治策略 4.1 最大子数组问题 (暴力求解算法)

/** * 最大子数组的暴力求解算法,复杂度为o(n2) * @param n * @return */ static MaxSubarray findMaxSubarraySlower(int[] n) { long tempSum = 0; int left = 0; int right = 0; long sum = Long.MIN_VALUE; for (int i = 0; i < n.length; i++) { for (int j = i; j < n.length; j++

【经典算法】分治策略

一.什么是分治 有很多算法是递归的:为了解决一个给定的问题,算法要一次或多次递归调用其自身来解决的子问题.这些算法通常采用分治策略:将原问题划分为n个规模较小而结构与原问题相似的子问题:递归地解决这些子问题,然后再合并其结果,就得到原问题的解. 二.分治算法的三个步骤 分治模式在每一层递归上都有三个步骤: 分解(Divide)步骤将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小. 解决(Conquer)步骤递归地求解出子问题.如果子问题规模足够小,则停止递归,直接求解. 合并(Co

分治策略(2)&mdash;&mdash;算法导论(4)

1. 引言     这一篇博文首先会介绍基于分治策略的矩阵乘法的Strassen算法,然后会给出几种求解递归式的方法.   2. 矩阵乘法的Strassen算法 (1) 普通矩阵乘法算法     矩阵乘法的基本算法的计算规则是:         若A=(aij)和B=(bij)是n×n的方阵(i,j = 1,2,3...),则C = A · B中的元素Cij为:     下面给出Java实现代码: public static void main(String[] args) { int[][]

递归与分治策略(一)---算法设计与分析

递归与分治策略(一) 简而言之,递归就是自己调用自己. 递归算法:直接或者间接地调用自身的算法. 递归函数:用函数自身给出定义的函数. 注意:每个递归函数都必须有非递归定义的初始值,以确保递归函数完成计算. 下面通过两个例子来介绍递归的特点 例1 阶乘函数 阶乘函数递归地定义为: n!=1   (n=0) 或者 n!=n(n-1)!  (n>0) 下面用一段简单的Java代码实现 这里是递归实现: public static int facterial(int n) { if (n == 0)