亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Python > 正文

Python實現各種排序算法的代碼示例總結

2020-01-04 17:54:53
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了Python實現各種排序算法的代碼示例總結,其實Python是非常好的算法入門學習時的配套高級語言,需要的朋友可以參考下
 

在Python實踐中,我們往往遇到排序問題,比如在對搜索結果打分的排序(沒有排序就沒有Google等搜索引擎的存在),當然,這樣的例子數不勝數?!稊祿Y構》也會花大量篇幅講解排序。之前一段時間,由于需要,我復習了一下排序算法,并用Python實現了各種排序算法,放在這里作為參考。

最簡單的排序有三種:插入排序,選擇排序和冒泡排序。這三種排序比較簡單,它們的平均時間復雜度均為O(n^2),在這里對原理就不加贅述了。貼出來源代碼。

插入排序:

def insertion_sort(sort_list):  iter_len = len(sort_list)  if iter_len < 2:    return sort_list  for i in range(1, iter_len):    key = sort_list[i]    j = i - 1    while j >= 0 and sort_list[j] > key:      sort_list[j+1] = sort_list[j]      j -= 1    sort_list[j+1] = key  return sort_list

冒泡排序:

def bubble_sort(sort_list):  iter_len = len(sort_list)  if iter_len < 2:    return sort_list  for i in range(iter_len-1):    for j in range(iter_len-i-1):      if sort_list[j] > sort_list[j+1]:        sort_list[j], sort_list[j+1] = sort_list[j+1], sort_list[j]  return sort_list

選擇排序:

def selection_sort(sort_list):  iter_len = len(sort_list)  if iter_len < 2:    return sort_list  for i in range(iter_len-1):    smallest = sort_list[i]    location = i    for j in range(i, iter_len):      if sort_list[j] < smallest:        smallest = sort_list[j]        location = j    if i != location:      sort_list[i], sort_list[location] = sort_list[location], sort_list[i]  return sort_list

這里我們可以看到這樣的句子:

sort_list[i], sort_list[location] = sort_list[location], sort_list[i]
不了解Python的同學可能會覺得奇怪,沒錯,這是交換兩個數的做法,通常在其他語言中如果要交換a與b的值,常常需要一個中間變量temp,首先把a賦給temp,然后把b賦給a,最后再把temp賦給b。但是在python中你就可以這么寫:a, b = b, a,其實這是因為賦值符號的左右兩邊都是元組(這里需要強調的是,在python中,元組其實是由逗號“,”來界定的,而不是括號)。

平均時間復雜度為O(nlogn)的算法有:歸并排序,堆排序和快速排序。

歸并排序。對于一個子序列,分成兩份,比較兩份的第一個元素,小者彈出,然后重復這個過程。對于待排序列,以中間值分成左右兩個序列,然后對于各子序列再遞歸調用。源代碼如下,由于有工具函數,所以寫成了callable的類:

class merge_sort(object):  def _merge(self, alist, p, q, r):    left = alist[p:q+1]    right = alist[q+1:r+1]    for i in range(p, r+1):      if len(left) > 0 and len(right) > 0:        if left[0] <= right[0]:          alist[i] = left.pop(0)        else:          alist[i] = right.pop(0)      elif len(right) == 0:        alist[i] = left.pop(0)      elif len(left) == 0:        alist[i] = right.pop(0)  def _merge_sort(self, alist, p, r):    if p<r:      q = int((p+r)/2)      self._merge_sort(alist, p, q)      self._merge_sort(alist, q+1, r)      self._merge(alist, p, q, r)  def __call__(self, sort_list):    self._merge_sort(sort_list, 0, len(sort_list)-1)    return sort_list

堆排序,是建立在數據結構——堆上的。關于堆的基本概念、以及堆的存儲方式這里不作介紹。這里用一個列表來存儲堆(和用數組存儲類似),對于處在i位置的元素,2i+1位置上的是其左孩子,2i+2是其右孩子,類似得可以得出該元素的父元素。

