通俗理解九大排序算法


轉自本人知乎文章:https://zhuanlan.zhihu.com/p/30311662

現在,但凡規模大一點的互聯網公司招聘軟件相關的崗位,都會對數據結構和算法有一定要求。作為非科班出身的程序yuan,要想進好一點的公司,還是老老實實地把基礎打扎實吧。

說到排序,大家應該都不陌生,因為你生活中肯定有過網購吧,你在淘寶搜索寶貝的時候,遇到的就是排序,比如有按價格高低排序、按綜合排序、按信用高低排序。所以排序算法應該可以說是算法里面很重要的一個分支。

開始之前,先簡單介紹一下排序算法的幾個重要指標,這里,我盡量用自己理解的傻瓜式方法解讀:

(1)穩定性:當序列中存在兩個或兩個以上的關鍵字相等的時候,如果排序前序列中r1領先於r2,那么排序后r1如果仍舊領先r2的話,則是穩定的。(相等的元素排序后相對位置不變)

(2)不穩定性:當序列中存在兩個或兩個以上的關鍵字相等的時候,如果排序前序列中r1領先於r2,那么排序后r1如果落后r2的話,則是不穩定的。(相等的元素排序后相對位置發生改變)

(3)時間復雜度:算法的時間開銷是衡量其好壞的最重要的標志。高效率的算法應該具有更少的比較次數和記錄移動次數。

(4)空間復雜度:即執行算法所需要的輔助存儲的空間。

一、直接插入排序(插入類)

流程描述:遍歷序列中的關鍵字,每次取一個待排序的關鍵字,從待排序關鍵字的前一個關鍵字逐次向前掃描,如果掃描到的關鍵字大於待排序關鍵字,則把掃描到的關鍵字往后移一個位置。最后找到插入位置,將待排序關鍵字插入。

void InsertSort(int R[],int n)
{
int i,j
int temp;
for(i=1;i<n;++i)
 {
temp=R[i];   //將待排關鍵字暫時存放在temp中
j=i-1;      //待排關鍵字的前一個關鍵字序號
while(j>=0&&temp<R[j])
//從待排關鍵字的前一個關鍵字開始掃描,如果大於待排關鍵字,則往后移一個位置
  {
  R[j+1]=R[j];
  --j; 
  }
  R[j+1]=temp; //找到插入位置,將temp中暫存的待排關鍵字插入
 }
}

最壞情況:整個序列是逆序的時候,則內層循環的條件temp<R[j]始終成立,此時對於每一次外層循環,內層循環次數每次達到最大值(即內層循環位i次),外層循環i取值為1~i-1,所以總的執行次數為n(n-1)/2 。

最好情況:整個序列為正序的時候。內層循環條件始終不成立,所以內層循環始終不執行,始終執行語句R[j+1]=temp。所以時間復雜度為O(n)。

空間復雜度:算法所需的輔助存儲空間不隨待排序列的規模變化而變化,是個常量,所以為O(1)。

二、折半插入排序(插入類)

過程描述:過程同直接插入排序,只是不同於直接插入排序時用順序查找,這里用的是折半查找。所以折半插入排序在查找過程上比直接插入排序節約不少時間。但是關鍵字移動次數和直接插入排序一樣。

最好情況時間復雜度:

最壞情況時間復雜度:

平均情況時間復雜度:

 

三、冒泡排序(交換類)

過程描述:通過一系列的交換動作實現排序。首先第一個關鍵字和第二個關鍵字比較,如果第一個關鍵字大,二者交換;然后第二個關鍵字和第三個關鍵字比較,如果第二個關鍵字大,二者交換,否則不交換。一直進行下去,知道最終最大的哪個關鍵字被交換到了最后,一趟冒泡排序完成。

