1. 程式人生 > >各個排序演算法應用:求取陣列中第K大的數( LeetCode 215. Kth Largest Element in an Array )

各個排序演算法應用:求取陣列中第K大的數( LeetCode 215. Kth Largest Element in an Array )

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

Example 1:

Input: [3,2,1,5,6,4] and k = 2
Output: 5

Example 2:

Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4

Note: 

You may assume k is always valid, 1 ≤ k ≤ array's length.

以下為各個演算法相應的程式碼

int findKthLargest(int* nums, int numsSize, int k) {
    int i,j;
    int t;
    int max;
    
       /*
1、冒泡法********************************************************************************************
    //通過從左到右不斷交換相鄰逆序的相鄰元素,在一輪的交換之後,可以讓未排序的元素上浮到最右側,是的右側是已排序的。在一輪交換中,如果沒有發生交換,就說明陣列已經是有序的,此時可以直接退出。


    for(i=0;i<numsSize-1;i++)
        for(j=1;j<numsSize-i;j++)
        {
            if(nums[j]>nums[j-1]){
                int t=nums[j];
                nums[j]=nums[j-1];
                nums[j-1]=t;
            }
        }
    return nums[k-1];
    
    */  
    
    
    
       /*
2、插入排序******************************************************************************************
    //插入排序從左到右進行,每次都將當前元素插入到左側已經排序的陣列中,使得插入之後左部陣列依然有序。第 j 元素是通過不斷向左比較並交換來實現插入過程:當第 j 元素小於第 j - 1 元素,就將它們的位置交換,然後令 j 指標向左移動一個位置,不斷進行以上操作。

    for(i=0;i<numsSize-1;i++){


        for(j=i+1;j>0;j--){
            if(nums[j]>nums[j-1]){
                t=nums[j];
                nums[j]=nums[j-1];
                nums[j-1]=t;
            }
            else{
                break;
            }  
        }
    }
    return nums[k-1];
    */ 
    
    
         /*
3、選擇排序,每次選擇最大的排在第k個位置****************************************************************
    //選擇出陣列中的最小元素,將它與陣列的第一個元素交換位置。再從剩下的元素中選擇出最小的元素,將它與陣列的第二個元素交換位置。不斷進行這樣的操作,直到將整個陣列排序。
    for(i=0;i<k;i++)  
    {
        t=i;
        max=nums[i];
        for(j=i+1;j<numsSize;j++){
            
            if(nums[j]>max){
                t=j;
                max=nums[j];
            }
        }
        int a=nums[i];
        nums[i]=max;
        nums[t]=a;    
    }
    return nums[k-1];
    */
    
        /*
4、歸併排序*****************************************************************************************

    if(numsSize==1)
        return nums[0];
    
    sort_2(nums,0,numsSize-1);
    
    return nums[k-1];
    */
    
    
    /*
5、希爾排序*****************************************************************************************
//對於大規模的陣列,插入排序很慢,因為它只能交換相鄰的元素,每次只能將逆序數量減少 1。
    //希爾排序的出現就是為了改進插入排序的這種侷限性,它通過交換不相鄰的元素,每次可以將逆序數量減少大於 1。
    //希爾排序使用插入排序對間隔 h 的序列進行排序。通過不斷減小 h,最後令 h=1,就可以使得整個陣列是有序的。
    int gap;  //跳躍的步數
    for(gap=numsSize/2;gap>=1;gap=gap/2){
        
        for(i=0;i<numsSize-gap;i++){
            
          
            for(j=i+gap;j-gap>=0;j=j-gap){   //對gap內的陣列進行插入排序
                
                if(nums[j]>nums[j-gap]){
                    
                    int aaa=nums[j];
                    nums[j]=nums[j-gap];
                    nums[j-gap]=aaa;
                }
                
                
            }
            
        } 
    }
    return nums[k-1];
    */
    
    
    
    
    
    /*
6、快速排序**************************************************************************************
    sort(nums,0,numsSize-1);
    return nums[k-1];
    */
    
    
    
    /*
7、基於切分的快速選擇排序************************************************************************
     //時間複雜度O(n)
    //該演算法是線性級別的,因為每次正好將陣列二分,那麼比較的總次數為 (N+N/2+N/4+..),直到找到第 k 個元素,這個和顯然小於 2N。
    int low=0;
    int high=numsSize-1;
    
    while(high>low){
        
        int q;
        q=splite_num(nums,low,high); //獲得切分的下標,並排好序
        if(q==k-1){
            break;
        }
        if(q>k-1){
            high=q-1;
        }
        else{
            low=q+1;
        }
        
    }
    return nums[k-1];
    */
    
    
    
    /*
8、堆排序********************************************************************************************
    // 參考 https://www.cnblogs.com/chengxiao/p/6129630.html
    //1.構建大頂堆
    for(int i=numsSize/2-1;i>=0;i--){
            //從第一個非葉子結點從下至上,從右至左調整結構
            adjustHeap(nums,i,numsSize);
    }
    //2.調整堆結構+交換堆頂元素與末尾元素
    for(int j=numsSize-1;j>0;j--){
        
            swap(nums,0,j);//將堆頂元素與末尾元素進行交換
            
            adjustHeap(nums,0,j);//重新對堆進行調整
    }
    return nums[numsSize-k];
    */ 
    
    //********************************************************************************************
    
    
}