首先我們寫一個函數,對于某個子樹,從根節點開始,如果其值小于子節點的值,就交換其值。用此方法來遞歸其子樹。接著,我們對于堆的所有非葉節點,自下而上調用先前所述的函數,得到一個樹,對于每個節點(非葉節點),它都大于其子節點。(其實這是建立最大堆的過程)在完成之后,將列表的頭元素和尾元素調換順序,這樣列表的最后一位就是最大的數,接著在對列表的0到n-1部分再調用以上建立最大堆的過程。最后得到堆排序完成的列表。以下是源代碼:

class heap_sort(object):  def _left(self, i):    return 2*i+1  def _right(self, i):    return 2*i+2  def _parent(self, i):    if i%2==1:      return int(i/2)    else:      return i/2-1  def _max_heapify(self, alist, i, heap_size=None):    length = len(alist)    if heap_size is None:      heap_size = length    l = self._left(i)    r = self._right(i)    if l < heap_size and alist[l] > alist[i]:      largest = l    else:      largest = i    if r < heap_size and alist[r] > alist[largest]:      largest = r    if largest!=i:      alist[i], alist[largest] = alist[largest], alist[i]      self._max_heapify(alist, largest, heap_size)  def _build_max_heap(self, alist):    roop_end = int(len(alist)/2)    for i in range(0, roop_end)[::-1]:      self._max_heapify(alist, i)  def __call__(self, sort_list):    self._build_max_heap(sort_list)    heap_size = len(sort_list)    for i in range(1, len(sort_list))[::-1]:      sort_list[0], sort_list[i] = sort_list[i], sort_list[0]      heap_size -= 1      self._max_heapify(sort_list, 0, heap_size)    return sort_list

最后一種要說明的交換排序算法(以上所有算法都為交換排序,原因是都需要通過兩兩比較交換順序)自然就是經典的快速排序。

先來講解一下原理。首先要用到的是分區工具函數(partition),對于給定的列表(數組),我們首先選擇基準元素(這里我選擇最后一個元素),通過比較,最后使得該元素的位置,使得這個運行結束的新列表(就地運行)所有在基準元素左邊的數都小于基準元素,而右邊的數都大于它。然后我們對于待排的列表,用分區函數求得位置,將列表分為左右兩個列表(理想情況下),然后對其遞歸調用分區函數,直到子序列的長度小于等于1。

下面是快速排序的源代碼:

class quick_sort(object):  def _partition(self, alist, p, r):    i = p-1    x = alist[r]    for j in range(p, r):      if alist[j] <= x:        i += 1        alist[i], alist[j] = alist[j], alist[i]    alist[i+1], alist[r] = alist[r], alist[i+1]    return i+1  def _quicksort(self, alist, p, r):    if p < r:      q = self._partition(alist, p, r)      self._quicksort(alist, p, q-1)      self._quicksort(alist, q+1, r)  def __call__(self, sort_list):    self._quicksort(sort_list, 0, len(sort_list)-1)    return sort_list

細心的朋友在這里可能會發現一個問題,如果待排序列正好是順序的時候,整個的遞歸將會達到最大遞歸深度(序列的長度)。而實際上在操作的時候,當列表長度大于1000(理論值)的時候,程序會中斷,報超出最大遞歸深度的錯誤(maximum recursion depth exceeded)。在查過資料后我們知道,Python在默認情況下,最大遞歸深度為1000(理論值,其實真實情況下,只有995左右,各個系統這個值的大小也不同)。這個問題有兩種解決方案,1)重新設置最大遞歸深度,采用以下方法設置:

import syssys.setrecursionlimit(99999)

2)第二種方法就是采用另外一個版本的分區函數,稱為隨機化分區函數。由于之前我們的選擇都是子序列的最后一個數,因此對于特殊情況的健壯性就差了許多?,F在我們隨機從子序列選擇基準元素,這樣可以減少對特殊情況的差錯率。新的randomize partition函數如下:

