排序算法學習(一):冒泡排序

所謂排序即按照必定的規律將一組數據進行排列。排序算法的形式化定義以下:設存在一組無序序列$\{x_1,x_2,...,x_n\}$​ ,而且存在一個函數$f(x)$​,在通過一系列對該序列中的元素位置調整後,使得新獲得的序列知足對於任意的$0 \le i \le j \le n$​,均有$f(x_i) \le f(x_j)$​(升序排序)或者$f(x_i) \ge f(x_j)$​(降序排序)。針對不一樣的數據源的不一樣特性,人們設計出了不一樣的排序算法,以求對算法的時間複雜度或者空間複雜度進行優化。常見的排序算法有:冒泡排序,插入排序,快速排序,桶排序算法等。下面對各類排序算法的原理進行簡述,並分析其優缺點。(如下在對排序算法的描述時,都假設將數據按照升序排序進行排列)。python

冒泡排序

冒泡算法的步驟以下:比較相鄰兩個元素的大小,根據比較結果將兩個元素按照從小到大的順序排列,逐步向後移動,一直移動到末端。冒泡算法中的一輪過程如圖1所示,每i輪的排序過程便是將第i個最大的元素移動到當前的數組中最大的位置。在每一輪的排序過程當中,右邊的已排序序列長度不斷增加,最終使得整個序列都成爲已排序序列。冒泡排序的完整過程以下圖所示:算法

冒泡排序算法執行過程

圖1 冒泡排序的完整過程數組

 

 

冒泡排序算法的最基本實現

根據圖1所描述的過程,冒泡排序算法的最基本的Python代碼實現以下:函數

[sourcecode language='python'  padlinenumbers='true']
	def bubbleSort(data:List[int])->List[int]:
		length = len(data)
		for i in range(0, length):
			for j in range (0, length - 1):
				if data[j] > data[j+1]:
					tmp = data[j]
					data[j] = data[j+1]
					data[j+1] = tmp
		return data
[/sourcecode]

對優化後的代碼分析可知,一共須要進行$n$輪的排序,每一輪的排序中都須要進行$n-1$次的大小比較,所以該算法的算法複雜度爲$O(n^2)$,空間複雜度爲$1$優化

針對每輪比較次數的效率優化

經過對圖1的觀察,第1輪排序完成後,最右側的1個元素是一個已完成排序的序列,在第2輪的排序過程當中,最後1次的比較沒有發生順序的改變;第2輪排序完成後,最右側的2個元素是一個已完成排序的序列,在第3輪的排序過程當中,最後2次的比較沒有發生順序的改變。第$i$輪排序完成後,最右側的$i$個元素是一個已完成排序的序列,在第$i+1$輪的排序過程當中,最後$i$次的比較沒有發生順序的改變。根據這個特色,咱們能夠對每一輪的排序過程當中須要比較的次數進行優化。代碼以下:spa

def bubbleSort(data:List[int])->List[int]:
            length = len(data) for i in range(0, length): for j in range (0, length - i - 1): if data[j] > data[j + 1]: tmp = data[j] data[j] = data[j+1] data[j+1] = tmp return data

在進行優化以後,能夠發如今每一輪的排序過程當中須要比較的元素的數量獲得了優化,在第$i$輪中排序中進行了$i-1$次大小比較,所以在整個排序過程當中,一共須要進行$\sum_{1}^{n}(i-1)$次比較運算,算法複雜度爲$O(n^2)$,空間複雜度爲$1$設計

針對算法終止條件的效率優化

另外,還能夠對冒泡排序算法的終止條件進行優化。咱們對圖1進行觀察,發現從第3輪開始,序列的順序已經達到排序完成的狀態,此後序列中元素的相對位置再也不發生改變,所以咱們能夠根據此對排序的終止條件的判斷進行優化。代碼以下:code

    def bubbleSort(data:List[int])->List[int]):
            length = len(data) changed = 0 for i in range(0, length): for j in range (0, length - i - 1): if data[j] > data[j + 1]: tmp = data[j] data[j] = data[j+1] data[j+1] = tmp changed = 1 if changed == 0: return data return data

在進行優化以後,能夠發現排序算法可能提早終止排序狀態。考慮最好的狀況,序列的順序原本就是排列好的,那麼須要進行一輪排序來進行確認,一共須要進行$n-1$次排序,所以在最好的狀況下算法的時間複雜度爲$O(n)$,空間複雜度爲$1$;在最差的狀況下,一共須要進行$\sum_{1}^{n}(i-1)$次比較運算,算法複雜度爲$O(n^2)$,空間複雜度爲$1$blog

雞尾酒排序

雞尾酒排序又稱爲雙向冒泡排序,是冒泡排序的一個變種算法,能夠在某些狀況下減小算法的比較次數。雞尾酒排序算法的過程如圖2所示:排序

雞尾酒排序算法執行過程

​圖2 雞尾酒排序算法的執行過程

對圖2觀察可知,利用雞尾酒排序在第三輪的時候,序列就已經達到了排序完成的狀態。比普通的冒泡排序算法快了一輪,下面分析爲何會比冒泡算法快一輪的緣由。在冒泡算法過程當中,元素單一的從一側按照從小到大的順序被移動到另外一側。可是若是假設有這樣一種狀況,即序列的左側的大部分元素的相對順序都是排好的,只有右側部分元素的順序不對,那麼此時雞尾酒排序相對於冒泡排序就會有極大的優點。極端一點的例子,如:序列$[2,3,4,5,1]$,若是按照冒泡排序來進行,那麼須要進行4輪排序,若是按照雞尾酒排序的算法來排序則只須要2輪排序。可是對於某些極端狀況,如序列$[5,4,3,2,1]$,冒泡排序和雞尾酒排序都須要進行4輪排序。

雞尾酒排序算法的Python代碼以下(如下代碼已經針對每輪的比較次數和算法終止條件進行了優化):

    def cocktailsort(data:List[int])->List[int]):
            start = 0 end = len(data) changed = 0 while start != end: for i in range(start, len(data) - 1): if data[i] > data[i + 1]: tmp = data[i] data[i] = data[i + 1] data[i + 1] = tmp changed = 1 print(data) if changed == 0: return data start = start + 1 changed = 0 for i in range(end - 1, 0, -1): if data[i] < data[i - 1]: tmp = data[i] data[i] = data[i - 1] data[i - 1] = tmp changed = 1 print(data) if changed == 0: return data end = end - 1 return data

 

相關文章
相關標籤/搜索