1. 程式人生 > >最壞情況為線性的選擇演算法

最壞情況為線性的選擇演算法

基本思想

主體上是在期望為線性的選擇演算法上進行改進,將其中的隨機的劃分元素改為取中位數,使劃分更均勻,從而達到最壞時時間複雜度也為線性.需要注意的是實現時裡面的索引很暈,別搞混了.我就是先寫了個很亂,然後實在改不下去了,就重寫了,總共我大概寫了5,6個小時吧.(可能我太菜了)

圖解

F36hAe.png

程式碼

虛擬碼

這裡書中未給虛擬碼,僅給了整個演算法的流程,但並不影響我們的實現
F364tH.png

C程式碼

#include <stdio.h>
#define N 50

void show(int *a, int p, int r);
int Partition(int * a, int p, int r, int x)//以值x來進行分割
{
    int k;
    int pos;
    for(k = p; k <= r; k++)//先把值x與末尾r交換位置,不太好,因為我還遍歷了陣列來找x的索引值
    {
        if(a[k] == x)
            pos = k;
    }
    int temp;
    int t = a[pos];
    a[pos] = a[r];
    a[r] = t;
    int i, j;
    i = p-1;
    for(j = p; j <= r; j++)
    {
        if(a[j] <= t)
        {
            i+=1;
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    return i;//返回劃分後的x所對應的索引
}

int Insertion_sort(int * a, int p, int r)//用來對每組元素進行插排
{
    int i, j;
    for(i = p+1; i <= r; i++)
    {
        j = i;
        while(j > p && a[j] < a[j-1])
        {
            int temp = a[j];
            a[j] = a[j-1];
            a[j-1] = temp;
            j--;
        }
    }
}

int Select(int *a, int p, int r, int i, int len)//返回第i個元素的值
{
    if(p==r)//僅一個元素時直接返回
    {
        return a[p];
    }

    int midval[N];
    int group = len%5==0 ? len/5 : len/5+1;
    if(len%5==0)//每組剛好5人
    {
        int i;
        for(i = 0; i < group; i++)
        {
            Insertion_sort(a,p+5*i,p+5*i+4);    
            midval[i] = a[p+i*5+2];
        }   
    }
    else//最後一組不滿5人
    {
        int i;
        for(i = 0; i < group-1; i++)
        {
            Insertion_sort(a,p+5*i,p+5*i+4);
            midval[i] = a[p+i*5+2];
        }
        //單獨處理最後一組
        int lastgroupsize = len%5;
        Insertion_sort(a,p+5*i,r);
        midval[i] = a[p+i*5+lastgroupsize/2];
    }
    
    int pos2 = Select(midval,0,group-1,group/2,group);//對midval[]遞迴查詢其中位數
    int q = Partition(a,p,r,pos2);//以中位數pos2來劃分元素

    int k = q-p;//劃分元素的相對位置
    if(i == k)
        return a[q];//劃分元素剛好為所查元素,返回 
    else if(i < k)
        return Select(a,p,p+k-1,i,k);//繼續處理左半邊
    else 
        return Select(a,p+k+1,r,i-k-1,r-p-k);//繼續處理右半邊
}

int main()
{
    int a[]  = {34,65,21,32,555,11,4,78,64,99,25,100,24};
    int len = sizeof(a)/sizeof(int);
    int k;
    int i;
    for(i = 0; i < len; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\ninput nth to search\n");
    scanf("%d",&k);
    int ans = Select(a,0,12,k,13);
    printf("ans %d\n", ans);
    return 0;
}
//演算法流程不難,但實現起來其中的細節很多,尤其這裡面的下標很繞人

時間複雜度

O(n)