def _randomized_partition(self, alist, p, r):  i = random.randint(p, r)  alist[i], alist[r] = alist[r], alist[i]  return self._partition(alist, p, r)

完整的randomize_quick_sort的代碼如下(這里我直接繼承之前的quick_sort類):

import randomclass randomized_quick_sort(quick_sort):  def _randomized_partition(self, alist, p, r):    i = random.randint(p, r)    alist[i], alist[r] = alist[r], alist[i]    return self._partition(alist, p, r)  def _quicksort(self, alist, p, r):    if p<r:      q = self._randomized_partition(alist, p, r)      self._quicksort(alist, p, q-1)      self._quicksort(alist, q+1, r)

關于快速排序的討論還沒有結束。我們都知道,Python是一門很優雅的語言,而Python寫出來的代碼是相當簡潔而可讀性極強的。這里就介紹快排的另一種寫法,只需要三行就能夠搞定,但是又不失閱讀性。(當然,要看懂是需要一定的Python基礎的)代碼如下:

def quick_sort_2(sort_list):  if len(sort_list)<=1:    return sort_list  return quick_sort_2([lt for lt in sort_list[1:] if lt<sort_list[0]]) + /      sort_list[0:1] + /      quick_sort_2([ge for ge in sort_list[1:] if ge>=sort_list[0]])

怎么樣看懂了吧,這段代碼出自《Python cookbook 第二版》,這種寫法展示出了列表推導的強大表現力。

對于比較排序算法,我們知道,可以把所有可能出現的情況畫成二叉樹(決策樹模型),對于n個長度的列表,其決策樹的高度為h,葉子節點就是這個列表亂序的全部可能性為n!,而我們知道,這個二叉樹的葉子節點不會超過2^h,所以有2^h>=n!,取對數,可以知道,h>=logn!,這個是近似于O(nlogn)。也就是說比較排序算法的最好性能就是O(nlgn)。

那有沒有線性時間,也就是時間復雜度為O(n)的算法呢?答案是肯定的。不過由于排序在實際應用中算法其實是非常復雜的。這里只是討論在一些特殊情形下的線性排序算法。特殊情形下的線性排序算法主要有計數排序,桶排序和基數排序。這里只簡單說一下計數排序。

計數排序是建立在對待排序列這樣的假設下:假設待排序列都是正整數。首先,聲明一個新序列list2,序列的長度為待排序列中的最大數。遍歷待排序列,對每個數,設其大小為i,list2[i]++,這相當于計數大小為i的數出現的次數。然后,申請一個list,長度等于待排序列的長度(這個是輸出序列,由此可以看出計數排序不是就地排序算法),倒序遍歷待排序列(倒排的原因是為了保持排序的穩定性,及大小相同的兩個數在排完序后位置不會調換),假設當前數大小為i,list[list2[i]-1] = i,同時list2[i]自減1(這是因為這個大小的數已經輸出一個,所以大小要自減)。于是,計數排序的源代碼如下。

class counting_sort(object):  def _counting_sort(self, alist, k):    alist3 = [0 for i in range(k)]    alist2 = [0 for i in range(len(alist))]    for j in alist:      alist3[j] += 1    for i in range(1, k):      alist3[i] = alist3[i-1] + alist3[i]    for l in alist[::-1]:      alist2[alist3[l]-1] = l      alist3[l] -= 1    return alist2  def __call__(self, sort_list, k=None):    if k is None:      import heapq      k = heapq.nlargest(1, sort_list)[0] + 1    return self._counting_sort(sort_list, k)

