資料結構與演算法 java描述 第一章 演算法及其複雜度

喝一口七喜發表於2021-08-10

資料結構與演算法 java描述 筆記

第一章 演算法及其複雜度

演算法的定義

​ 在特定計算模型下,在資訊處理過程中為了解決某一類問題而設計的一個指令序列。

要素

  • 輸入:待處理的資訊,即對具體問題的描述。
  • 輸出:經過處理之後得到的資訊,即問題的答案
  • 確定性:任一演算法都可以描述為由若干種基本操作組成的序列。
  • 可行性:在相應的計算模型中,每一基本操作都可以實現,且能夠在常數時間內完成。
  • 有窮性:對於任何輸入,按照演算法,經過有窮次基本操作都可以得到正確的輸出。

演算法效能的分析與評價

問題規模、執行時間及時間複雜度

​ 為了簡化分析,我們通常只考慮輸入規模這一主要因素。

​ 如果將某一演算法為了處理規模為 n 的問題所需的時間記作 T(n),那麼隨著問題規模 n 的增長,執行時間 T(n)將如何增長?我們將 T(n) 稱作演算法的時間複雜度

漸進複雜度

​ 在評價演算法的執行時間時,我們往往可以忽略其在處理小規模問題時的效能,轉而關注其在處理足夠大規模問題時的效能,即所謂的漸進複雜度(Asmpototic complexity)。

大 O 記號

​ 如果存 在正常數 a、N 和一個函式 f(n),使得對於任何 n > N,都有

​ T(n) < a × f(n)

​ 我們就可以認為在 n 足夠大之後,f(n)給出了 T(n)的一個上界。

​ 對於這種情況,我們記之為 T(n) = O(f(n)) 這裡的 O 稱作“大 O 記號(Big-O notation)”。

大Ω記號

​ 如果存在正常數 a、N 和一個函式 g(n),使得對於任何 n > N,都有

​ T(n) > a × g(n)

​ 我們就可以認為在 n 足夠大之後,g(n)給出了 T(n)的一個下界。

​ 對於這種情況,我們記之為 T(n) = Ω(g(n)) 這裡的Ω稱作“大Ω記號(Big-Ω notation)”。

Θ記號

​ 如果存在正常數 a N,都有

​ a × h(n) < T(n) < b × h(n)

​ 我們就可以認為在 n 足夠大之後,h(n)給出了 T(n)的一個確界。

​ 對於這種情況,我們記之為 T(n) = Θ(h(n)) Θ記號是對演算法執行效率的一種準確估計⎯⎯對於規模為 n 的任意輸入,演算法的執行時間都與 Θ(h(n))同階。

空間複雜度

​ 演算法所需使用的儲存空間量,即演算法空間複雜度

​ 就漸進複雜度的意義而言,在任何一個演算法的任何一次執行過程中,其實際佔用的存 儲空間都不會多於其間執行的基本操作次數。

​ 引入時間複雜度的各種記號來度量演算法的空間複雜度。

演算法複雜度及其分析

O(1)⎯⎯取非極端元素

問題:給定整數子集S, +∞ > |S| = n ≥ 3,從中找出一個元素 a∈S,使得 a ≠ max(S)a ≠ min(S)。也就是說,在最大、最小者之外,取出任意一個數。

演算法:NonextremeElement(S[], n)
輸入:由n個整數構成的集合S
輸出:其中的任一非極端元素
{
	任取的三個元素x, y, z ∈ S; //既然S是集合,這三個元素必互異
	通過比較,找出其中的最小者min{x, y, z}和最大者max{x, y, z};
	輸出最小、最大者之外的那個元素;
} 

思路:

S 是有限集,故其中的最大、最小元素各有且僅有一個。

因此,無論 S 的規 模有多大,在前三個元素 S[0]、S[1]和 S[2]中,必包含至少一個非極端元素。

我們可以取 x = S[0]、y = S[1]和 z = S[ 2],這隻需執行三次基本操作,耗費 O(3)時間。

為了確定這三個元 素的大小次序,我們最多需要做三次比較(請讀者自己給出證明),也是 O(3)時間。

最後,輸出居中 的那個元素只需 O(1)時間。

執行時間為: T(n) = O(3) + O(3) + O(1) = O(7) = O(1)

O(logn)⎯⎯進位制轉換

問題:給定任一十進位制整數,將其轉換為三進製表示。比如

​ 23(10) = 212(3)

​ 101(10) = 10202(3)

演算法:BaseConversion(n)
輸入:十進位制整數n
輸出:n的三進製表示
{
	不斷迴圈,直到n = 0 {
 		輸出 n % 3; //取模
 		令 n = n/3; //整除
 	}
} 

以 101(10)為例思路:

​ 第一輪迴圈,輸出 101 mod 3 = 2,n = 100/3 = 33; 2

​ 第二輪迴圈,輸出 33 mod 3 = 0,n = 33/3 = 11; 0

​ 第三輪迴圈,輸出 11 mod 3 = 2,n = 11/3 = 3; 2

​ 第四輪迴圈,輸出 3 mod 3 = 0,n = 3/3 = 1; 0

​ 第五輪迴圈,輸出 1 mod 3 = 1,n = 1/3 = 0。 1

​ result=10202(3)

該演算法由若干次迴圈構成, 每一輪迴圈內部,都只需進行兩次基本操作(取模、整除)。

每經過一輪迴圈,n都至少減少至 1/3。於是,至多經過

\[1+[log3n] \]

次迴圈,即 可減小至 0。

因此,該演算法需要執行 O(2×(1+[log3n])) = O(log3n)時間。

鑑於大 O 記號的性質,我們通常會忽略對數函式的常底數。比如這裡的底數為常數 3,故通常 將上述複雜度記作 O(logn)。

O(n)⎯⎯陣列求和

問題:給定n個整數,計算它們的總和。

演算法:Sum(A[], n)
輸入:由n個整陣列成的陣列A[]
輸出:A[]中所有元素的總和
{
	令s = 0;
	對於每一個A[i],i = 0, 1, …, n-1
	 	令s = s + A[i]; 
	輸出s;
} 

思路

對s的初始化需要O(1)時間。

每一輪迴圈中只需進行一次累 加運算,這屬於基本操作,可以在O(1)時間內完成。

O(1) + O(1)×n = O(n+1) = O(n)

O(n\(^2\) )⎯⎯起泡排序

問題:氣泡排序

演算法:Bubblesort(S[], n)
輸入:n個元素組成的一個序列S[],每個元素由[0..n-1]之間的下標確定,元素之間可以比較大小
輸出:重新調整S[]中元素的次序,使得它們按照非降次序排列
{
	從S[0]和S[1]開始,依次檢查每一對相鄰的元素;
	只要它們位置顛倒,則交換其位置;
	反覆執行上述操作,直到每一對相鄰元素的次序都符合要求;
} 

思路:

為了對n個整數排序,該演算法的外迴圈最多需要做n輪。

經過第i輪迴圈,元素 S[n-i-1]必然就位,i = 0, 1, …, n-1。\(r\)

在第i輪外迴圈中,內迴圈需要做n-i-1 輪。

在每一輪內迴圈中, 需要做一次比較操作,另外至多需要做三次賦值操作,這些都屬於基本操作,可以在O(4)的時間內 完成。

\[T(n) = \sum_{i=0}^{n-1} {(n-i-1)} × O(4) = O(2n(n-1)) = O(2n^2–2n) \]

\[ \]

鑑於大 O 記號的特性,低次項可以忽略,常係數可以簡化為 1,故再次得到 T(n) = O(n^2 )

O(2\(^r\) )⎯⎯冪函式

問題:慮冪函式的計算

演算法:PowerBruteForce(r) 
輸入:非負整數r
輸出:冪2^r
{
 power = 1;
 while (0 < r--)
 	power = power * 2;
 return power;
} 

共需要做r次迭代,每次迭代只涉及常數次基本操作,故總共需要執行O(r)時間。

問題的輸入規模為n,故有O(r) = O(2\(^n\) )。

計算模型

  • 可解性

    ​ 現代意義上的電子計算機所對應的計算模型,就是所謂的圖靈機

  • 有效可解

    具體來說就是指存在某一演算法,能夠在多項式時間以內解決這一問題。反之,若某問題的任一 演算法都具有不低於指數的複雜度,則不是有效可解的

  • 下界

    在任何一種特定計算模型下,對於任一可有效解決的問題,任何演算法的時間複雜度都 不可能低於某一範圍,我們稱之為該問題在這一計算模型下的複雜度下界,或簡稱該問題的下界。

遞迴

當某個方法呼叫自己時,我們就稱之為遞迴呼叫(Recursive call )。

線性遞迴

​ 類方法的每個例項只能遞迴地呼叫自己至多一次.

​ 最後一次遞迴呼叫被稱作遞迴的“基底”,簡稱“遞迴基”。

性質:經過有限的時間後,它必須能夠終止。

線性遞迴式演算法都具有如下形式

  • ​ 檢測遞迴基。首先要檢測是否到達遞迴基,也就是最基本、最簡單的情況,在這些平凡情 況下無需做進一步遞迴呼叫。
  • ​ 遞迴處理。如果尚未遇到平凡的情況,則執行一次遞迴呼叫。通常,遞迴呼叫有多種可能, 此時需要經過進一步的檢測以判斷具體應按何種方式做遞迴呼叫。

遞迴演算法的複雜度分析

遞迴跟蹤法

​ 一種直觀的、可視的分析方法,就是將遞迴方法的執行過程表示為圖形的形式.方法的每一例項都對應於一個方框,其中註明了該例項呼叫的引數;若方法例項 M 呼叫方法例項 N,則在 M 與 N 對 應的方框之間新增一條有向聯線,指明呼叫與被呼叫的關係。

遞推方程法

對遞迴的模式進行歸納從而匯出關於複雜度函式的遞推方程,遞迴方程的解將給出演算法的複雜度。

二分遞迴

將一個大問題分解為兩個子問題,然後分別通過遞迴呼叫來求解,這種情 況稱作二分遞迴(Binary recursion )。

多分支遞迴

一個問題可能需要分解為不止兩個子問題,此時就要採用多分支遞迴(Multiple recursion)。

相關文章