zoukankan      html  css  js  c++  java
  • 最坏情况为线性时间的选择算法

    题目

    在n个元素的无序数组中选择第k(1<=k<=n)小元素。
    当k=1时,相当于找最小值。
    当k=n时,相当于找最大值。
    当k=n/2时,称中值。
    【要求】
    线性时间内完成,即O(n)。

    算法解析

    通过执行下列步骤,算法SELECT可以确定一个有n>1个不同元素的输入数组中第i小的元素。(如果n=1,则SELECT只返回它的唯一输入数值作为第i小的元素。)
    1、将输入数组的n个元素划分为⌈n/5⌉组,每组5个元素,且至多有一个组由剩下的n mod 5个元素组成。
    2、寻找这⌈n/5⌉组中每一组的中位数:首先对每组元素进行插入排序(我这里用的是改进版的冒泡排序),然后确定有序元素的中位数。
    3、对第2步中找出的⌈n/5⌉个中位数,递归调用SELECT找出其中位数x。(如果有偶数个中位数,为了方便,约定x是较小的中位数)。
    4、利用修改过的PARTITION过程,按中位数的中位数x对输入数组进行划分。让k比划分低区的元素多1,因此x是第k小的元素,并且有n-k个元素在划分的高区。
    5、如果i=k,则返回x,否则,如果i<k,则在低区递归调用SELECT以找出第i小的元素,如果i>k,则在高区递归查找第(i-k)小的元素。

    核心代码

    template<class Type>
    Type select (Type a[],int p, int r, int k){
        if (r-p<75) {
        //对数组a[p:r]排序;
        bubbleSort(a,p,r);
        return a[p+k-1];
        }
        
        //找中位数的中位数,r-p-4即上面所说的n-5
        for ( int i = 0; i<=(r-p-4)/5; i++ ){
            int s=p+5*i,t=s+4;
            //冒泡操作保证第三个元素为中位数 
            for (int j=0;j<3;j++) bubble(a,s,t-j);
            //将a[p+5*i]至a[p+5*i+4]的第3小元素(即中位数)与a[p+i]交换位置;
            swap(a, p+i, s+2);
        }
        
        //获取中位数的中位数 
        Type x = select(a,p, p+(r-p-4)/5, (r-p-4)/10);
        //根据x划分序列 ,i中位数的中位数所在下标 
        int i=partition(a,p,r,x);
        //j是中位数的中位数的序数 
        int j=i-p+1;
        
        if (k<=j) return select(a,p,i,k);
        else return select(a,i+1,r,k-j);
    }

    完整代码

    #include <iostream>
    
    using namespace std;
    
    
    //交换函数 
    template<class Type>
    void swap(Type a[],int p,int r){
        Type temp=a[p];
        a[p]=a[r];
        a[r]=temp;
    }
    
    //改进版冒泡排序 
    template<class Type>
    void bubbleSort(Type a[],int p,int r){
        int lastSwapPos = 0,lastSwapPosTemp = 0;
        for (int i = p; i < r; i++)  {  
            lastSwapPos = lastSwapPosTemp;
            for (int j = r; j >lastSwapPos; j--)  {
                if (a[j - 1] > a[j]){
                    swap(a,j-1,j);
      
                    lastSwapPosTemp = j;  
                }  
            }
            
            if (lastSwapPos == lastSwapPosTemp)  
                break;  
        }
    }
    
    //冒泡操作 
    template <class T>
    void bubble(T a[],int p, int r){
        for(int i=p;i<r;i++){
            if(a[i]>a[r])
            swap(a,i,r);
        }
    }
    
    //划分函数 
    template <class T>
    int partition (T a[], const int p, const int r,T x) {
        int pivotpos = p;
        for (int i = p; i <= r; i++)
        if (a[i] < x) {
            pivotpos++;
            if (pivotpos != i){
                swap(a,pivotpos,i);
            }
        }
        return pivotpos;
    }
    
    
    
    template<class Type>
    Type select (Type a[],int p, int r, int k){
        if (r-p<75) {
        //对数组a[p:r]排序;
        bubbleSort(a,p,r);
        return a[p+k-1];
        }
        
        //找中位数的中位数,r-p-4即上面所说的n-5
        for ( int i = 0; i<=(r-p-4)/5; i++ ){
            int s=p+5*i,t=s+4;
            //冒泡操作保证第三个元素为中位数 
            for (int j=0;j<3;j++) bubble(a,s,t-j);
            //将a[p+5*i]至a[p+5*i+4]的第3小元素(即中位数)与a[p+i]交换位置;
            swap(a, p+i, s+2);
        }
        
        //获取中位数的中位数 
        Type x = select(a,p, p+(r-p-4)/5, (r-p-4)/10);
        //根据x划分序列 ,i中位数的中位数所在下标 
        int i=partition(a,p,r,x);
        //j是中位数的中位数的序数 
        int j=i-p+1;
        
        if (k<=j) return select(a,p,i,k);
        else return select(a,i+1,r,k-j);
    }
    
    
    int main() {
        
        //int a[]={0,6,5,1};
        //int a[]={1,2,3,4,5,6,7,8,9,10};
        int a[]={1,29,3,4,5,9,7,855,97,106};
        cout<<select(a,0,9,8);
    
        return 0;
    }

    时间复杂度

  • 相关阅读:
    Paths on a Grid
    Three Kingdoms(优先队列+bfs)
    Factstone Benchmark(数学)
    C. Searching for Graph(cf)
    B. Trees in a Row(cf)
    String Successor(模拟)
    乘积最大的分解(数学)
    Kindergarten Election
    In 7-bit
    Friends
  • 原文地址:https://www.cnblogs.com/wxgblog/p/xianxingshijianxuanzhe.html
Copyright © 2011-2022 走看看