Python3 實現(xiàn)十大經(jīng)典排序算法
算法分類
算法比較
術(shù)語說明
- 穩(wěn)定:如果a原本在b前面蚤氏,而a=b梗逮,排序之后a仍然在b的前面渠旁;
- 不穩(wěn)定:如果a原本在b的前面,而a=b蒂教,排序之后a可能會出現(xiàn)在b的后面香罐;
- 內(nèi)排序:所有排序操作都在內(nèi)存中完成栗菜;
- 外排序:由于數(shù)據(jù)太大风题,因此把數(shù)據(jù)放在磁盤中,而排序通過磁盤和內(nèi)存的數(shù)據(jù)傳輸才能進行咨察;
- 時間復(fù)雜度: 一個算法執(zhí)行所耗費的時間论熙。
- 空間復(fù)雜度:運行完一個程序所需內(nèi)存的大小。
1.選擇排序(Selection Sort)
-
選擇排序(Selection Sort):
- 選擇排序(Selection-sort)是一種簡單直觀的排序算法摄狱。它的工作原理:首先在未排序序列中找到最信Ч睢(大)元素,存放到排序序列的起始位置二蓝,然后誉券,再從剩余未排序元素中繼續(xù)尋找最兄秆帷(大)元素刊愚,然后放到已排序序列的末尾。以此類推踩验,直到所有元素均排序完畢鸥诽。
-
算法描述:
- n個記錄的直接選擇排序可經(jīng)過n-1趟直接選擇排序得到有序結(jié)果。具體算法描述如下:
- 1.初始狀態(tài):無序區(qū)為R[1..n]箕憾,有序區(qū)為空牡借;
- 2.第i趟排序(i=1,2,3…n-1)開始時,當(dāng)前有序區(qū)和無序區(qū)分別為R[1..i-1]和R(i..n)袭异。該趟排序從當(dāng)前無序區(qū)中-選出關(guān)鍵字最小的記錄 R[k]钠龙,將它與無序區(qū)的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變?yōu)橛涗泜€數(shù)增加1個的新有序區(qū)和記錄個數(shù)減少1個的新無序區(qū)御铃;
- 3.n-1趟結(jié)束碴里,數(shù)組有序化了。
- n個記錄的直接選擇排序可經(jīng)過n-1趟直接選擇排序得到有序結(jié)果。具體算法描述如下:
-
算法演示
算法實現(xiàn)
def select_sort(data):
"""
:param data: list
:return: sorted data
"""
length = len(data)
if length < 2:
return data
for i in range(length - 1):
# 最小值的索引
minIndex = i
for j in range(i + 1, length):
# 找到最小值的索引
if (data[j] < data[minIndex]):
minIndex = j
# 將最小的值放在i處
data[i], data[minIndex] = data[minIndex], data[i]
# print(data)
return data
2.冒泡排序(Bubble Sort)
- 冒泡排序(Bubble Sort):
- 冒泡排序是一種簡單的排序算法上真。它重復(fù)地走訪過要排序的數(shù)列咬腋,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來睡互。走訪數(shù)列的工作是重復(fù)地進行直到?jīng)]有再需要交換根竿,也就是說該數(shù)列已經(jīng)排序完成陵像。這個算法的名字由來是因為越小的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端。
- 算法描述:
- 1.比較相鄰的元素寇壳。如果第一個比第二個大醒颖,就交換它們兩個;
- 2.對每一對相鄰元素作同樣的工作九巡,從開始第一對到結(jié)尾的最后一對图贸,這樣在最后的元素應(yīng)該會是最大的數(shù);
- 3.針對所有的元素重復(fù)以上的步驟冕广,除了最后一個疏日;
- 4.重復(fù)步驟1~3,直到排序完成撒汉。
-
算法演示
- 算法實現(xiàn)
def bubble_sort(data):
"""
:param data: list
:return: sorted data
"""
length = len(data)
if length < 2:
return data
for i in range(length - 1):
for j in range(length - 1 - i):
if (data[j + 1] < data[j]):
data[j], data[j + 1] = data[j + 1], data[j]
# print(data)
return data
3.插入排序(Insertion Sort)
- 插入排序(Insertion Sort):
- 插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法沟优。它的工作原理是通過構(gòu)建有序序列,對于未排序數(shù)據(jù)睬辐,在已排序序列中從后向前掃描挠阁,找到相應(yīng)位置并插入。插入排序在實現(xiàn)上溯饵,通常采用in-place排序(即只需用到O(1)的額外空間的排序)侵俗,因而在從后向前掃描過程中,需要反復(fù)把已排序元素逐步向后挪位丰刊,為最新元素提供插入空間隘谣。
- 算法描述:
- 一般來說,插入排序都采用in-place在數(shù)組上實現(xiàn)啄巧。具體算法描述如下:
- 1.從第一個元素開始寻歧,該元素可以認為已經(jīng)被排序;
- 2.取出下一個元素秩仆,在已經(jīng)排序的元素序列中從后向前掃描码泛;
- 3.如果該元素(已排序)大于新元素,將該元素移到下一位置澄耍;
- 4.重復(fù)步驟3噪珊,直到找到已排序的元素小于或者等于新元素的位置;
- 5.將新元素插入到該位置后齐莲;
- 6.重復(fù)步驟2~5痢站。
- 一般來說,插入排序都采用in-place在數(shù)組上實現(xiàn)啄巧。具體算法描述如下:
-
算法演示
- 算法實現(xiàn)
def insert_sort(data):
"""
:param data:list
:return: sorted data
"""
length = len(data)
if length < 2:
return data
for i in range(0, length - 1):
preIndex = i
current = data[i + 1]
while preIndex >= 0 and current < data[preIndex]:
# 增加空間
data[preIndex + 1] = data[preIndex]
preIndex -= 1
data[preIndex + 1] = current
# print(data)
return data
4.希爾排序(Shell Sort)
- 希爾排序(Shell Sort):
- 希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序铅搓,它是簡單插入排序經(jīng)過改進之后的一個更高效的版本瑟押,也稱為縮小增量排序,同時該算法是沖破O(n2)的第一批算法之一星掰。它與插入排序的不同之處在于多望,它會優(yōu)先比較距離較遠的元素嫩舟。希爾排序又叫縮小增量排序。希爾排序是把記錄按下表的一定增量分組怀偷,對每組使用直接插入排序算法排序家厌;隨著增量逐漸減少,每組包含的關(guān)鍵詞越來越多椎工,當(dāng)增量減至1時饭于,整個文件恰被分成一組,算法便終止维蒙。
- 算法描述:
- 我們來看下希爾排序的基本步驟掰吕,在此我們選擇增量gap=length/2,縮小增量繼續(xù)以gap = gap/2的方式颅痊,這種增量選擇我們可以用一個序列來表示殖熟,{n/2,(n/2)/2...1},稱為增量序列斑响。希爾排序的增量序列的選擇與證明是個數(shù)學(xué)難題菱属,我們選擇的這個增量序列是比較常用的,也是希爾建議的增量舰罚,稱為希爾增量纽门,但其實這個增量序列不是最優(yōu)的。此處我們做示例使用希爾增量营罢。先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序赏陵,具體算法描述:選擇一個增量序列t1,t2愤钾,…瘟滨,tk候醒,其中ti>tj能颁,tk=1;按增量序列個數(shù)k倒淫,對序列進行k 趟排序伙菊;每趟排序,根據(jù)對應(yīng)的增量ti敌土,將待排序列分割成若干長度為m 的子序列镜硕,分別對各子表進行直接插入排序。僅增量因子為1 時返干,整個序列作為一個表來處理兴枯,表長度即為整個序列的長度。
-
算法演示
- 算法實現(xiàn)
def shell_sort(data):
"""
:param data: list
:return: sorted data
"""
length = len(data)
if length < 2:
return data
step = length // 2
while step > 0:
for i in range(step, length):
while i >= step and data[i - step] > data[i]:
data[i - step], data[i] = data[i], data[i - step]
i -= step
# print(data)
step = step // 2
return data
5.歸并排序(Merge Sort)
-
歸并排序(Merge Sort):
- 歸并排序是建立在歸并操作上的一種有效的排序算法矩欠。該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用财剖。將已有序的子序列合并悠夯,得到完全有序的序列;即先使每個子序列有序躺坟,再使子序列段間有序沦补。若將兩個有序表合并成一個有序表,稱為2-路歸并咪橙。
-
算法描述:
- 1.把長度為n的輸入序列分成兩個長度為n/2的子序列夕膀;
- 2.對這兩個子序列分別采用歸并排序;
- 3.將兩個排序好的子序列合并成一個最終的排序序列美侦。
-
算法演示
- 算法實現(xiàn)
def merge_sort(data):
"""
:param data: list
:return: sorted data
"""
length = len(data)
if length < 2:
return data
mid = length // 2
# 分別對兩個子列表并歸排序
merge_left = merge_sort(data[:mid])
merge_right = merge_sort(data[mid:])
def merge(data_left, data_right):
# print(data_left, data_right)
"""
合并(將兩個有序的列表合并成一個有序的列表)
:param data_left: data[:mid]
:param data_right:data[mid:]
:return: sorted data
"""
left, right = 0, 0
len_left = len(data_left)
len_right = len(data_right)
sorted_data = []
while left < len_left and right < len_right:
if data_left[left] < data_right[right]:
sorted_data.append(data_left[left])
left += 1
else:
sorted_data.append(data_right[right])
right += 1
sorted_data += data_left[left:]
sorted_data += data_right[right:]
return sorted_data
# 合并(將兩個有序的列表合并成一個有序的列表)
return merge(merge_left, merge_right)
6.快速排序(Quick Sort)
-
快速排序(Quick Sort):
- 快速排序的基本思想:通過一趟排序?qū)⒋庞涗浄指舫瑟毩⒌膬刹糠植瑁渲幸徊糠钟涗浀年P(guān)鍵字均比另一部分的關(guān)鍵字小,則可分別對這兩部分記錄繼續(xù)進行排序菠剩,以達到整個序列有序庞瘸。
-
算法描述:
- 快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體算法描述如下:
- 1.從數(shù)列中挑出一個元素赠叼,稱為 “基準(zhǔn)”(pivot)擦囊;
- 2.重新排序數(shù)列,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面嘴办,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一 邊)瞬场。在這個分區(qū)退出之后,該基準(zhǔn)就處于數(shù)列的中間位置涧郊。這個稱為分區(qū)(partition)操作贯被;
- 3.遞歸地(recursive)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序。
- 快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體算法描述如下:
-
算法演示
- 算法實現(xiàn)
def quick_sort(data):
"""
:param data:list
:return:sorted data
"""
length = len(data)
if length < 2:
return data
# 隨機基準(zhǔn)
import random
index = random.randint(0, length - 1)
left = [l for l in data[index + 1:] + data[0:index] if l <= data[index]]
right = [r for r in data[index + 1:] + data[0:index] if r > data[index]]
# 有序數(shù)據(jù) = 基準(zhǔn)左側(cè) + 基準(zhǔn) + 基準(zhǔn)右側(cè)
return quick_sort(left) + [data[index]] + quick_sort(right)
快速排序妆艘,一行代碼實現(xiàn)(為了方便查閱彤灶,用了換行)
one_quick_sort = (lambda array: array if len(array) <= 1 else
one_quick_sort([item for item in array[1:] if item <= array[0]]) + # 小于基準(zhǔn)
[array[0]] + # 等于基準(zhǔn)
one_quick_sort([item for item in array[1:] if item > array[0]])) # 大于基準(zhǔn)
7.堆排序(Heap Sort)
-
堆排序(Heap Sort):
- 堆排序(Heapsort)是指利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計的一種排序算法。堆積是一個近似完全二叉樹的結(jié)構(gòu)批旺,并同時滿足堆積的性質(zhì):即子結(jié)點的鍵值或索引總是小于(或者大于)它的父節(jié)點幌陕。
-
算法描述:
- 1.將初始待排序關(guān)鍵字序列(R1,R2….Rn)構(gòu)建成大頂堆,此堆為初始的無序區(qū)汽煮;
- 2.將堆頂元素R1與最后一個元素Rn交換搏熄,此時得到新的無序區(qū)(R1,R2,……Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2…n-1]<=Rn;
- 3.由于交換后新的堆頂R1可能違反堆的性質(zhì)暇赤,因此需要對當(dāng)前無序區(qū)(R1,R2,……Rn-1)調(diào)整為新堆心例,然后再次將R1與無序區(qū)最后一個元素交換,得到新的無序區(qū)(R1,R2….Rn-2)和新的有序區(qū)(Rn-1,Rn)鞋囊。不斷重復(fù)此過程直到有序區(qū)的元素個數(shù)為n-1止后,則整個
排序過程完成。
-
算法演示
- 算法實現(xiàn)
def heap_sort(data):
"""
:param data:list
:return:sorted data
"""
length = len(data)
if length < 2:
return data
def adjustHeap(i):
"""調(diào)整使之成為最大堆"""
maxIndex = i
if i * 2 < length and data[i * 2] > data[maxIndex]:
maxIndex = i * 2
if i * 2 + 1 < length and data[i * 2 + 1] > data[maxIndex]:
maxIndex = i * 2 + 1
if maxIndex != i:
data[maxIndex], data[i] = data[i], data[maxIndex]
adjustHeap(maxIndex)
def buildMaxHeap():
"""建立最大堆"""
# 從最后一個非葉子節(jié)點開始向上構(gòu)造最大堆
i = (length - 1) // 2
while i >= 0:
adjustHeap(i)
i -= 1
# 構(gòu)建一個最大堆
buildMaxHeap()
# 循環(huán)將堆首位(最大值)與末位交換溜腐,然后再重新調(diào)整最大堆
while length > 0:
data[0], data[length - 1] = data[length - 1], data[0]
length -= 1
adjustHeap(0)
# print(data)
return data
8.計數(shù)排序(Counting Sort)
-
計數(shù)排序(Counting Sort):
- 計數(shù)排序的核心在于將輸入的數(shù)據(jù)值轉(zhuǎn)化為鍵存儲在額外開辟的數(shù)組空間中译株。 作為一種線性時間復(fù)雜度的排序微饥,計數(shù)排序要求輸入的數(shù)據(jù)必須是有確定范圍的整數(shù)。計數(shù)排(Counting sort)是一種穩(wěn)定的排序算法古戴。計數(shù)排序使用一個額外的數(shù)組C欠橘,其中第i個元素是待排序數(shù)組A中值等于i的元素的個數(shù)。然后根據(jù)數(shù)組C來將A中的元素排到正確的位置现恼。它只能對整數(shù)進行排序肃续。
-
算法描述:
- 1.找出待排序的數(shù)組中最大和最小的元素;
- 2.統(tǒng)計數(shù)組中每個值為i的元素出現(xiàn)的次數(shù)叉袍,存入數(shù)組C的第i項始锚;(i必須為正整數(shù))
- 3.對所有的計數(shù)累加(從C中的第一個元素開始,每一項和前一項相加)喳逛;
- 4.反向填充目標(biāo)數(shù)組:將每個元素i放在新數(shù)組的第C(i)項瞧捌,每放一個元素就將C(i)減去1。
-
算法演示
- 算法實現(xiàn)
def count_sort(data):
"""
:param data:list
:return:sorted data
"""
length = len(data)
if length < 2:
return data
# 數(shù)組C,None表示當(dāng)前位置沒有記錄數(shù)據(jù)信息
C = [None] * (max(data) - min(data) + 1)
# 將計數(shù)信息存入C數(shù)組中
for i in range(length):
C[data[i] - min(data)] += 1
# print(C)
# 從C數(shù)組中按序取出數(shù)值并返回
sorted_data = []
for index, value in enumerate(C):
# 判斷當(dāng)前位置是否記錄有信息
if value is not None:
sorted_data += [index + min(data)] * value
return sorted_data
9.桶排序(Bucket Sort)
-
桶排序(Bucket Sort):
- 桶排序是計數(shù)排序的升級版润文。它利用了函數(shù)的映射關(guān)系姐呐,高效與否的關(guān)鍵就在于這個映射函數(shù)的確定。
- 桶排序 (Bucket sort)的工作的原理:假設(shè)輸入數(shù)據(jù)服從均勻分布典蝌,將數(shù)據(jù)分到有限數(shù)量的桶里曙砂,每個桶再分別排序(有可能再
使用別的排序算法或是以遞歸方式繼續(xù)使用桶排序進行排序。
-
算法描述:
- 1.人為設(shè)置一個BucketSize骏掀,作為每個桶所能放置多少個不同數(shù)值(例如當(dāng)BucketSize==5時鸠澈,該桶可以存放{1,2,3,4,5}這幾
種數(shù)字,但是容量不限截驮,即可以存放100個3)笑陈; - 2.遍歷輸入數(shù)據(jù),并且把數(shù)據(jù)一個一個放到對應(yīng)的桶里去葵袭;
- 3.對每個不是空的桶進行排序涵妥,可以使用其它排序方法,也可以遞歸使用桶排序眶熬;
- 4.從不是空的桶里把排好序的數(shù)據(jù)拼接起來妹笆。
- 1.人為設(shè)置一個BucketSize骏掀,作為每個桶所能放置多少個不同數(shù)值(例如當(dāng)BucketSize==5時鸠澈,該桶可以存放{1,2,3,4,5}這幾
-
算法演示
- 算法實現(xiàn)
def bucket_sort(data, num=10):
"""
:param data:list
:param num:bucket num
:return:sorted data
"""
length = len(data)
if length < 2:
return data
min_value = min(data)
max_value = max(data)
import re
pattern = r'^[1-9]+[0-9]*$'
# 桶的數(shù)量块请,如果不匹配默認取10
num = num if num > 1 and re.match(pattern, str(num)) else 10
# 桶的空間
space = int((max_value - min_value) / num) + 1
# 初始化桶空間,注意:[[]] * num生成list的坑,兩者append方法結(jié)果不同
buckets = [[] for _ in range(num)]
# 將數(shù)據(jù)放到對應(yīng)的桶里面,難點:數(shù)據(jù)與桶的映射關(guān)系
for i in range(length):
# 數(shù)據(jù)與桶的映射關(guān)系
index = int((data[i] - min_value) / space)
# print(data[i], index)
buckets[index].append(data[i])
# print(buckets)
# 桶內(nèi)數(shù)據(jù)排序
# for n in buckets:
# print(n)
# n.sort()
# 按序取出桶內(nèi)的數(shù)據(jù)娜氏,組成有序序列
sorted_data = []
for n in buckets:
n.sort()
sorted_data += n
return sorted_data
10.基數(shù)排序(Radix Sort)
-
基數(shù)排序(Radix Sort):
- 基數(shù)排序也是非比較的排序算法,對每一位進行排序墩新,從最低位開始排序贸弥,復(fù)雜度為O(kn),為數(shù)組長度,k為數(shù)組中的數(shù)的最大的位數(shù)海渊;基數(shù)排序是按照低位先排序绵疲,然后收集哲鸳;再按照高位排序,然后再收集盔憨;依次類推徙菠,直到最高位。有時候有些屬性是有優(yōu)先級順序的郁岩,先按低優(yōu)先級排序婿奔,再按高優(yōu)先級排序。最后的次序就是高優(yōu)先級高的在前问慎,高優(yōu)先級相同的低優(yōu)先級高的在 前萍摊。基數(shù)排序基于分別排序如叼,分別收集冰木,所以是穩(wěn)定的。
-
算法描述:
- 1.取得數(shù)組中的最大數(shù)笼恰,并取得位數(shù)踊沸;
- 2.arr為原始數(shù)組,從最低位開始取每個位組成radix數(shù)組社证;
- 3.對radix進行計數(shù)排序(利用計數(shù)排序適用于小范圍數(shù)的特點)雕沿;
-
基數(shù)排序有兩種方法:
- 1.MSD 從高位開始進行排序
- 2.LSD 從低位開始進行排序
-
基數(shù)排序 vs 計數(shù)排序 vs 桶排序
- 這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差異:
- 基數(shù)排序:根據(jù)鍵值的每位數(shù)字來分配桶
- 計數(shù)排序:每個桶只存儲單一鍵值
- 桶排序:每個桶存儲一定范圍的數(shù)值
- 這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差異:
-
算法演示
- 算法實現(xiàn)
def radix_sort(data):
"""
:param data:list
:return:sorted data
"""
length = len(data)
if length < 2:
return data
# 最大位數(shù)
max_value = max(data)
max_digit = str(max_value).__len__()
# 將數(shù)據(jù)按照個位猴仑、十位审轮、百位。辽俗。疾渣。上的數(shù)字放到對應(yīng)編號的桶里
for i in range(max_digit):
# 初始化桶,因為每個位置數(shù)范圍0-9崖飘,故初始化10個桶
buckets = [[] for _ in range(10)]
for d in data:
index = int(d / (10 ** i) % 10)
buckets[index].append(d)
# 從桶中按序取出數(shù)據(jù)放回原數(shù)組
data = [d for b in buckets for d in b]
print(data)
return data
后話
其實Python有自帶的排序函數(shù),這里自己實現(xiàn)十大經(jīng)典排序榴捡,一方面提高Python語言熟練度,另一方面鍛煉自己的思維朱浴。
# 一句話搞定的事兒吊圾,何必呢。翰蠢。项乒。孔乙己:你知道“回”有多少種寫法嗎梁沧。檀何。。
print(sorted(data))
本文動圖來自網(wǎng)絡(luò),感謝網(wǎng)友們的分享频鉴。