算法——基礎

1、算法是什麼?

  算法(Algorithm):一個計算過程,解決問題的方法。python

  Niklaus Wirth說:「程序=數據結構+算法算法

  算法是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令,算法表明着用系統的方法描述解決問題的策略機制。也就是說,可以對必定規範的輸入,在有限時間內得到所要求的輸出。若是一個算法有缺陷,或不適合於某個問題,執行這個算法將不會解決這個問題。不一樣的算法可能用不一樣的時間、空間或效率來完成一樣的任務。一個算法的優劣能夠用空間複雜度與時間複雜度來衡量數據結構

  

一個算法應該具備如下七個重要的特徵:分佈式

  • ①有窮性(Finiteness):算法的有窮性是指算法必須能在執行有限個步驟以後終止;
  • ②確切性(Definiteness):算法的每一步驟必須有確切的定義;
  • ③輸入項(Input):一個算法有0個或多個輸入,以刻畫運算對象的初始狀況,所謂0個輸     入是指算法自己定出了初始條件;
  • ④輸出項(Output):一個算法有一個或多個輸出,以反映對輸入數據加工後的結果。沒       有輸出的算法是毫無心義的;
  • ⑤可行性(Effectiveness):算法中執行的任何計算步驟都是能夠被分解爲基本的可執行       的操做步,即每一個計算步均可以在有限時間內完成(也稱之爲有效性);
  • ⑥高效性(High efficiency):執行速度快,佔用資源少;
  • ⑦健壯性(Robustness):對數據響應正確。

2、時間複雜度

  時間複雜度:就是用來評估算法運行時間的一個式子(單位)。通常來講,時間複雜度高的算法比複雜度低的算法慢。函數

一、時間複雜度舉例說明

  類比生活的一些時間,估計時間:性能

  

  來講說下面這些代碼的時間複雜度是多少呢?spa

print('Hello World')       # O(1)

for i in range(n):         # O(n)
    print('Hello World')

for i in range(n):         # O(n^2)
    for j in range(n):
        print('Hello World')

for i in range(n):         # O(n^3)
    for j in range(n):
        for k in range(n):
            print('Hello World')

while n > 1:               # O(log2n)或者O(logn)
    print(n)
    n = n // 2

  當算法過程當中出現循環折半的時候,複雜度式子中會出現logn。3d

二、常見的算法時間複雜度(按照效率排序)

  大O簡而言之能夠認爲它的含義是「order of」(大約是)。對象

  Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<O(n2logn)< Ο(n^3)<…<Ο(2^n)<Ο(n!)blog

例如:

  

  由圖中咱們能夠看出,當 n 趨於無窮大時, O(nlogn) 的性能顯然要比 O(n^2) 來的高

  通常來講,只要算法中不存在循環語句,其時間複雜度就是 O(1)

而時間複雜度又分爲三種:

  • 最優時間複雜度 (Best-Case)
  • 平均時間複雜度 (Average-Case)
  • 最差時間複雜度 (Worst-Case)

  最差時間複雜度的分析給了一個在最壞狀況下的時間複雜度狀況,這每每比平均時間複雜度好計算,而最優時間複雜度通常沒什麼用,由於沒人會拿一些特殊狀況去評判這個算法的好壞。

三、時間複雜度總結

  • 時間複雜度是用來估計算法運行時間的一個式子(單位)。
  • 通常來講,時間複雜度高的算法比複雜度低的算法慢。
  • 常見的時間複雜度(按效率排序):O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^2logn)<O(n^3)
  • 複雜問題的時間複雜度(難解決的問題):O(n!)  O(2n)  O(nn)......

四、若是簡單快速判斷算法複雜度?

  1. 肯定問題規模n
  2. 循環減半過程——>logn
  3. k層關於n的循環——>nk

  複雜狀況根據算法執行過程來判斷。

3、空間複雜度

  空間複雜度:用來評估算法內存佔用大小的式子。

一、空間複雜度表示方式

  算法使用了幾個變量:O(1)

  算法使用了長度爲n的一維列表:O(n)

  算法使用了m行n列的二維列表:O(mn)

二、空間換時間

  算法寧肯佔用更多的內存也要讓時間變快,分佈式的運算也是一個空間換時間的過程。

4、回顧遞歸

一、遞歸的兩個特色

  • 調用自身
  • 結束條件

  由這兩個特色判斷函數是不是合法的遞歸:

# 沒有結束條件不是合法的遞歸
def func1(x):
    print(x)
    func1(x-1)


# 結束條件結束不了,不是合法的遞歸
def func2(x):
    if x>0:
        print(x)
        func2(x+1)


# 合法的遞歸: 先打印後遞歸
def func3(x):
    if x>0:
        print(x)
        func3(x-1)
func3(5)   # 5 4 3 2 1


# 合法的遞歸:先遞歸後打印,所以先打印最裏層的 1
def func4(x):
    if x>0:
        func4(x-1)
        print(x)
func4(5)  # 1 2 3 4 5

二、遞歸實例:漢諾塔問題 

  

  大焚天創造世界的時候作了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞着64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序從新擺放到另外一根柱子上。在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。64根柱子移動完畢之日就是世界毀滅之時。

(1)解決思路

n個盤子時:

  • 把n-1個圓盤從A通過C移動到B;
  • 把第n個圓盤從A移動到C;
  • 把n-1個小圓盤從B通過A移動到C

(2)問題圖解

  將n-1個盤子看作一個總體,把最後一個盤子看作一個總體。

  

  將n-1個盤子從A經C移動到B:

  

  把第n個盤子從A移動到C:

  

  把n-1個小圓盤從B通過A移動到C:

  

  能夠看到第一步、第三步就是比原問題規模小一的一樣問題,所以就是原問題遞歸的一個子問題。

(3)代碼實現

def hanoi(n, a, b, c):
    """
    漢諾塔問題
    :param n: 問題規模
    :param a: 從哪一個柱子
    :param b: 經哪一個柱子
    :param c: 到哪一個柱子
    :return:
    """
    if n > 0:
        hanoi(n-1, a, c, b)   # 將n-1個盤子從a通過c移動到b
        print("moving from %s to %s" % (a, c))  # 將剩餘的最後一個盤子從a移動到c
        hanoi(n-1, b, a, c)   # 將n-1個盤子從b通過a移動到c


n = int(input('請輸入漢諾塔的層數:'))
hanoi(n, "A柱", "B柱", "C柱")

"""
請輸入漢諾塔的層數:3
moving from A柱 to C柱
moving from A柱 to B柱
moving from C柱 to B柱
moving from A柱 to C柱
moving from B柱 to A柱
moving from B柱 to C柱
moving from A柱 to C柱
"""

(4)漢諾塔問題總結

  漢諾塔移動次數的遞推式:h(x)=2h(x-1)+1。

  h(64)=18446744073709551615

  假設婆羅門每秒搬一次盤子,總共須要5800億年。

相關文章
相關標籤/搜索