之前參加地特考試,程式考題中有一題探討第k大數字的問題。

當時腦中馬上浮現選擇排序法(Selection Sort),但是題目想要有O(n)效率。

所以這故事告訴我們,選擇排序法的O(n^2)效率其實不大好,希望能改善。((瞬間想翻桌

因此自己寫出兩個方法,使用同樣的陣列,以最後一個為max,每過一回都減少陣列範圍。

 

以下參考書為:2015資料結構 by 王致強


結果圖:

第k大數字好神.jpg

 

程式碼:

import java.util.Scanner;

public class Selection
{        
    String SelectionK(int s[],int k)
    {
        //題目要求O(n)的時間複雜度
        int max=0,count=0,temp;
        String ans;
        if(s.length<k){return "k值過大!";}
        else if(k==0){return "k值不能零!";}        
        else 
        {
            for (int i=1;i<=k;i++)
            {    
                max=s[s.length-i];//隨i值增加,max重新取得縮小的陣列範圍最後一個
                for (int j=0;j<s.length-i;j++)//隨i值增加,j的最大值會減少,也就是說比的陣列範圍會縮小
                {
                    if(max<s[j]){temp=s[j];s[j]=max;max=temp;}//有比max大就交換
                    count+=1;//計算效率
                }
                s[s.length-i]=max;//每比出的最大值存入最後一個
                //System.out.println("第"+i+"大為"+max);//顯示過程
                
            }
            System.out.println("第k大數字效率為"+count);//顯示效率
            //s陣列存入數與k影響效率為
            //n=1 效率0, n=2 效率1~1, n=3 效率2~3, n=4 效率3~6, n=5 效率4~10        
            ans="第"+k+"大為"+max;
            return ans;
        }
    }    
    void SelectionSort(int a[])
    {
        //Selection Sort的時間複雜度
        int max=0,count=0,temp;
        for (int i=a.length-1;i>0;i--)
        {                
            max=a[i];//隨i值減少,max重新取得縮小的陣列範圍最後一個
            for (int j=0;j<i;j++)//隨i值減少,j的最大值會減少,也就是說比的陣列範圍會縮小
            {
                if(max<a[j]){temp=a[j];a[j]=max;max=temp;}//有比max大就交換
                count+=1;//計算效率        
            }
            a[i]=max;//每比出的最大值存入最後一個
        }
        System.out.println("選擇排序法效率為"+count);//顯示效率
        //a陣列存入數影響效率為
        //n=1 效率0, n=2 效率1, n=3 效率3, n=4 效率6, n=5 效率10
    }
   public static void main(String args[])
   {    
        int s[] = {5,4,3,2,1};//第k大數字的陣列
        int a[] = {5,4,3,2,1};//選擇排序法的陣列
        System.out.print("第k大數字陣列:");
        for (int i=0;i<s.length;i++){System.out.print(s[i]+" ");}//顯示原本陣列
        System.out.println();
        System.out.print("選擇排序法陣列:");
        for (int j=0;j<a.length;j++){System.out.print(a[j]+" ");}//顯示原本陣列
        System.out.println();
        System.out.print("請輸入k值:");
        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        System.out.println("輸入k值:"+k); 
        Selection text = new Selection();
        System.out.println(text.SelectionK(s,k));
        text.SelectionSort(a);
        System.out.print("第k大數字陣列:"); 
        for (int x=0;x<s.length;x++){System.out.print(s[x]+" ");}//顯示重整陣列
        System.out.println();
        System.out.print("選擇排序法陣列:"); 
        for (int y=0;y<a.length;y++){System.out.print(a[y]+" ");}//顯示重整陣列
   }
}

 

SelectionK簡單流程如下:

如果k不等於0且k不大於陣列長度,就能進入雙迴圈。

第一個迴圈範圍為1~k,設max為s[陣列長-第一個迴圈值],接著再入第二個迴圈。

第二個迴圈範圍為0~(陣列長-第一個迴圈值-1),如果max小於s[j]就交換,count加一次,等第二個迴圈做完s[陣列長-第一個迴圈值]設為max。

執行完雙迴圈,最後顯示效率與第k大數字。

以n=5,k=1效率為  4(i=1)=4次 

以n=5,k=5效率為  4(i=1)+3(i=2)+2(i=3)+1(i=4)=10次

 

SelectionSort簡單流程如下:

直接進入雙迴圈,不需要管k值。

第一個迴圈範圍為(陣列長-1)~1,設max為a[i],接著再入第二個迴圈。

第二個迴圈範圍為0~(第一個迴圈值-1),如果max小於a[j]就交換,count加一次,等第二個迴圈做完a[i]設為max。

執行完雙迴圈,最後顯示效率。

以n=5效率為  4(i=4)+3(i=3)+2(i=2)+1(i=1)=10次

 

結論:

雖然兩者在最糟情況都10次,不過第k大數字最佳只有n-1次,運作原理跟"找出最大值"同樣的時間複雜度。

而且當k=陣列長,剛好把數字從小排到大,運作原理跟"選擇排序法"同樣的時間複雜度。

第k大數字能接受使用者點菜(啥)、顯示第k大、時間複雜度n-1~n^2、從小排到大等等這麼多好處,我們乾脆忘了選擇排序法。((遭毆

其實題目要用c++寫,但個人私心用java寫。

arrow
arrow
    全站熱搜

    o迷苓o 發表在 痞客邦 留言(0) 人氣()