各種排序算法介紹完(以上的代碼都通過了我寫的單元測試),我們再回到Python這個主題上來。其實Python從最早的版本開始,多次更換內置的排序算法。從開始使用C庫提供的qsort例程(這個方法有相當多的問題),到后來自己開始實現自己的算法,包括2.3版本以前的抽樣排序和折半插入排序的混合體,以及最新的適應性的排序算法,代碼也由C語言的800行到1200行,以至于更多。從這些我們可以知道,在實際生產環境中,使用經典的排序算法是不切實際的,它們僅僅能做學習研究之用。而在實踐中,更推薦的做法應該遵循以下兩點:

當需要排序的時候,盡量設法使用內建Python列表的sort方法。
當需要搜索的時候,盡量設法使用內建的字典。
我寫了測試函數,來比較內置的sort方法相比于以上方法的優越性。測試序列長度為5000,每個函數測試3次取平均值,可以得到以下的測試結果:

Python實現各種排序算法的代碼示例總結

可以看出,Python內置函數是有很大的優勢的。因此在實際應用時,我們應該盡量使用內置的sort方法。

由此,我們引出另外一個問題。怎么樣判斷一個序列中是否有重復元素,如果有返回True,沒有返回False。有人會說,這不很簡單么,直接寫兩個嵌套的迭代,遍歷就是了。代碼寫下來應該是這樣。

def normal_find_same(alist):  length = len(alist)  for i in range(length):    for j in range(i+1, length):      if alist[i] == alist[j]:        return True  return False

這種方法的代價是非常大的(平均時間復雜度是O(n^2),當列表中沒有重復元素的時候會達到最壞情況),由之前的經驗,我們可以想到,利用內置sort方法極快的經驗,我們可以這么做:首先將列表排序,然后遍歷一遍,看是否有重復元素。包括完整的測試代碼如下:

import timeimport randomdef record_time(func, alist):  start = time.time()  func(alist)  end = time.time()  return end - startdef quick_find_same(alist):  alist.sort()  length = len(alist)  for i in range(length-1):    if alist[i] == alist[i+1]:      return True  return Falseif __name__ == "__main__":  methods = (normal_find_same, quick_find_same)  alist = range(5000)  random.shuffle(alist)  for m in methods:    print 'The method %s spends %s' % (m.__name__, record_time(m, alist))

