幾種常見排序演算法總結

Britjeans發表於2018-06-03

常見排序演算法總結:

  • 插入排序

  • 選擇排序

  • 快速排序

  • 堆排序

  • 希爾排序

  • 歸併排序

排序演算法衡量標準:

  1. 穩定性

  2. 時間、空間複雜度

一、插入排序(insertion sort)

實際執行時間,更多地取決於輸入序列所含逆序對的總數

template <typename T> void List <T>:: insertionSort( Posi(T) p, int n){ 
    for(int r=0;r<n;r++){ 
         insertAfter(search(p->data,r,p),p->data);//查詢+插入 
         p=p->succ; remove(p->pred); 
    } 
} 

效能分析:O(n)


二、選擇排序(selection sort)

template <typename T> void List <T> ::selectionSort(Posi(T) p, int n){
    Posi(T) head =p->pred; Posi(T) tail =p; //待排序區間(head,tail)
    for(int i=0; i<n; i++) tail=tail->succ; //head/tail可能是頭/尾哨兵
    while(1<n){
        //反覆從(非平凡的)待排序區間找出最大者,並移至有序區間前段
        insertBefore(tail, remove(selectMax(head->succ,n)));
        tail=tail->red; n- -;//待排序區間、有序區間的範圍,均同步更新
    }  
}

效能分析:

當selectMax()複雜度為theta(n)時 ,總體複雜度為theta(n^2) ,儘管如此,元素移動操作遠遠少於bubble sort,複雜度主要來自於比較操作 ,但是我們可以將selectMax()的複雜度降低為theta(log n)

三、快速排序(Quicksort)

快速排序是一種常用的演算法,它利用了分治策略(Divide and Conquer)。雖然它在最壞的情況下可能達到O(n^2)的時間複雜度,但是在平均的情況下,它一般都是非常有效的,所以是一種實用的演算法。

演算法思路:

選出一個軸點P,令P的左側元素均小於P點的元素,右側均大於P點的元素

虛擬碼:

quickSort(arr[],lo,hi){
    if(lo<hi){
        p=partition(arr,lo,hi);
        quickSort(arr,lo,pi-1);
		quickSort(arr,pi+1,hi);       
    }
}

效能分析:

最壞情況下時間複雜度:O(n^2)

最好情況下時間複雜度:O(n*lg n)

四、堆排序(heap sort)

演算法思路:

建堆過程即為排序過程。

堆通常以陣列形式表現

RepresentationExplanation
Arr[(2*i)+1]Returns the left child node
Arr[(2*i)+2]Returns the right child node
Arr[(i-1)/2]Returns the parent node
程式碼實現:
class Heap{
public:
    inline int left(int idx){
        return (idx<<1)+1;
    }
    inline int right(int idx){
        return (idx<<1)+2;
    }
    void max_heapify(vector<int>& nums, int idx){
        int largest =idx;
        int l =left(idx), r =right(idx);
        if(l<heap_size && nums[l]>nums[largest]) largest=l;
        if(l<heap_size && nums[r]>nums[largest]) largest=r;
        if(largest!=idx) {
            swap(nums[idx],nums[largest]);
            max_heapify(nums,largest);
        }
    }
    void build_max_heap(vector<int> & nums){
        heap_size=nums.size();
        for(int i=(heap_size>>1)-1;i>=0;i--)
            max_heapify(nums,i);
    }
private:
    int heap_size;
}

效能分析:排序時間複雜度為O(n*lg n)


五、希爾排序(shell sort)

將整個序列看作一個矩陣,逐列w-sorting

遞減增量 diminishing increment

由粗到細:重排矩陣,使其更窄,再次逐列排序w-ordered

逐步求精:如此往復,直至矩陣變成一列1-sorting

步長序列 step sequence:由各矩陣寬度構成的逆序列

優劣

1.不需要大量的輔助空間,和歸併排序一樣容易實現

2.希爾排序的時間複雜度與增量序列的選取有關,例如希爾增量時間複雜度為O(n^2),而Hibbard增量的希爾排序的時間複雜度為O(n^(3/2)),希爾排序時間複雜度的下界是n*log2n

3.希爾排序沒有快速排序演算法快,因此中等大小規模表現良好,對規模非常大的資料排序不是最優選擇

希爾演算法在最壞情況下和平均情況下執行效率相差不是很多,而快速排序在最壞情況下的執行效率會非常差。

void shellsort(int v[],int n)
{
    int gap,i,j,temp;
    for (gap = n/2; gap > 0; gap /= 2)
    {
        for(i = gap; i < n; i++)       
            for(j = i-gap; j>=0 && v[j]>v[j+gap]; j -= gap)
            {
                temp = v[j];
                v[j] = v[j+gap];
                v[j+gap] = temp;
            }     
    }
}


六、歸併排序(merge sort)

歸併排序(merge sort)

歸併排序利用了分治(divide-and-conquer)的思想。

演算法思路如下:

MergeSort(arr[],l,r)
If(r>l):
	1. 找到中點,將陣列一分為二:mi=(l+r)/2
	2. 對第一個部分進行歸併排序:mergeSort(arr,l,m)
	3. 對第二部分進行歸併排序:mergeSort(arr,m+1,r)
	4. 將上面兩個排好序的序列融合:merge(arr,l,m,r)

效能分析:

1.是穩定的排序演算法

2.時間複雜度theta(n*lg n)







相關文章