這是一篇講解快速排序演算法的文章
排序演算法有很多種,例如氣泡排序法,堆排序法,簡單排序法以及快速排序法等。其中,快速排序演算法的時間複雜度為o(nlogn),氣泡排序演算法的時間複雜度為o(n^2).故快速排序演算法比較快,但是其不穩定。
穩定性是指:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序後的序列中,r[i]仍在r[j]之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。
快速排序演算法的核心就是,先找到乙個值a,把待排序的資料中小於a的放在a資料的左邊,大於的放在a的右邊。對a左邊的資料與右邊的資料分別再進行上述的操作,直到資料達到穩定的效果。
詳細描述:首先在要排序的序列 a 中選取乙個中軸值,而後將序列分成兩個部分,其中左邊的部分 b 中的元素均小於或者等於 中軸值,右邊的部分 c 的元素 均大於或者等於中軸值,而後通過遞迴呼叫快速排序的過程分別對兩個部分進行排序,最後將兩部分產生的結果合併即可得到最後的排序序列。
「基準值」的選擇有很多種方法。最簡單的是使用第乙個記錄的關鍵字值。但是如果輸入的陣列是正序或者逆序的,就會將所有的記錄分到「基準值」的一邊。較好的方法是隨機選取「基準值」,這樣可以減少原始輸入對排序造成的影響。但是隨機選取「基準值」的開銷大。
為了實現一次劃分,我們可以從陣列(假定資料是存在陣列中)的兩端移動下標,必要時交換記錄,直到陣列兩端的下標相遇為止。為此,我們附設兩個指標(下角標)i 和 j, 通過 j 從當前序列的有段向左掃瞄,越過不小於基準值的記錄。當遇到小於基準值的記錄時,掃瞄停止。通過 i 從當前序列的左端向右掃瞄,越過小於基準值的記錄。當遇到不小於基準值的記錄時,掃瞄停止。交換兩個方向掃瞄停止的記錄 a[j] 與 a[i]。 然後,繼續掃瞄,直至 i 與 j 相遇為止。掃瞄和交換的過程結束。這是 i 左邊的記錄的關鍵字值都小於基準值,右邊的記錄的關鍵字值都不小於基準值。
通過兩個不相鄰元素交換,可以一次交換消除多個逆序,加快排序速度。快速排序方法在要排序的資料已經有序的情況下最不利於發揮其長處。
下面我們通過乙個案例來演示一下快速排序的基本步驟: 以序列 46 30 82 90 56 17 95 15 共8個元素
初始狀態: 46 30 82 90 56 17 95 15 選擇46 作為基準值,i = 0, j = 7
i = 0 j = 7
15 30 82 90 56 17 95 46 15 < 46, 交換 15 和 46,移動 i, i = 1
i = 1 j = 7
15 30 82 90 56 17 95 46 30 < 46, 不需要交換,移動 i , i = 2
i = 2 j = 7
15 30 46 90 56 17 95 82 82 > 46, 交換82 和 46,移動 j , j = 6
i = 2 j = 6
15 30 46 90 56 17 95 82 95 > 46, 不需要交換,移動 j , j = 5
i = 2 j = 5
15 30 17 90 56 46 95 82 17 < 46, 交換46 和 17,移動 i, i = 3
i = 3 j = 5
15 30 17 46 56 90 95 82 90 > 46, 交換90 和 46,移動 j , j = 4
3 = i j = 4
15 30 17 46 56 90 95 82 56 > 46, 不需要交換,移動 j , j = 3
i = j = 3
i = j = 3, 這樣序列就這樣分割成了兩部分,左邊部分 均小於 基準值(46);右邊部分 ,均大於基準值。這樣子我們就達到了分割序列的目標。在接著對子序列用同樣的辦法進行分割,直至子串行不超過乙個元素,那麼排序結束,整個序列處於有序狀態。
import numpy as npimport time
start=time.clock()
def gatedata(filename):
fr=open(filename)
arraylines=fr.readlines()
longoflines=len(arraylines)
index = 0
returnmat=np.zeros((longoflines,1),dtype="float_")
for lines in arraylines:
line = lines.strip()
lines=line.split()
returnmat[index,0]=lines[1]
index+=1
return returnmat
a=gatedata("d:\\python高階\\birch\\data1.txt")
class sqlist:
def __init__(self, lis=none):
self.r = lis
def swap(self, i, j):
"""定義乙個交換元素的方法,方便後面呼叫。"""
self.r[[i,j],:]=self.r[[j,i],:]
def quick_sort(self):
"""呼叫入口"""
self.qsort(0, self.r.shape[0]-1)
def qsort(self, low, high):
"""遞迴呼叫"""
if low < high:
pivot = self.partition(low, high)
self.qsort(low, pivot-1)
self.qsort(pivot+1, high)
def partition(self, low, high):
"""快速排序的核心**。
其實就是將選取的pivot_key不斷交換,將比它小的換到左邊,將比它大的換到右邊。
它自己也在交換中不斷變換自己的位置,直到完成所有的交換為止。
但在函式呼叫的過程中,pivot_key的值始終不變。
:param low:左邊界下標
:param high:右邊界下標
:return:分完左右區後pivot_key所在位置的下標
"""lis = self.r
pivot_key = lis[low,0]
while low < high:
while low < high and lis[high,0] >= pivot_key:
high -= 1
self.swap(low, high)
while low < high and lis[low,0] <= pivot_key:
low += 1
self.swap(low, high)
return low
# def __str__(self):
# ret = ""
# for i in self.r:
# ret += " %s" % i
# return ret
if __name__ == '__main__':
sqlist = sqlist(a)
sqlist.quick_sort()
end=time.clock()
#print(sqlist.r)
print('the program consums %f seconds'%(end-start))
遞迴和迭代區別
遞迴和迭代 遞迴的實現是通過呼叫函式本身,函式呼叫的時候,每次呼叫時要做位址儲存,引數傳遞等,這是通過乙個遞迴工作棧實現的。具體是每次呼叫函式本身時需要儲存的內容有區域性變數 形參 函式位址等,那麼,如果遞迴呼叫n次,則遞迴棧裡需要儲存n 區域性變數,n 形參,n 函式位址個記憶體空間,很可能導致空...
遞迴和迭代的區別
遞迴的基本概念 程式呼叫自身的程式設計技巧稱為遞迴,是函式自己呼叫自己.乙個函式在其定義中直接或間接呼叫自身的一種方法,它通常把乙個大型的複雜的問題轉化為乙個與原問題相似的規模較小的問題來解決,可以極大的減少 量.遞迴的能力在於用有限的語句來定義物件的無限集合.使用遞迴要注意的有兩點 1 遞迴就是在...
遞迴和迭代的區別
遞迴的基本概念 程式呼叫自身的程式設計技巧稱為遞迴,是函式自己呼叫自己.乙個函式在其定義中直接或間接呼叫自身的一種方法,它通常把乙個大型的複雜的問題轉化為乙個與原問題相似的規模較小的問題來解決,可以極大的減少 量.遞迴的能力在於用有限的語句來定義物件的無限集合.使用遞迴要注意的有兩點 1 遞迴就是在...