python数据结构与算法之排序
排序算法的穩(wěn)定性:
假設(shè)有一串?dāng)?shù)據(jù):(4,1)(3,1)(3,7)(5,6);要求按照第一個(gè)數(shù)排序,結(jié)果如下:
第一種:(3,1)(3,7)(4,1)(5,6)(3相同,維持原來(lái)的次序)
第二種:(3,7)(3,1)(4,1)(5,6)(3相同,次序被改變)
第一種是穩(wěn)定的。
冒泡排序(以從小到大排為例):
每次走一遍把最大的元素冒泡,排到最后。
''' 冒泡排序:它也可以用于之前講的鏈表什么的,只是交換部分稍微煩一點(diǎn),思想一樣。這里簡(jiǎn)單一點(diǎn) ,以數(shù)字為例 ''' def bubble_sort(alist):'''冒泡排序,參數(shù)為列表'''n = len(alist)-1for j in range(n):for i in range(n-j):if alist[i]>alist[i+1]:# 前一個(gè)大于后一個(gè),交換alist[i], alist[i+1] = alist[i+1], alist[i] # 這樣寫在python這種動(dòng)態(tài)語(yǔ)言中可以if __name__ == '__main__':a = [2,0,5,1,10]bubble_sort(a)print(a)冒泡排序的時(shí)間復(fù)雜度為:最壞可以認(rèn)為是O(n^2),穩(wěn)定的
改進(jìn):假如傳入的序列就是有序的,比如[1,2,3,4,5,6]。此時(shí)按照上面代碼還是要一步步比較,復(fù)雜度是一樣的。改進(jìn)之后,最優(yōu)時(shí)間復(fù)雜度為O(n),最壞時(shí)間復(fù)雜度不變。
def bubble_sort(alist):'''冒泡排序,參數(shù)為列表'''n = len(alist)-1for j in range(n):count = 0for i in range(n-j):if alist[i]>alist[i+1]:# 前一個(gè)大于后一個(gè),交換alist[i], alist[i+1] = alist[i+1], alist[i] # 這樣寫在python這種動(dòng)態(tài)語(yǔ)言中可以count += 1if count == 0:return選擇排序:
思想解釋:每次找到最小的值,與無(wú)序數(shù)中的一個(gè)數(shù)交換,比如:
a = [52,100,23,43,55,20,17,108]
找到最小值是17,將17與52交換,得:
a = [17,100,23,43,55,20,52,108]
看除了第一個(gè)數(shù)17外,其他最小的為20,與“第一個(gè)數(shù)”100交換:
a = [17,20,23,43,55,100,52,108]
此時(shí),前面兩個(gè)數(shù)已經(jīng)有序,以此往下。
def select_sort(alist):"""選擇排序,既然是研究數(shù)據(jù)結(jié)構(gòu)與算法,這里不用min()函數(shù)"""n = len(alist)for j in range(n-1):# 記錄最小值的位置,這里首次默認(rèn)是無(wú)序中的第一個(gè)min_index = jfor i in range(j+1,n):if alist[i]<alist[min_index]:min_index = ialist[j], alist[min_index] = alist[min_index], alist[j]選擇排序的時(shí)間復(fù)雜度:O(n^2),不穩(wěn)定
插入排序算法:
思想理解,與上面選擇排序有點(diǎn)雷士,其實(shí)還是將序列無(wú)形的分為兩部分。比如序列[52,100,23,43,55,20,17,108]。
將序列分為[52, ? ? ? ? ? ? ? ? ? ?? 100,23,43,55,20,17,108],第一部分是有序的。
然后將無(wú)序中的第一個(gè)100與有序中52比較,放在正確的位置[52,100, ? ? ? ? ? ? ? ? ? ?? 23,43,55,20,17,108],
同理接著比較23與[52,100],將其插入正確的位置[23,52,100, ? ? ? ? ? ? ? ? ? ? ? ? ?? 43,55,20,17,108]
?注意:插入的過(guò)程其實(shí)就是一個(gè)小排序,比如插入23時(shí),先與100比,然后與52........
def insert_sort(alist):"""插入排序"""for j in range(1,len(alist)):i = j# 從無(wú)序部分選一個(gè)插入到有序部分的過(guò)程while i>0:if alist[i]<alist[i-1]:alist[i], alist[i-1] = alist[i-1], alist[i]i -= 1else:# 因?yàn)橛行虿糠质怯行虻?#xff0c;只要前一個(gè)數(shù)比當(dāng)前數(shù)小,那前面所有的數(shù)都比當(dāng)前數(shù)小break最壞時(shí)間復(fù)雜度是O(n^2),最優(yōu)時(shí)間復(fù)雜度是O(n),穩(wěn)定的
希爾排序:
它其實(shí)就是插入排序的改進(jìn)版,思想百度百科一下就可以了。
簡(jiǎn)單介紹:它有一個(gè)gap(間隙),假設(shè)gap=4。原序列為54,26,93,17,77,31,44,55,20;
因?yàn)間ap=4,索引相差四的抽出,那原序列變成4層:
54, ? ? ? ? ? ? ? ? ?? 77 , ? ? ? ? ? ? 20
? ?? 26, ? ? ? ? ? ? ? ? ?? 31,
? ? ? ? ? 93, ? ? ? ? ? ? ? ? ?? 44
? ? ? ? ? ? ? 17 ? ? ? ? ? ? ? ? ? ? ?? 55
每一層排序后再插入回去,即經(jīng)過(guò)第一次排序之后:20,26,44,17,54,31,93,55,77;
再取gap=2,原序列變成兩層:
20 ? ? ? ?? 44 ? ? ? ? ? 54 ? ? ? ? ?? 93 ? ? ? ?? 77
? ? ? 26 ? ? ? ?? 17 ? ? ? ?? 31 ? ? ? ? ? ?? 55
同理每層子序列用插入算法,再取gap=1.得到最后的結(jié)果。
def shell_sort(alist):"""希爾排序,核心部分其實(shí)是插入排序"""n = len(alist)gap = n//2 # 其實(shí)有最優(yōu)的gap值,這里直接用長(zhǎng)度的一半取整# gap變化為0前,插入算法執(zhí)行的次數(shù)while gap>=1:# 內(nèi)層循環(huán)其實(shí)就是插入算法,與普通的插入算法的區(qū)別在于gapfor j in range(gap,n):i = jwhile i>0:if alist[i]<alist[i-gap]:alist[i], alist[i-gap] = alist[i-gap], alist[i]i -= gapelse:breakgap //= 2最壞時(shí)間復(fù)雜度O(n^2),最優(yōu)時(shí)間復(fù)雜度根據(jù)gap值不同的而不同(優(yōu)化問(wèn)題)。不穩(wěn)定
快速排序
具體思想介紹可以百度一下,其實(shí)有點(diǎn)像冒泡的改進(jìn)。舉個(gè)例子:
假設(shè)原序列為:54,26,93,17,77,31,44,55,20;
先找到第一個(gè)數(shù)54在有序情況下的位置,怎么找?設(shè)定兩個(gè)游標(biāo),游標(biāo)low先指向26,游標(biāo)high先指向最后一個(gè)數(shù)20.
當(dāng)low指向的數(shù)小于54時(shí),可以繼續(xù)向high的方向移動(dòng),否則先靜止;同理當(dāng)high指向的數(shù)大于54時(shí)可以向low的方向移動(dòng),否則靜止;
按照上面的要求,此時(shí)54,26,93(low),17,77,31,44,55,20(high);
卡主不動(dòng)了,這時(shí)候交換93,20的位置,數(shù)據(jù)變成54,26,20(low),17,77,31,44,55,93(high);這時(shí)不卡了,繼續(xù)移動(dòng)(先動(dòng)low),一直到low與high重合,如下:
54, ? ? ? ?? 26,20,17,31,44(low_and_high),77,55,93
此時(shí)確定54的位置:26,20,17,31,44, ? ? ? ? ? ? ? ?? 54 ? ? ? ? ? ? ? ? ? ? ,77,55,93同理處理54前后兩部分;
?
大致思想是這樣,為了方便編程,稍微變通一下(仍然是快速排序):
序列還是上面那個(gè),一開始low指向54,high指向20,設(shè)一個(gè)mid_value=54(把第一個(gè)數(shù)存起來(lái)),游標(biāo)還是往中間動(dòng);
high先動(dòng),能動(dòng)的條件一樣,此時(shí)20,high游標(biāo)不能動(dòng),就將20代替low指向的數(shù)(54早已存起來(lái))。變成20(low),26,93,17,77,31,44,55,空(high);
這時(shí)候low動(dòng)起來(lái),一直到:20,26,93(low),17,77,31,44,55,空(high)
替換:20,26,空(low),17,77,31,44,55,93(high);high動(dòng)
最終:20,26,44,17,31,空(low_high),77,55,93
讓空=54,20,26,44,17,31, ? ? ? ? ? 54, ? ? ? ? ?? 77,55,93,同理處理54兩邊。
1 def quick_sort(alist): 2 """快速排序,錯(cuò)誤示范,錯(cuò)誤的地方在下面遞歸""" 3 n = len(alist) 4 mid_value = alist[0] 5 low = 0 6 high = n-1 7 8 while low < high: 9 # high左移動(dòng)(等于的情況放一邊處理比較好) 10 while low < high and alist[high] >= mid_value: 11 high -= 1 12 alist[low] = alist[high] 13 # low右移 14 while low < high and alist[low] < mid_value: 15 low += 1 16 alist[high] = alist[low] 17 # 從while退出,low等于high 18 alist[low] = mid_value 19 20 # mid_walue兩邊處理方式與上面一樣 21 quick_sort(alist[:low-1]) #不能切片傳,這等于傳一個(gè)新的列表了,也就是說(shuō)操作的不是一個(gè)列表 22 quick_sort(alist[low+1:]) 錯(cuò)誤示范 def quick_sort(alist, first, last):"""快速排序:param alist: 列表:param first: 列表的第一個(gè)元素索引,0:param last: 列表最后一個(gè)元素,len()-1:return:"""# 遞歸的終止條件if first >= last:returnmid_value = alist[first]low = firsthigh = lastwhile low < high:# high左移動(dòng)(等于的情況放一邊處理比較好)while low < high and alist[high] >= mid_value:high -= 1alist[low] = alist[high]# low右移while low < high and alist[low] < mid_value:low += 1alist[high] = alist[low]# 從while退出,low等于highalist[low] = mid_value# mid_walue兩邊處理方式與上面一樣quick_sort(alist, first, low-1)quick_sort(alist, low+1, last)if __name__ == '__main__':b = [54,26,93,17,77,31,44,55,20]quick_sort(b,0,len(b)-1)print(b)快速排序的最優(yōu)時(shí)間復(fù)雜度O(nlogn),最壞時(shí)間復(fù)雜度O(n^2),不穩(wěn)定。
歸并排序:
思想:百度一下就好
def Merge_Sort(lists):if len(lists) <= 1:return listsmid = int(len(lists) / 2)# left 采用歸并排序后形成的有序的新的列表left = Merge_Sort(lists[:mid])# right 采用歸并排序后形成的有序的新的列表right = Merge_Sort(lists[mid:])# 將兩個(gè)有序的子序列合并# Merge(left, right)r, l=0, 0 # 為兩個(gè)指針result=[]while l<len(left) and r<len(right):if left[l] < right[r]:result.append(left[l])l += 1else:result.append(right[r])r += 1result += list(left[l:])result += list(right[r:])return resultif __name__ == '__main__':b = [54,26,93,17,77,31,44,55,20]b = Merge_Sort(b)print(b)上面代碼遞歸有點(diǎn)復(fù)雜,執(zhí)行流程可看https://www.bilibili.com/video/av21540971/?p=35
最壞與最優(yōu)時(shí)間復(fù)雜度都是:O(nlogn),穩(wěn)定。
堆排序沒介紹,可以百度一下,快速排序用的比較多。
?
轉(zhuǎn)載于:https://www.cnblogs.com/maxiaonong/p/10521552.html
總結(jié)
以上是生活随笔為你收集整理的python数据结构与算法之排序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: pageadminCMS.Net Fra
- 下一篇: Python用起来极度舒适的强大背后