幾種經常使用的排序算法

什麼是算法

我想不少程序員恐怕誤解了「算法」的意義,一想到算法就是動態規劃,機器學習之類的高大名詞。算法其實就是數學中的「解題過程」,解題過程要求精確,考慮各類狀況,須要人看得懂。算法不須要你在鍵盤上選擇什麼編程語言實現,只須要在本子上詳細的寫出每個步驟就能夠了。程序員

算法真的很重要嗎?

我常常在社區裏看到有人說初級開發不須要懂算法,這是很是真切的,不少的業務構建都是很常規的套路,查個數據庫返回,沒有太多複雜的計算,哪須要什麼解題過程。面試

可是我想遇到稍微複雜一點的業務,或者想要系統運行得更流暢、更有性能的時候,咱們就會構思採起什麼樣的方法能讓系統跑得更快、更穩定,因而有了「分佈式算法」等架構方面的算法。有時候咱們發現某個響應很慢,可能就是某個算法的執行效率過慢,只是咱們不知道這也能稱爲算法?最多見的恐怕是多層遍歷,很容易致使效率很低的問題。因此在編程的時候要養成思考算法複雜度的習慣。算法

算法對於提升代碼的執行效率,對問題的抽象有很是大的幫助。算法學好了,在遇到同一類問題的時候不再用擠破腦殼來想了,可以更加容易的聯想到相關的算法解決問題。程序員要想編程更容易,不怕各類場景沒遇到過,學好算法是頗有必要的。數據庫

排序算法的運用很是普遍。各類語言都有本身內置的排序函數,在面試過程當中也常常會有排序算法的考題。總結幾種排序算法的實現。編程

這個問題的顯示錶示是:請詳細描述如何將一組數字按從小到大的順序排列。數組

我首先想到的是:bash

  1. 找出數組中最小的一個;
  2. 把這個數放到另外一數組的最後面;
  3. 把這個數從原來的數組中剔除;
  4. 重複

重複的過程一般涉及到遍歷和遞歸,上面這個解法叫「選擇排序」,用 Python 實現以下:架構

def select_sort(arr):
    new_arr = []
     # 重複
    for i in range(len(arr)):
        small_index = find_smallest(arr)
         # 把這個數從原來的數組中剔除;
        smallest = arr.pop(small_index)
         # 把這個數放到另外一數組的最後面;
        new_arr.append(smallest)
    return new_arr
       
def find_smallest(arr):
     """找出數組中最小的一個;"""
    smallest = arr[0]
    index = 0
    for e in range(1,len(arr)):
        if arr[e] < smallest:
            smallest = arr[e]
            index = e
    return index
複製代碼

能夠看出來,代碼實現基本上就是用編程語言寫出解題思路。因此不少編程進階書都提到一個解決問題的辦法就是離開鍵盤,去上個廁所,在紙上畫一畫。只要是解題思路很詳細,基本上是能夠用來當僞代碼使用的,能夠所有放入代碼的註釋當中。app

冒泡排序(Bubble Sort)

  1. 比較前一個數和後一個數,若是前比後大,對換他們的位置;
  2. 循環執行
def bubble_sort(arr):
    for i in range(len(arr) - 1):
        for j in range(len(arr) - i - 1):
            if arr[j] > arr[j + 1]:
                tmp = arr[j + 1]
                arr[j + 1] = arr[j]
                arr[j] = tmp
    return arr
複製代碼

快速排序

上面兩種算法要操做的步驟不少,當數組太多時就會形成性能太低,咱們能夠想辦法減小要操做的步驟,從而下降算法的複雜度,提升執行效率。減小步驟的不少算法都是將數據分紅幾部分來處理,也就是一般說的「分治」,從而不斷減小沒部分須要處理的步驟,選擇排序就是這樣一種算法: 1.選出第一個元素 2.遍歷每一個元素,也就是從第二個開始拿,若是比第一個元素小,放到一個新數組裏;若是比它大,放到另外一個數組; 3.對兩個新數組執行一樣的操做; 那何時不須要執行這樣的操做了呢?當數組的元素個數小於2的時候,不須要比較了,分治策略就結束。less

「分治」是一種很是常見的解題思路。由於它能不斷的將問題變成更簡單的問題,最後變成一個顯而易見的事。也就是說它有兩個條件:

  • 基準條件。也就是沒有辦法再分了,足夠簡單了。
  • 分治條件或者叫遞歸條件。可以進一步縮小須要解決的問題的規模。

分治法在算法中很是廣泛,不是由於他能下降算法的複雜度,而是他能一步步將複雜的問題變得愈來愈簡單,規模愈來愈小,最後變成一個超級簡單的問題,若是能進一步抽象這種過程,就能考執行一樣的抽象步驟解出來來。分治法常常和遞歸用在一塊兒,這就衍生了一種變成方式——函數式編程,若是能多接觸一些遞歸的案例,對於函數式變成和抽象是很是有幫助的。軟件設計就是講一個很是複雜的問題抽象的過程,因此掌握函數式編程和遞歸概念對於抽象能力和軟件設計應該是頗有幫助的。

下面實現快速排序:

def quick(arr):
    if len(arr) < 2:
        return arr
    else:
        base = arr[0]
        less = [i for i in arr[1:] if i < base]
        greater = [i for i in arr[1:] if i >= base]
        return quick(less) + [base] + quick(greater)
複製代碼

歸併排序

歸併排序和選擇排序同樣是一種分治遞歸策略:

  1. 從中間分紅兩組
  2. 將兩個已經排序好的列表進行合併,合併成的列表就是排序好的 那怎麼對上述兩個列表排序呢?對兩個列表再執行分組策略 何時不能繼續了呢?當元素個數小於 2 的時候

具體實現:

def merge_sort(arr):
    # divide to two
    if len(arr) < 2:
        return arr
    mid = int(len(arr)/2)
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    j = 0
    i = 0
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    # add the larger part both left and right
    result += left[i:]
    result += right[j:]
    return result
複製代碼

總結

這篇文章總結了 4 個經常使用的排序算法的實現,這幾個算法常常在面試中能夠用到,須要緊緊掌握。掌握基礎算法能讓程序員知道一些經常使用的設計套路,在真遇到這些算法的應用場景的那一天,不至於撓頭不知所措。經常使用的算法的複雜度能讓咱們把握程序設計的性能,在軟件出現性能瓶頸的時候能經過改善軟件的算法來優化。在文章中,我還說明了遞歸和函數式編程的重要性,軟件設計是一個抽象的過程,面向流程的編程很難讓人養成抽象性思惟,而函數式編程每每經過遞歸能將具體的問題進一步抽象,有助於培養咱們的軟件設計能力,這恐怕是之前的大牛和黑客都特別崇尚 Lisp 的緣由。

相關文章
相關標籤/搜索