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