平均時間復雜度為O(nlogn)的排序算法


本文包括

1.快速排序

2.歸並排序

3.堆排序

 

1.快速排序

快速排序的基本思想是:采取分而治之的思想,把大的拆分為小的,每一趟排序,把比選定值小的數字放在它的左邊,比它大的值放在右邊;重復以上步驟,直到每個區間只有一個數。此時數組已經排序完成。

快速排序最重要的是partition函數功能的實現,也就是將比選定基數小的值放在他的左邊,比選定基數大的值放在它的右邊的功能函數。

熟悉快速排序的人也許都會有自己的partition函數的寫法。此處,提供兩種不同的partition函數寫法。

例1:

public void partition(int[] a,int start,int end){
        if(start>=end)
            return ;
        int low=start;
        int high=end;
        int index = a[low];//選定基數
        while(low<high){
            while(a[high]>index && high>low)//找到右邊比基數小的數字
                high--;
            if(low<high)
                a[low++] = a[high];
            while(a[low]<index && high>low)//找到左邊比基數大的數字
                low++;
            if(low<high)
                a[high--] = a[low];
        }
        a[low] = index;
        partition(a,start,low-1);
        partition(a,low+1,end);    
    }

例2:

public void partition1(int[] a,int start,int end){
        if(start>=end)
            return ;
        int index = start;
        swap(a[index],a[end]);
        int small=start-1;
        for(index = start;index<end;index++){
            if(a[index]<a[end]){//若比選定基數小,則與前面比大於基數的數字進行交換
                small ++ ;
                if(small!=index){
                    swap(a[small],a[index]);
                }
            }
        }
        small++;
        swap(a[small],a[end]);
        partition1(a,start,small-1);
        partition1(a,small+1,end);
    }

快速排序

public void quickSort(int[] a){
        partition1(a,0,a.length-1);
}

快速排序的時間復雜度為O(nlogn),最壞時間復雜度為O(n^2),最壞的情況適之每次區間划分的結果都是基准關鍵字的最左邊或者右邊,即選擇的數字是待排序列中最小或者最大的。當n較大時使用較好。

 

2.歸並排序

歸並排序是利用遞歸與分治技術將數據序列划分為越來越小的半子表,再對半子表進行排序,最后在利用遞歸方法將排好序的半子表合並成為越來越大的有序序列。

歸並排序中的歸,就是遞歸的意思,遞歸將數組分成小的字數組。

例如數組[13,6,8,11]

先將數組分成[13,6],[8,11]兩個數組,再將數組分為[13],[6],[8],[11]。然后進行合並,[6,13],[8,11],最后合並為[6,8,11,13]。

因為歸並排序需要將數組元素分割存儲,所以空間復雜度為O(n),是以空間換時間的做法。

public void Merge(int[] a,int start,int mid,int end){
        int length1,length2; //新數組的大小
        int i,j,k;
        length1 = mid - start + 1;
        length2 = end - mid;
        int[] L = new int[length1];
        int[] R = new int[length2];
        for(i=0,k=start;i<length1;i++,k++)//將前半部分數組存入L中
            L[i] = a[k];
        for(i=0,k=mid+1;i<length2;i++,k++)//將后半部分數組存入R中
            R[i] = a[k];
        for(k=start,i=0,j=0;i<length1&&j<length2;k++){//分別從兩個數組中讀取數據,取較小的放入員數組中
            if(L[i]<R[j]){
                a[k] = L[i];
                i++;
            }
            else{
                a[k] = R[j];
                j++;
            }
        }
        if(i<length1)//將L中還有的剩余數字存入原數組
            for(j=i;j<length1;j++,k++)
                a[k] = L[j];
        if(j<length2)//將R中還有的剩余數字存入元素族
            for(i=j;i<length2;i++,k++)
                a[k] = R[i];
    }

調用歸並排序。

public void MergeSort(int[] a,int start,int end){
        if(start<end && a.length>1){
            int mid = (start+end)/2;
            MergeSort(a,start,mid);
            MergeSort(a,mid+1,end);
            Merge(a,start,mid,end);
        }
    }

歸並排序的時間復復雜度為O(nlogn),且歸並排序是穩定的排序算法,適合n較大時使用。雖然歸並排序時間復雜度較低且具有穩定性,但因為其利用了O(n)的空間存儲數據,所以使用的時候需要綜合考慮。

 

3.堆排序

堆是一種特殊的樹形數據結構,每個節點都有一個值,通常提到的堆是一棵完全二叉樹,根節點的值小於或大於兩個節點的值,同時根節點的兩個子樹也分別是兩個堆。

堆排序的思想是對於給定的n個記錄,初始時把這些記錄看作成一棵順序的二叉樹,然后將其調整為一個大頂堆,將堆的最后一個元素與堆頂的元素進行交換后,堆的最后一個元素即為最大元素;接着將前(n-1)個元素重新調整為一個大頂堆,再將堆頂元素與當前堆的最后一個元素進行交換后得到次大的記錄,重復該過程知道調整的堆中只剩下一個元素為止。此時已經得到一個有序的數組。

簡要來說,堆排序就是兩個過程:構建堆;交換堆頂元素與最后一個元素的值。

public void adjustHeap(int[] a,int pos,int len){
        int temp;
        int child;
        for(temp = a[pos];2 * pos+1<=len;pos=child){
            child = 2 * pos+1;//得到子節點的下標
            if(child<len&&a[child]<a[child+1])//得到子節點中較大的節點
                child++;
            if(a[child] > temp)//將子節點和父節點進行交換
                a[pos] = a[child];
            else
                break;
        }
        a[pos] = temp;
    }
    //堆排序
    public void HeapSort(int[] a){
        for(int i=a.length/2-1;i>=0;i--)//構建堆
            adjustHeap(a,i,a.length-1);
        for(int i=a.length-1;i>=0;i--){
            //把第一個數字和最后一個數字交換
            int temp = a[0];
            a[0] = a[i];
            a[i] = temp;
            //調整堆保證第一個數字是最大的
            adjustHeap(a,0,i-1);//調整剩下i-1個元素
        }
    }

堆排序的時間復雜度也為O(nlogn),同樣適合n較大時使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM