什麼是算法

1、算法的官方定義

  • 算法(Algorithm)
    • 一個有限指令集
    • 接受一些輸入(有些狀況下不須要輸入)
    • 產生輸出
    • 必定在有限步驟以後終止
    • 每一條指令必須
      • 有充分明確的目標,不能夠有歧義
      • 計算機能處理的範圍以內
      • 描述應不依賴於任何一種計算機語言以及具體的實現手段

2、例1:選擇排序算法的僞碼描述

/* 僞代碼描述 */
void SelectionSort (int List[], int N)
{ 將N個整數List[0]...List[N-1]進行非遞減排序;
從List[i]到List[N-1]中找最小元,並將其位置賦給MinPosition;
將未排序部分的最小元換到有序部分的最後位置;
}
}
/* c語言實現 */

void SelectionSort (int List[], int N)
{ /* 將N個整數List[0]...List[N-1]進行非遞減排序 */
  for (i=0; i<N; i++){
    MinPosition = ScanForMin(List, i, N-1);
      /* 從List[i]到List[N-1]中找最小元,並將其位置賦給MinPosition;*/
    Swap(List[i], List[MinPosition]);
    /* 將未排序部分的最小元換到有序部分的最後位置; */
}
}
# python語言實現

def selection_sort(lt, n):
  for i in range(n):
    min_position = scan_for_min(lt, i, n-1)
    swap(lt[i], lt[min_position])

抽象 ——算法

List到底是數組仍是鏈表(雖然看上去像數組)?數組

Swap用函數仍是用宏去實現?函數

3、什麼是好的算法

一般經過下面兩個指標衡量算法的好壞spa

3.1 空間複雜度S(n)

根據算法寫成的程序在執行時佔用存儲單元的長度。這個長度每每與輸入數據的規模有關。空間複雜度太高的算法可能致使使用的內存超限,形成程序非正常中斷。設計

3.2 時間複雜度T(n)

根據算法寫成的程序在執行時耗費時間的長度。這個長度每每也與輸入數據的規模有關。時間複雜度太高的低效算法可能致使咱們在有生之年都等不到運行結果code

3.3 0101-例2-空間複雜度

/* c語言實現 */

void PrintN (int N)
{if (N){
  PrintN(N - 1);
  printf("%d\n", N);
}
 return;
}
# python語言實現

def print_n(n: int):
  if n:
    print_n(n - 1)
    print(n)

首先內存記錄PrintN(100000)的狀態,但因爲是遞歸調用,會繼續記錄PrintN(99999)的狀態,因爲繼續在遞歸調用,會繼續記錄PrintN(99998)的狀態,……所以內存佔用超限。blog

3.4 0101-例3-時間複雜度

3.4.1 方法1

\[ f(x) = a_0+a_1x+\cdots+a_{n-1}x^{n-1}+a_nx^n \]排序

對於上述的多項式,咱們可使用如下代碼實現:遞歸

/* c語言實現 */

double f(int n, double a[], double x)
{int i;
 double p = a[0]
   for (i=1; i<=n; i++)
     p += (a[i] * pow(x, i));  /* pow會執行i次乘法 */
 return p;
}
# python語言實現

def f(n: int, a_list: list, x: float):
  p = a_list[0]
  for i in range(1, n):
    p += (a_list[i] * pow(x, i))
  return p

時間複雜度:
\[ \begin{aligned} & (1+2+\dots+n)= (n^2+n)/2 \\ & T(n) = C_1n^2+C_2n \end{aligned} \]

3.4.2 方法2

可是上述的方法極其複雜,咱們能夠對多項式進行以下化簡:
\[ f(x) = a_0+x(a_1+(x(\cdots(a_{n-1}+x(a_n))\cdots)) \]

/* c語言實現 */

double f(int n, double a[], double x)
{int i;
 double p = a[n];
 for (i=n; i>0; i--)
   p = a[i-1] + x*p;  /* 一次乘法 */
 return p
}
# python語言實現

def f(n: int, a_list: list, x: float):
  p = a_list[n]
  for i in range(0,n,-1):
    p = a_list[i-1] + x*p
  return p

時間複雜度:
\[ \begin{aligned} & (1+1+\dots+1)\quad\text{n次} \\ & T(n) = Cn \end{aligned} \]

綜上:在分析通常算法的效率時,咱們常常關注下面兩種複雜度

  • 最壞狀況複雜度 \(T_{worst}( n )\)
  • 平均複雜度 \(T_{avg}( n )\)

因爲平均複雜度的計算難度遠遠大於最壞狀況複雜度,所以一般考慮最快狀況複雜度,即\(T_{worst}(n)\geq{T_{avg}(n)}\)

4、算法複雜度的漸進表示

對於算法的複雜度,沒有必要求出一個精確值,只須要粗略的知道算法的增加趨勢便可。

  • \(T(n)=O(f(n))\)表示存在常數\(C>0,n_0>0\)使得當\(n\geq{n_0}\)時有\(T(n)\leq{C}f(n)\)
  • \(T(n)=\Omega(g(n))\)表示存在常數\(C>0,n_0>0\)使得當\(n\geq{n_0}\)時有\(T(n)\geq{C}g(n)\)
  • \(T(n)=\Theta(h(n))\)表示同時有\(T(n)=O(h(n))\)\(T(n)=\Omega(h(n))\)

分析算法效率時,咱們老是但願找到\(O\)時最大的上界;找到\(\Omega\)時最小的下界。

下面三張圖表示算法複雜度爲不一樣函數時對應的耗時:

綜上:一個專業的程序猿,設計了一個\(O(N^2)\)的算法,應該本能的想到是否能把他改進爲\(O(Nlog_N)\)的算法。

5、算法複雜度分析小竅門

  • 若兩段算法分別有複雜度\(T_1(n)=O(f_1(n))\)\(T_2(n)=O(f_2(n))\),則
    • \(T_1(n)+T_2(n))=max(O(f_1(n)),O(f_2(n)))\)
    • \(T_1(n)×T_2(n))=max(O(f_1(n)×f_2(n))\)
  • \(T(n)\)是關於\(n\)\(k\)階多項式,那麼\(T(n)=\Theta{(n^k)}\)

  • 一個for循環的時間複雜度等於循環次數乘以循環體代碼的複雜度
  • if-else結構的複雜度取決於if的條件判斷複雜度和兩個分支部分的複雜度,整體複雜度取三者中最大

相關文章
相關標籤/搜索