各類排序(一)

  • 本文中 \(n\) 表明着待排序序列的長度。
  • 算法是否穩定:若 \(a_i = a_j \ , \ i < j\),排序後若\(i < j\) 則穩定,反之不穩定。(可能有點歧義湊活看)

冒泡排序

又叫氣泡排序,起泡排序,泡沫排序
這應該是最簡單的排序算法了吧。
在未排好序以前一直掃描序列,每次將最大數的放到序列的最後,因此最多 \(n-1\) 次掃描後序列就排好序了。算法

最差時間複雜度:\(O(n ^ 2)\)
最優時間複雜度:\(O(n)\)
平均時間複雜度:\(O(n ^ 2)\)
算法是否穩定:是優化

for(int i=1;i<n;++i) {//n-1輪掃描
    bool okay=true;
    for(int j=1;j<=n-i;++j) {//[n-i+2,n]都已經排好序了。
        if(a[j]>a[j+1]) {
            swap(a[j],a[j+1]);
            okay=false;
        }
    }
    if(okay==true) break;
}

上幾張動圖,幫助理解。spa

雞尾酒排序

冒泡排序的一種優化,在一些特殊狀況下有用。
有兩種操做:code

  • 將序列中最大的數放到最後
  • 將序列中最小的數放到最前
    在未排好序以前將上面兩種操做交替進行。

最差時間複雜度:\(O(n ^ 2)\)
最優時間複雜度:\(O(n)\)
平均時間複雜度:\(O(n ^ 2)\)
算法是否穩定:是blog

int left=1,right=n;//[left,right]須要排序。
while(left<right) {//最多進行到left=right就中止
    bool okay=true;
    for(int i=left;i<right;++i) {//將序列中最大的數放到最後
        if(a[i]>a[i+1]) {
            swap(a[i],a[i+1]);
            okay=false;
        }
    }
    if(okay==true) break;
    --right;//[right+1,n]已經排好序了
    for(int i=right;i>left;--i) {
        if(a[i]<a[i-1]) {
            swap(a[i],a[i-1]);
            okay=false;
        }
    }
    ++left;//[1,left-1]已經排好序了
    if(okay==true) break;
}

選擇排序

在未排好序以前一直掃描序列,每次將最小數的放到序列的最前,因此最多 \(n-1\) 次掃描後序列就排好序了。
與冒泡排序的不一樣:冒泡排序每掃一次序列會進行屢次交換,將不符合順序的都交換。選擇排序每掃一次序列只會進行一次交換,將最小的元素與最前的元素交換。排序

最差時間複雜度\(O(n ^ 2)\)
最優時間複雜度\(O(n ^ 2)\)
平均時間複雜度\(O(n ^ 2)\)
算法是否穩定:否class

for(int i=1;i<n;++i) {//最多掃n-1次
    bool okay=true;
    int minn=0x7fffffff,flag;
    for(int j=i;j<=n;++j) {
        if(a[j]<minn) {
            minn=a[j];flag=j;//找最小的並記錄下位置。
            okay=false;
        }
    }
    if(okay==true) break;
    std::swap(a[i],a[flag]);//將最小的元素與最前的元素交換
}

放張圖理解一下效率

{5,8,5,2,9},可知選擇排序不穩定。基礎

插入排序

流程就像是打牌的摸牌階段。im

操做將一個數插入到一個排好序的序列中時期仍然排好序便可。在序列基本有序或者序列長度小時效率很高。

最差時間複雜度:\(O(n ^ 2)\)
最優時間複雜度:\(O(n)\)
平均時間複雜度:\(O(n ^ 2)\)
算法是否穩定:是

for(int i=2;i<=n;++i) {//[1,i-1]已經排好了序
    int temp=a[i],j=i-1;
    while(j>0&&a[j]>temp) {//將第i張牌插入其中
        a[j+1]=a[j];
        j--;
    }
    a[j+1]=temp;
}

上張動圖理解一下

二分插入排序

在插入排序的基礎上使用二分查找來肯定當前數應該插入到哪裏。
這是優化嗎?我爲何感受比插入排序還慢。

時間複雜度:\(O(n(log n + n))\)
算法是否穩定:是否

for(int i=2;i<=n;++i) {
    int left=1,right=i-1,temp=a[i];
    while(left<=right) {//二分查找當前數插入到哪裏
        int mid=(left+right)>>1;
        if(a[mid]>temp) right=mid-1;
        else left=mid+1;
    }
    for(int j=i-1;j>=left;--j) a[j+1]=a[j];
    a[left]=temp;//插進去
}

對於if(a[mid]>temp) right=mid-1;

  • 若是加了等號的話,不穩定排序
  • 若是不加等號的話,是穩定排序

希爾排序

  • 插入排序的特色:在序列基本有序或者序列長度小時效率很高。

運用了插入排序,如今咱們有一個增量 \(x\) 通常爲 \(\frac{n}{2}\) ,咱們按照這個份量將整個序列分紅 \(x\) 組,對每組進行插入排序(由於插入排序在須要排序的序列的長度很小的時候很是快)。而後咱們將增量減少通常爲 \(x = \frac{x}{2}\) (最後增量必定會變成 \(1\) ,因此是必然正確的),重複上面的步驟。雖然增量在變小,序列長度在增長,但會變得愈來愈有序,也就愈來愈高效。
又叫縮小增量排序。

最差時間複雜度:\(O(n(logn) ^ 2)\)
最優時間複雜度:\(O(n)\)
算法是否穩定:否

int ad=n/2;//增量一開始爲n/2
while(ad>=1) {
    for(int i=1;i<=ad;++i) {//分紅ad組進行插入排序
        for(int j=i+ad;j<=n;j+=ad) {//插入排序
            int k=j-ad,temp=a[j];
            while(k>=i&&a[k]>temp) {
                a[k+ad]=a[k];k-=ad;
            }
            a[k+ad]=temp;
        }
    }
    ad=ad/2;
}

以後可能會更新一下地精排序啥的,但以後再說。

相關文章
相關標籤/搜索