python 常見演算法

來源:互聯網
上載者:User

標籤:merge   第一個   快速   特殊   通過   result   子節點   bin   估算   

  python雖然具備很多進階模組,也是內建電池的程式設計語言,但是要想做一個合格的程式員,基本的演算法還是需要掌握,本文主要介紹列表的一些排序演算法

  遞迴是演算法中一個比較核心的概念,有三個特點,1 調用自身  2 具有結束條件  3 代碼規模逐漸減少

  舉例:以下四個函數只有兩個為遞迴

  func3和func4 但是輸出是不同的比如func3(5)輸出為5,4,3,2,1func4(5)輸出為1,2,3,4,5,有一個遞迴層級在裡面。

  兩個概念:時間複雜度和空間複雜度

  時間複雜度:用於體現演算法執行時間的快慢,用O表示。一般常用的有:幾次迴圈就為O(n幾次方)  迴圈減半的O(logn)

  空間複雜度:用來評估演算法記憶體佔用大小的一個式子,通常情況下會選擇使用空間換時間

 

  e.g 列表尋找:從列表中尋找指定元素

    輸入:列表、待尋找元素

    輸出:元素下標或未尋找到元素 

    version 1 順序尋找:從列表中的第一個元素開始,順序進行搜尋,直到找到為止,複雜度為O(n)

       version 2 二分尋找:從有序列表中,通過待查值與中間值比較,以減半的方式進行尋找,複雜度為O(logn)

    代碼如下:

list = [1,2,3,4,5,6,7,8,9]element = 7def ord_sear(list,element):    for i in range(0,len(list)):        if list[i] == element:            print(‘list[{0}]={1}‘.format(i,element))            return i    else:        print(‘not found‘)def bin_sear(list,element):    low = 0    high = len(list)-1    while low<=high:        mid = (low+high)//2        if element == list[mid]:            print(‘list[{0}]={1}‘.format(mid,element))            return mid        elif element > list[mid]:            low =mid +1        else:            high =mid -1    return Nonei = bin_sear(list,element)j = ord_sear(list,element)

  二分尋找雖然在時間複雜度上優於順序尋找,但是有比較苛刻的條件,即列表必須為有序的。下面將介紹列表排序:

  列表排序是編程中一個最基本的方法,應用情境非常廣泛,比如各大音樂、閱讀、電影、應用榜單等,雖然python為我們提供了許多排序的函數,但我們那排序來作為演算法的練習再好不過。

  首先介紹的是最簡單的三種排序方式:1 冒泡排序 2 選擇排序 3 插入排序

  冒泡排序:列表中每相鄰兩個如果順序不是我們預期的大小排列,則交換。時間複雜度O(n^2)

def bubble(list):    high = len(list)-1      #指定一個最高位    while high>0:        for i in range(0,high):                if list[i]>list[i+1]:   #如果比下一位大                    list[i],list[i+1] = list[i+1],list[i]   #交換位置        high -=1            #最高位減1    return list #返回列表print(bubble(list))

  最佳化一下:

list = [3,1,5,7,8,6,2,0,4,9]def bubble(list):    high = len(list)-1      #定一個最高位    for j in range(high,0,-1):        exchange = False    #交換的標誌,如果提前排好序可在完整遍曆前結束        for i in range(0,j):            if list[i]>list[i+1]:   #如果比下一位大                list[i],list[i+1] = list[i+1],list[i]   #交換位置                exchange = True #設定交換標誌        if exchange == False:            return list     # return list #返回列表print(bubble(list))

  選擇排序:一趟遍曆選擇最小的數放在第一位,再進行下一次遍曆直到最後一個元素。複雜度依然為O(n^2)

list = [3, 1, 5, 7, 8, 6, 2, 0, 4, 9]def choice(list):    for i in range(0,len(list)-1):        min_loc = i        for j in range(i+1,len(list)-1):            if list[min_loc]>list[j]:   #最小值遍曆比較                min_loc = j        list[i],list[min_loc] = list[min_loc],list[i]    return listprint(choice(list))

  插入排序:將列表分為有序區和無序區,最開始的有序區只有一個元素,每次從無序區選擇一個元素按大小插到有序區中

list = [3,1,5,7,8,6,2,0,4,9]def cut(list):    for i in range(1,len(list)-1):        temp = list[i]        for j in range(i-1,-1,-1):  #從有序區最大值開始遍曆            if list[j]>temp:    #如果待插入值小於有序區的值                list[j+1] = list[j] #向後挪一位                list[j] = temp  #將temp放進去    return listprint(cut(list))

  這三種排序方式時間複雜度都是O(n^2),不太高效,所以下面介紹幾種更高效的排序方式

  1 快速排序:好寫的排序裡最快的,快的排序裡最好寫的。步驟為1 提取 2 左右分開 3 遞迴調用