void BubbleSort(int R[],int n)
{
int i,j,flag;
int temp;
for(i=n-1;i>=1;--i)
{
  flag=0;    //flag用來標記本趟排序是否發生了交換
  for(j=1;j<=i;++j)
  {
    if(R[j-1]>R[j])
     {
     temp=R[j];
     R[j-1]=R[j];
     R[j]=temp;
     flag=1;   //flag=1表示本次排序發生了交換
     }  
   if(flag==0)//如果沒有發生交換,說明序列有序,排序結束
   return;
}
}

最壞情況:序列逆序,此時內層循環if語句的條件始終成立,基本操作執行的次數為n-i。i取值為1~n-1,所以總的執行次數為(n-1+1)(n-1)/2=n(n-1)/2,所以時間復雜度為O(n^2)。

最好情況:序列正序。此時內層循環的條件比較語句始終不成立,不發生交換,內層循環執行n-1次,所以時間復雜度為O(n)。

平均情況:時間復雜度O(n^2)。

四、簡單選擇排序(選擇類)

void SelectSort(int R[],int n)
{
int i,j,k;
int temp;
  for(i=0;i<n;++i)
  {
    k=i;
    for(j=i+1;j<n;++j) //從i后面的序列中挑選一個最小的關鍵字
    {
       if(R[k]>R[j])
       k=j;            //
       temp=R[i];
       R[i]=R[k];
       R[k]=temp;
    }
  }
}

五、希爾排序(插入類)

過程 描述:重點在增量的選取。如果增量為m,那么將下標為0、m、2m、3m的關鍵字分成一組,將下標為1、m+1、2m+1、3m+1等關鍵字分成另外一組,分別對這些組進行插入排序。這就是一趟希爾排序。

 

六、快速排序(交換類)

過程描述:每一趟選擇當前子序列中的一個關鍵字作為樞軸(一般選擇第一個關鍵字作為樞軸),將子序列中比樞軸小的移到樞軸前面,比樞軸大的移到樞軸后面,本趟交換完成后得到新的更短的子序列,成為下一趟交換的初始序列。一趟排序之后可以確定樞軸的最終位置。比樞軸小的全部在樞軸左邊,比樞軸大的全部在樞軸右邊。

void QuickSort(int R[],int high,int low)
{
int temp;
int i=low,j=high;
if(low<high)
{
temp=R[low];
while(i!=j)
 {
    while(j>i&&R[j]>=temp) --j; //從右往左掃描,找到一個小於樞軸temp的關鍵字
    if(i<j)
    {
      R[i]=R[j];  //將右邊小於樞軸temp的關鍵字放在temp的左邊
      ++i;       //左邊序列號向右移一位
    }
    while(i<j&&R[i]<temp) ++i;//從左向右掃描,找到一個大於樞軸關鍵字temp的關鍵字
    if(i<j)
     {
       R[j]=R[i];//將左邊大於樞軸temp的關鍵字放在temp的右邊
       --j;      //右邊序號向左移動一位
     }  
  }
 R[i]=temp;
 QuickSort(R,low,i-1);
 QuickSort(R,i+1,high);
 }
}

最好情況:時間復雜度為O(nlogx_{2}n) ,待排序列越接近無序,本算法效率越高。

最壞情況:時間復雜度為 O(n^{2}) ,待排序列越接近有序,本算法效率越低。

平均情況:時間復雜度 O(nlogx_{2}n) 。

空間復雜度:從頭到尾只用了temp這一個輔助存儲,所以為O(1)。

七、堆排序(選擇類)

把堆看成完全二叉樹,大根堆---父親大孩子小;小根堆---父親小孩子大。

過程描述:整個排序的過程就是不斷地將序列調整為堆。

以原始序列:49 38 65 97 76 13 27 49為例,調整為大根堆。

(1)調整97,97>49,不需要調整

(2)調整65,65>13,65>27,不需要調整

(3)調整38,38<97,38<76。需要調整,38和97交換,交換后38成為49的根節點,,繼續將38和49交換。

(4)調整49,49<97,49<65,所以49和較大者97交換,交換后,49<76,仍然不滿足大根堆,將49與76交換。

八、2路歸並排序

void mergeSort(int A[],int low,int high)
{
  if(low<high)
  {
    int mid=(low+high)/2;
    mergeSort(A,low,mid);   //歸並排序前半段
    mergeSort(A,mid+1,high);//歸並排序后半段
    merge(A,low,mid,high);  //把數組中的low到mid  和 mid+1到high的兩段有序序列歸並成一段有序序列
  }
}

九、基數排序

"多關鍵字排序",(1)最高位優先(2)最低位優先。例如最高位優先:先按最高位排成若干子序列,再對每個子序列按次高位進行排序。

如下圖,低位優先的排序過程:每個桶相當於一個隊列,先進先出規則。

最后得到的結果:最高為有序,最高位相同的關鍵字次高位有序,次高位相同的關鍵字最低位有序,所以整個序列有序。


免責聲明!

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



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