運行以后我的數據是,對于5000長度,沒有重復元素的列表,普通方法需要花費大約1.205秒,而快速查找法花費只有0.003秒。這就是排序在實際應用中的一個例子。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美激情综合色综合啪啪五月| 久久久久久国产精品| 欧美激情亚洲另类| 蜜臀久久99精品久久久无需会员| 国产亚洲欧美一区| 欧美性视频在线| 日韩视频免费在线| 国产69久久精品成人| 欧美激情精品久久久久久免费印度| 成人xxxx视频| 91精品视频在线| 亚洲精品小视频在线观看| 成人欧美一区二区三区在线湿哒哒| 欧美xxxx做受欧美| 欧美日韩国产精品专区| 久久久久久久国产精品视频| 亚洲欧洲免费视频| 久久噜噜噜精品国产亚洲综合| 一区二区三区视频免费在线观看| 亚洲第一级黄色片| 在线观看国产精品日韩av| 97在线看免费观看视频在线观看| 欧美日韩精品在线播放| 国产国产精品人在线视| 亚洲级视频在线观看免费1级| 欧美老少配视频| 亚洲自拍在线观看| 国产成人久久久| 欧美精品在线视频观看| 亚洲国产精品成人av| 久精品免费视频| 精品国产1区2区| 欧美日韩在线另类| 91色琪琪电影亚洲精品久久| 亚洲最新在线视频| 91国内在线视频| 日韩中文字幕在线视频| 国产欧美日韩综合精品| 国产精品永久免费在线| 日韩成人在线免费观看| 中文字幕亚洲无线码在线一区| 国产精品亚洲片夜色在线| 亚洲高清久久网| 国产成人免费91av在线| 精品人伦一区二区三区蜜桃免费| 国内精品小视频| 色一情一乱一区二区| 亚洲在线免费观看| 狠狠躁夜夜躁人人爽天天天天97| 亚洲人成电影网站| 亚洲成人精品久久久| 久久国产精品久久久久久| 欧美成人精品在线| 91精品国产高清久久久久久91| 亚洲mm色国产网站| 国产欧美精品日韩精品| 欧美老肥婆性猛交视频| 69久久夜色精品国产69| 欧美专区国产专区| 亚洲аv电影天堂网| 久久精品在线视频| 国产精品高潮粉嫩av| 91精品国产91久久久久| 亚洲一区二区免费在线| 亚洲欧洲日产国产网站| 国产精品视频一区二区高潮| 福利一区视频在线观看| 中文字幕亚洲欧美一区二区三区| 成人在线中文字幕| 国产精品看片资源| 98视频在线噜噜噜国产| 欧美乱人伦中文字幕在线| 国产亚洲成av人片在线观看桃| 欧美成人午夜影院| 亚洲伊人久久大香线蕉av| 国产中文日韩欧美| 国产精品久久久久久久久久久新郎| 日韩av在线网站| 97在线视频一区| 国产精品视频久久久久| 插插插亚洲综合网| 日韩电影在线观看永久视频免费网站| 欧美大全免费观看电视剧大泉洋| 日韩视频永久免费观看| 高清一区二区三区日本久| 日韩一级黄色av| 欧美极品xxxx| 中文字幕精品av| 久久久欧美一区二区| 久热精品在线视频| 97超级碰碰碰| 欧美大全免费观看电视剧大泉洋| 国产精品久久久久久中文字| 国产精品aaaa| 少妇高潮久久久久久潘金莲| 中文字幕日韩欧美精品在线观看| 夜夜嗨av一区二区三区免费区| 中文字幕亚洲二区| 亚洲欧美www| 欧美日韩国产成人在线| 国产一区香蕉久久| 欧美另类老女人| 欧美激情按摩在线| 黄色成人av网| 欧美亚洲另类激情另类| 亚洲女人天堂成人av在线| 成人网址在线观看| 日韩影视在线观看| 亚洲精品色婷婷福利天堂| 久久亚洲国产成人| 欧美亚洲成人网| 中文字幕日韩av| 欧美激情一二三| 日韩av在线网| 国产视频丨精品|在线观看| 深夜福利日韩在线看| 欧美孕妇孕交黑巨大网站| 欧美日韩亚洲一区二区| 按摩亚洲人久久| 久久国内精品一国内精品| 欧美丰满片xxx777| 伊人久久综合97精品| 国产福利视频一区| 亚洲美女在线观看| 国产成人精品视| 欧美激情在线观看视频| 国内精品久久久久久久| 欧美大秀在线观看| 午夜美女久久久久爽久久| 欧美午夜无遮挡| 国内精品一区二区三区四区| 成人免费视频在线观看超级碰| 日韩av123| 亚洲第一福利视频| 国产欧美精品一区二区| 亚洲97在线观看| 中文国产亚洲喷潮| 欧美一区二三区| 欧美一区二区三区……| 亚洲区免费影片| 91精品成人久久| 亚洲a区在线视频| 亚洲自拍高清视频网站| 97视频在线观看播放| 日韩中文字幕在线视频播放| 国内偷自视频区视频综合| 欧美精品免费在线| 一本一本久久a久久精品牛牛影视| 欧美日产国产成人免费图片| 中文字幕av日韩| 欧美日韩一区二区三区| 97久久精品人人澡人人爽缅北| 中文字幕日韩高清| 成人黄色在线观看| 伊人青青综合网站| 中文字幕亚洲精品| 国产精品中文字幕在线| 国产精品国产自产拍高清av水多| 欧美疯狂xxxx大交乱88av| 91高清免费视频| 性欧美xxxx| 成人国产精品av| 亚洲国产成人久久| 亚洲a成v人在线观看|