#################################################################################################

void adjustHeap(int *arr,int i,int length){   //1.構建大頂堆 
        int temp = arr[i];//先取出當前元素i
        for(int k=i*2+1;k<length;k=k*2+1){//從i結點的左子結點開始,也就是2i+1處開始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子結點小於右子結點,k指向右子結點
                k++;
            }
            if(arr[k] >temp){//如果子節點大於父節點,將子節點值賦給父節點(不用進行交換)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;//將temp值放到最終的位置
}
    /**
     * 交換元素
     * @param arr
     * @param a
     * @param b
     */
void swap(int *arr,int a ,int b){  //交換元素
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
}




###############################################################################################

void  sort(int *nums,int low,int high){     //快速排序
    
    //•歸併排序將陣列分為兩個子陣列分別排序,並將有序的子陣列歸併使得整個陣列排序;
//•快速排序通過一個切分元素將陣列分為兩個子陣列,左子陣列小於等於切分元素,右子陣列大於等於切分元素,將這兩個子陣列排序也就將整個陣列排序了。


    
    if(high<=low)
        return;
    int q;
    q=splite_num(nums,low,high); //獲得切分的下標,並排好序
    sort(nums,low,q);
    sort(nums,q+1,high);
}

int splite_num(int *nums,int low,int high){          //獲得切分的下標,並排好序
    //取 a[low] 作為切分元素,然後從陣列的左端向右掃描直到找到第一個大於等於它的元素,再從陣列的右端向左掃描找到第一個小於等於它的元素,交換這兩個元素,並不斷進行這個過程,就可以保證左指標 i 的左側元素都不大於切分元素,右指標 j 的右側元素都不小於切分元素。當兩個指標相遇時,將切分元素 a[low] 和 a[j] 交換位置。
    
    int a=nums[low];
    int i=low+1;
    int j=high;
    while(1){
        
        while(i<high && nums[i]>=a)   //搜尋nums[i]<a,當 nums[i]==a的時候,可以分配到另一個數組裡,保證公平分配
        {
            i++;
        }
        
        while(j>low && nums[j]<=a)   //搜尋nums[j]>a,當 nums[i]==a的時候,可以分配到另一個數組裡,保證公平分配
        {
            j--;
        }
        if(i>=j){
            break;
        }
        else{
            
            int ttt=nums[i];
            nums[i]=nums[j];
            nums[j]=ttt;
            i++;
            j--;
            
        }
       
        
    }
    int ttt=nums[low];
    nums[low]=nums[j];
    nums[j]=ttt;
    return j;
    
}


##############################################################################################
int sort_2(int *nums,int low,int high){  //歸併排序
    // //歸併排序的思想是將陣列分成兩部分,分別進行排序,然後歸併起來。自頂向下歸併排序
    if(high<=low)
        return;
    int middle=low+(high-low)/2;
    sort_2(nums,low,middle);
    sort_2(nums,middle+1,high);
    merge(nums,low,middle,high);                     //合併兩個降序的陣列
}

void merge(int *nums,int low,int middle,int high){   //合併兩個降序的陣列
    
    int i,j;
    i=low;
    j=middle+1;
    int k=0;
    int b[high-low+1];
    
    while((i<=middle)&&(j<=high)&&(k<high-low+1)){
        
        if(nums[i]<nums[j]){
            
            b[k]=nums[j];
            j++;
            
        }
        else{
            b[k]=nums[i];
            i++;
        }
        k++;
        
    }
    
    if(i>middle && j<=high){
        for(;j<=high;j++){
            b[k]=nums[j];
            k++;
        }
        
    }
    if(j>high && i<=middle){
        for(;i<=middle;i++){
            b[k]=nums[i];
            k++;
        }
        
    }
    for(i=low;i<=high;i++)
        nums[i]=b[i-low]; 
}

#############################################################################################