list = [3,1,5,7,8,6,2,0,4,9]left = 0right = len(list) - 1def partition(left,right,list):    temp = list[left]    while left < right:        while left<right and list[right]>temp:      #當右邊值較大時,值不動            right -=1        list[left]=list[right]          #否則移動到左邊        while left<right and list[left]<temp:            left +=1        list[right]=list[left]    list[left]=temp    return left     #返回leftright都可以,值是一樣的def quick_sort(left,right,list):    if left<right:      #迭代中斷        mid = partition(left,right,list)        #擷取中間位置        quick_sort(left,mid-1,list)     #小序列進一步迭代        quick_sort(mid+1,right,list)    #大序列進一步迭代    return list         #返回列表print(quick_sort(left,right,list))

  快排的時間複雜度最佳情況是O(nlogn),最差情況是O(n^2)

  下面要介紹堆排序了。在介紹堆排序之前先簡單提一下樹的概念:

  樹是一種資料結構(比如目錄),樹是一種可以遞迴的資料結構,相關的概念有根節點、葉子節點,樹的深度(高度),樹的度(最多的節點),孩子節點/父節點,子樹等。

  在樹中最特殊的就是二叉樹(度不超過2的樹),二叉樹又分為滿二叉樹和完全二叉樹,見:

  二叉樹的儲存方式有:1 鏈式儲存 2 順序儲存(列表)

  父節點和左孩子節點的編號下表的關係為 i  -->  2i+1,右孩子則是i  --> 2i+2  最後一個父節點為(len(list)//2-1)  由此可以通過父親找到孩子或相反。

  知道了樹就可以說說堆了,堆分為大根堆和小根堆,分別的定義為:一棵完全二叉樹,滿足任一節點都比其孩子節點大或者小。

  堆排序的過程:

    1. 建立堆
    2. 得到堆頂元素,為最值
    3. 去掉堆頂,將最後一個元素放到堆頂,進行再一次堆排序(迭代)
    4. 第二次的堆頂為第二最值
    5. 重複3,4直到堆為空白

  代碼為:

list = [3, 1, 5, 7, 8, 6, 2, 0, 4, 9]def sift(low, high, list):#low為父節點,high為最後的節點編號    i = low    j = 2 * i + 1       #子節點位置    temp = list[i]      #存放臨時變數    while j <= high:    #遍曆子節點到最後一個        if j < high and list[j] < list[j + 1]:#如果第二子節點大於第一子節點            j += 1              if temp < list[j]:      #如果父節點小於子節點的值            list[i] = list[j]   #父子交換位置            i = j               #進行下一次編號            j = 2 * i + 1        else:            break       #遍曆完畢退出    list[i] = temp      #歸還臨時變數def heap_sort(list):    n = len(list)    for i in range(n // 2 - 1, -1, -1): #從最後一個父節點開始        sift(i, n-1, list)#完成堆排序    for i in range(n - 1, -1, -1):#開始排出資料        list[0], list[i] = list[i], list[0]#首尾交換        sift(0, i - 1, list)    #進行新一輪堆排序    return listprint(heap_sort(list))

   歸併排序:假設列表中可以被分成兩個有序的子列表,如何將這兩個子列表合成為一個有序的列表成為歸併。

  原理如:

  代碼如下:

def merg(low,high,mid,list):    i = low    j = mid +1    list_temp = []      #定義臨時列表    while i <=mid and j <=high:        if list[i]<=list[j]:        #分別比較有序子列表元素的大小            list_temp.append(list[i])   #添加進臨時列表中            i +=1        else:            list_temp.append(list[j])            j +=1    while i <= mid:        list_temp.append(list[i])        i +=1    while j <= high:        list_temp.append(list[j])        j +=1    list[low:high+1]=list_temp  #將已完成排序的列表賦值給原列表相應位置def merge_sort(low,high,list):    if low < high:        mid = (low+high)//2 #二分法        merge_sort(low,mid,list)        merge_sort(mid+1,high,list)#遞迴調用,        merg(low,high,mid,list)    return listlist = [3,1,5,7,8,6,2,0,4,9]print(merge_sort(0,len(list)-1,list))

 version2 代碼量更少:

def MergeSort(lists):    if len(lists) <= 1:        return lists    num = int(len(lists) / 2)    left = MergeSort(lists[:num])    right = MergeSort(lists[num:])    return Merge(left, right)def 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 += 1        else:            result.append(right[r])            r += 1    result += right[r:]    result += left[l:]    return resultprint(MergeSort(list))

快排,堆排,歸併的總結:

  • 時間複雜度都是O(nlogn)
  • 快排<歸併<堆排(一般情況)
  • 快排的缺點:極端情況效率較低,可到O(n^2),歸併則是需要額外的開銷,堆排則在排序演算法中相對較慢

 

  

python 常見演算法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.