利用快排partition求前N小的元素

  求前k小的數,通常人的想法就是先排序,而後再遍歷,可是題目只是求前N小,沒有必要徹底排序,因此能夠想到部分排序,而可以部分排序的排序算法我能想到的就是堆排序和快排了。算法

第一種思路,局部堆排序。

  首先,創建一個大小爲N的大頂堆,時間複雜度klgk,而後用其他的數和堆頂元素比較,若是小於堆頂元素則與堆頂元素交換,並進行一次調整,時間複雜度(n-k)lgk,而後klgk能夠常數級,(n-k)lgk=O(n)。數組

第二種思路,利用快排的partition。

  只須要稍微修改qsort函數便可,增長判斷條件,當partition小於k則快排partition+1到right,若是partition大於k則快排left到partition-1,若是partition等於k,則直接輸出數組前k個數,即所求。由於每一輪partition,左部的數都會小於基準數,右部大於基準數 ,因此若是基準下標小於k,說明第k大的數確定不在基準左部,因此能夠縮小搜索條件,直接搜索基準右部,同理基準下標大於k,說明第k大的數確定不在基準右部,直接搜索基準左部。不過須要注意的一點是qsort中的if(left<right)要改爲if(left<right),由於判斷partition==k要在下一個遞歸中。時間複雜度網上證實是O(n)。函數

public class partitionFindN {
    @Test
    public void test(){
        int[] num = {1,5,9,7,3,4,8,1,6,3,5};
        findSmallN(num,8);
    }
    
    public void findSmallN(int[] num,int n){
        Qsort(num,0,num.length-1,n-1);
    }
    
    public void Qsort(int[] num,int left,int right,int k){
        if(left<=right){
            int partition = partition(num,left,right);            
            if(partition==k){
                System.out.println("前"+(k+1)+"小的元素爲:");
                for(int i =0;i<= k;i++){
                    System.out.print(num[i]+" ");
                }
            }
            else if(partition<k){
                Qsort(num,partition+1,right,k);
            }
            else{
                Qsort(num,left,partition-1,k);
            }
        }
    }
    
    public int partition(int[] num,int left,int right){
        int partition = num[left];
        while(left<right){
            while(left<right && num[right]>=partition){
                right--;
            }
            swap(num,left,right);
            while(left<right && num[left]<=partition){
                left++;
            }
            swap(num,left,right);
        }
        return left;
    }
    
    public void swap(int[] num,int m,int n){
        int temp = num[m];
        num[m] = num[n];
        num[n] = temp;
    }
}
相關文章
相關標籤/搜索