轉自本人知乎文章:https://zhuanlan.zhihu.com/p/30311662算法
如今,但凡規模大一點的互聯網公司招聘軟件相關的崗位,都會對數據結構和算法有必定要求。做爲非科班出身的程序yuan,要想進好一點的公司,仍是老老實實地把基礎打紮實吧。數組
說到排序,你們應該都不陌生,由於你生活中確定有過網購吧,你在淘寶搜索寶貝的時候,遇到的就是排序,好比有按價格高低排序、按綜合排序、按信用高低排序。因此排序算法應該能夠說是算法裏面很重要的一個分支。數據結構
開始以前,先簡單介紹一下排序算法的幾個重要指標,這裏,我儘可能用本身理解的傻瓜式方法解讀:數據結構和算法
(1)穩定性:當序列中存在兩個或兩個以上的關鍵字相等的時候,若是排序前序列中r1領先於r2,那麼排序後r1若是仍舊領先r2的話,則是穩定的。(相等的元素排序後相對位置不變)ui
(2)不穩定性:當序列中存在兩個或兩個以上的關鍵字相等的時候,若是排序前序列中r1領先於r2,那麼排序後r1若是落後r2的話,則是不穩定的。(相等的元素排序後相對位置發生改變)spa
(3)時間複雜度:算法的時間開銷是衡量其好壞的最重要的標誌。高效率的算法應該具備更少的比較次數和記錄移動次數。3d
(4)空間複雜度:即執行算法所須要的輔助存儲的空間。code
1、直接插入排序(插入類)blog
流程描述:遍歷序列中的關鍵字,每次取一個待排序的關鍵字,從待排序關鍵字的前一個關鍵字逐次向前掃描,若是掃描到的關鍵字大於待排序關鍵字,則把掃描到的關鍵字日後移一個位置。最後找到插入位置,將待排序關鍵字插入。排序
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)。
2、折半插入排序(插入類)
過程描述:過程同直接插入排序,只是不一樣於直接插入排序時用順序查找,這裏用的是折半查找。因此折半插入排序在查找過程上比直接插入排序節約很多時間。可是關鍵字移動次數和直接插入排序同樣。
最好狀況時間複雜度:
最壞狀況時間複雜度:
平均狀況時間複雜度:
3、冒泡排序(交換類)
過程描述:經過一系列的交換動做實現排序。首先第一個關鍵字和第二個關鍵字比較,若是第一個關鍵字大,兩者交換;而後第二個關鍵字和第三個關鍵字比較,若是第二個關鍵字大,兩者交換,不然不交換。一直進行下去,知道最終最大的哪一個關鍵字被交換到了最後,一趟冒泡排序完成。
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)。
4、簡單選擇排序(選擇類)
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; } } }
5、希爾排序(插入類)
過程 描述:重點在增量的選取。若是增量爲m,那麼將下標爲0、m、2m、3m的關鍵字分紅一組,將下標爲一、m+一、2m+一、3m+1等關鍵字分紅另一組,分別對這些組進行插入排序。這就是一趟希爾排序。
6、快速排序(交換類)
過程描述:每一趟選擇當前子序列中的一個關鍵字做爲樞軸(通常選擇第一個關鍵字做爲樞軸),將子序列中比樞軸小的移到樞軸前面,比樞軸大的移到樞軸後面,本趟交換完成後獲得新的更短的子序列,成爲下一趟交換的初始序列。一趟排序以後能夠肯定樞軸的最終位置。比樞軸小的所有在樞軸左邊,比樞軸大的所有在樞軸右邊。
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); } }
最好狀況:時間複雜度爲 ,待排序列越接近無序,本算法效率越高。
最壞狀況:時間複雜度爲 ,待排序列越接近有序,本算法效率越低。
平均狀況:時間複雜度 。
空間複雜度:從頭至尾只用了temp這一個輔助存儲,因此爲O(1)。
7、堆排序(選擇類)
把堆當作徹底二叉樹,大根堆---父親大孩子小;小根堆---父親小孩子大。
過程描述:整個排序的過程就是不斷地將序列調整爲堆。
以原始序列: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交換。
8、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的兩段有序序列歸併成一段有序序列 } }
9、基數排序
"多關鍵字排序",(1)最高位優先(2)最低位優先。例如最高位優先:先按最高位排成若干子序列,再對每一個子序列按次高位進行排序。
以下圖,低位優先的排序過程:每一個桶至關於一個隊列,先進先出規則。
最後獲得的結果:最高爲有序,最高位相同的關鍵字次高位有序,次高位相同的關鍵字最低位有序,因此整個序列有序。