最近在找工做面試的時候老是會被問到排序算法的種種,如今對排序算法進行一個系統的總結。也算是再複習一遍的,有好多本身也忘得差很少了。python
排序有內部排序和外部排序兩大類。內部排序值得是全部的排序是在內存中進行;外部排序則是由於數據量太大,一次不能將所有數據放在內存中,在排序過程當中,須要訪問外存。面試
關於時間複雜度和空間複雜度算法
因爲時間關係,我沒有本身畫,如下的表格是在別處轉的,詳見經常使用排序算法總結shell
1.插入排序--直接插入排序(Straight Insertion Sort)數組
基本思想:數據結構
將一個記錄插入到已經有序的表中,從而獲得一個新的,記錄數+1的有序表。即:先將序列的第一個記錄當作是一個有序的子序列,而後從第二個記錄逐個進行插入,直至整個序列有序爲止。app
若是遇到元素相等時,那麼將插入的元素插入到與其相等的元素以後。相等元素的先後順序並無改變,因此插入排序是穩定的。less
算法的實現:ide
def insertion_sort(arr): arrlen = len(arr) for i in range(1, arrlen): insert(arr, i) def insert(arr, i): tmp = arr[i] j = i # 查找第i的元素應該的位置, 而且 # 順便把比它大的元素日後挪 # 實際上是用了心思在裏面的 while j > 0 and tmp < arr[j - 1]: arr[j] = arr[j - 1] j -= 1 arr[j] = tmp a = [65,6,3,5,54,65] insertion_sort(a) print a
2.希爾排序學習
基本思想:
希爾排序又叫作縮小增量排序。先將整個序列分割成若干個子序列,並對每一個子序列進行直接插入排序,再對所有序列進行直接插入排序。
算法實現:
def shell_sort(seq): incr = len(seq)/2 while(incr>=1): for i in range(incr,len(seq)): tmp=seq[i] pos=i; for j in range(i-incr,-1,-incr): if seq[j]>tmp: seq[j+incr]=seq[j] pos=j seq[pos]=tmp incr = incr/2 return seq if __name__ == '__main__': A = [10, -3, 5, 7, 1, 3, 7] print 'Before sort:',A shell_sort(A) print 'After sort:',A
3.選擇排序--簡單選擇排序
基本思想:
在要排序的一組數中,選出最小(或者最大)的一個數與第1個位置的數交換;而後在剩下的數當中再找最小(或者最大)的與第2個位置的數交換,依次類推,直到第n-1個元素(倒數第二個數)和第n個元素(最後一個數)比較爲止。
算法實現:
def selectionSort(seq): length=len(seq) for i in range(length): mini=min(seq[i:]) if seq[i]>mini: j=seq.index(mini,i) seq[i],seq[j]=seq[j],seq[i] if __name__=='__main__': seq=[3,4,5,9,3,1,5,7,90,-2,] selectionSort(seq) print(seq)
效率:
最壞狀況下,即待排序記錄初始狀態是按第一條記錄最大,以後的記錄從小到大順序排列,則須要移動記錄的次數最多爲3(n-1)。簡單選擇排序過程當中須要進行的比較次數與初始狀態下待排序的記錄序列的排列狀況無關。當i=1時,需進行n-1次比較;當i=2時,需進行n-2次比較;依次類推,共須要進行的比較次數是(n-1)+(n-2)+…+2+1=n(n-1)/2,即進行比較操做的時間複雜度爲O(n^2),進行移動操做的時間複雜度爲O(n)。(百度百科)
簡單選擇排序是不穩定排序。
簡單選擇排序的改進——二元選擇排序
簡單選擇排序,每趟循環只能肯定一個元素排序後的定位。咱們能夠考慮改進爲每趟循環肯定兩個元素(當前趟最大和最小記錄)的位置,從而減小排序所需的循環次數。改進後對n個數據進行排序,最多隻需進行[n/2]趟循環便可。
4.選擇排序--堆排序
基本思想:
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。能夠利用數組的特色快速定位指定索引的元素。堆分爲大根堆和小根堆,是徹底二叉樹。大根堆的要求是每一個節點的值都不大於其父節點的值,即A[PARENT[i]] >= A[i]。在數組的非降序排序中,須要使用的就是大根堆,由於根據大根堆的要求可知,最大的值必定在堆頂。
堆排序的過程相對來講比較複雜,現只給出算法實現,詳細過程請參照另一篇博文:堆排序學習筆記
算法實現:
def fixDown(a,k,n): #自頂向下堆化,從k開始堆化 N=n-1 while 2*k<=N: j=2*k if j<N and a[j]<a[j+1]: #選出左右孩子節點中更大的那個 j+=1 if a[k]<a[j]: a[k],a[j]=a[j],a[k] k=j else: break def heapSort(l): n=len(l)-1 for i in range(n//2,0,-1): fixDown(l,i,len(l)) while n>1: l[1],l[n]=l[n],l[1] fixDown(l,1,n) n-=1 return l[1:] l=[-1,26,5,77,1,61,11,59,15,48,19] #第一個元素不用,佔位 res=heapSort(l) print(res)
5.交換排序--冒泡排序
基本思想:
在要排序的一組數中,對當前還未排好序的範圍內的所有數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的數往上冒。即:每當相鄰的數比較後,發現排序不符合要求時,將他們互換。
示例:
#!/usr/bin/env python #coding:utf-8 def bubbleSort(seq): length=len(seq) for i in range(length): for j in range(length-1,i,-1): if seq[j-1]>seq[j]: seq[j-1],seq[j]=seq[j],seq[j-1] if __name__=='__main__': seq=[2,9,7,7,4,3,2,-4,54,-7,0] bubbleSort(seq) print(seq)
冒泡排序算法的改進(兩種):
設置一標誌性變量pos,用於記錄每趟排序中最後一次進行交換的位置。因爲pos位置以後的記錄均已交換到位,故在進行下一趟排序時只要掃描到pos位置便可。
傳統冒泡排序中每一趟排序操做只能找到一個最大值或最小值,咱們考慮利用在每趟排序中進行正向和反向兩遍冒泡的方法一次能夠獲得兩個最終值(最大者和最小者) , 從而使排序趟數幾乎減小了一半。
基本思想:
1)選擇一個基準元素,一般選擇第一個元素或者最後一個元素,
2)經過一趟排序講待排序的記錄分割成獨立的兩部分,其中一部分記錄的元素值均比基準元素值小。另外一部分記錄的 元素值比基準值大。
3)此時基準元素在其排好序後的正確位置
4)而後分別對這兩部分記錄用一樣的方法繼續進行排序,直到整個序列有序。
示例:
python代碼實現:
#!/usr/bin/env python #coding:utf-8 def qsort(seq): if seq==[]: return [] else: pivot=seq[0] lesser=qsort([x for x in seq[1:] if x<pivot]) greater=qsort([x for x in seq[1:] if x>=pivot]) return lesser+[pivot]+greater if __name__=='__main__': seq=[5,6,78,9,0,-1,2,3,-65,12] print(qsort(seq))
基本思想:
歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個子序列,每一個子序列是有序的。而後再把有序子序列合併爲總體有序序列。
def mergesort(seq): if len(seq)<=1: return seq mid=int(len(seq)/2) left=mergesort(seq[:mid]) right=mergesort(seq[mid:]) return merge(left,right) def merge(left,right): result=[] i,j=0,0 while i<len(left) and j<len(right): if left[i]<=right[j]: result.append(left[i]) i+=1 else: result.append(right[j]) j+=1 result+=left[i:] result+=right[j:] return result if __name__=='__main__': seq=[4,5,7,9,7,5,1,0,7,-2,3,-99,6] print(mergesort(seq))
效率:
時間複雜度無論在什麼狀況下都爲n(nlogn)