第二章 演算法基礎

2021-07-23 00:14:33 字數 3642 閱讀 9925

引言

《演算法導論》在本章將向我們介紹乙個演算法設計和分析框架,在後續的章節也將在這個框架的基礎上來分許演算法。

名詞解釋:

(1):偽**

偽**就是以最清晰、最簡潔的表示方法來說明演算法,而忽略資料抽象、模組性和錯誤處理的問題

(2):迴圈不變式

每次迴圈從陣列a中取出第j個元素插入有序數列a[1…..j-1],然後遞增j,這樣a[1,j-1]的有序性始終保持,這就是所謂迴圈不變式。(可用於證明演算法的正確性)

(3):執行時間

指程式執行的運算元或步數。

關於迴圈不變式,須證明以下三個性質

(1):初始化:迴圈第一次迭代之前,迴圈不變式為真

(2):保持:如果迴圈在某次迭前,它為真,那麼下次迭代之前,它仍為真

(3):終止:在迴圈終止時,迴圈不變式提供了乙個有助於證明演算法正確性的性質。

上述的三個證明,類似數學的數學歸納法:

初始化為基本情況

保持為歸納步,

終止性不同於我們的歸納法,歸納法是無限的,但是它有終止條件

偽**的一些約定(參考部落格)

插入排序

(1)思路:

在乙個陣列中,把未排好序的第乙個元素取出來,作為乙個關鍵字,插入到已經排好序的數列中,插入到恰當的位置,使得插入該關鍵字後的數列依舊是有序的。

(2)偽**:

for j=2 to a.length

key=a[j]

//insert a[j] into the sorted sequence a[1...j-1]

i=j-1

while i>0 && a[i]>key

a[i+1]=a[i]

i=i-1

a[i+1]=key

證明插入排序的,三個性質的成立(1)初始化:當子陣列只有乙個元素時,迴圈不變式一定成立

(2)保持: 每次插入乙個數後得到仍然是乙個有序的數列,迴圈不變式成立

(3)終止:陣列是有長度的,迴圈一定會終止

分析演算法

ram模型:描述序列演算法所用資源及其代價的模型。(請參考)

分析插入排序演算法:

計算具有n個輸入值上的insertion-sort的執行時間t(n);

t(n)=c1n+c2(n-1)+c4(n-1)+c5(∑tj)+c6(∑(tj-1))+c7(∑(tj-1))+c8(n-1)

最好情況就是:輸入的陣列已經是有序的了,所以tj=1;

所以:t(n)=c1n+c2(n-1)+c4(n-1)+c5(n-1)+c8(n-1)

所以:t(n)=an+c,它為n的線性函式

最壞的情況就是:輸入陣列是逆序的,此時tj=j

所以有:∑tj=(n(n+1)/2)-1;

∑(tj-1)=n(n-1)/2

所以:t(n)=c1n+c2(n-1)+c4(n-1)+c5((n(n+1)/2)-1)+c6(n(n-1)/2)+c7(n(n-1)/2)+c8(n-1)

所以:它是n的二次函式

上面我們分析了插入演算法的最好和最壞情況,但是在餘下的章節中,我們將集中於最壞情況的執行時間,理由如下

(1):它給出了任何輸入的執行時間的上限,

(2):對某些演算法,最壞情況經常出現

(3):平均情況經常和最壞情況大致一樣差。

分治演算法

分治演算法的思路就是:把原問題分解為幾個規模較小但類似原問題的子問題,遞迴地求解這些子問題,然後再合併這些子問題解做為原問題的解。

分治模式在每層遞迴都要進行三個步驟

(1):把原問題劃分為多個子問題(原問題小規模時的例項。)。

(2):解決這些子問題。

(3):合併子問題的解。

歸併排序舉例

(1)分解:把待排序的n個序列,分為兩個數量相等的子串行

(2)解決:使用歸併排序遞迴的排序兩個子串行

(3)合併:把兩個已經排好序的子串行合併。

當待排序的子串行只有乙個元素時,開始「遞迴回公升」

歸併排序**

#include

#include

#include

using

namespace

std;

void print(int * num, int n)

void merge_array(int * num,int * b,int low,int mid,int heigh)

/*把殘餘的資料新增到臨時陣列中

*/while (j <= heigh)

b[k++] = num[j++];

while (i <= mid)

b[k++] = num[i++];

/*把排好序的陣列重新新增回原來的資料,這樣這段資料就是有序的了;

*/for (i = 0; i < k; i++)

num[low + i] = b[i];

}void mergesort(int * num,int *b,int first,int last)

}int main()

分析分治演算法就按照上述的遞迴是分析歸併演算法:首先我們是假設陣列的大小為2的冪,來簡化問題的分析,在這個假設下,每個步驟產生的規模剛好為n/2的兩個子串行,即a=b=2;

(1)當n=1時,t(n)=o(1);

(2)當n>1時,

分解:分解只是計算陣列的中間位置,故d(n)=o(1);

解決:遞迴求解兩個規模均為n/2的子問題,貢獻的時間為:2t(n/2)

合併:我們是要把兩個n/2的規模的子陣列合併為n大小的陣列,故需要時間為o(n)。

當我們把合併和分解時間相加時,將得到的是乙個一次函式,所以其執行時間為o(n);

在第四章我們將通過「主定理」,可以證明t(n)=o(nlog(n)),其中log是以2為底的對數。其實我們可以通過另外一種方式得到這個結論,就是通過構建乙個滿二叉樹的方式。

我們通過把每個分解步驟通過乙個二叉樹的形式展現,我們通過二叉樹的性質可以知道這個二叉樹的深度為:log(n)+1,其中log為以2為底對數,然後每層又貢獻多了時間為:cn,所以總代價為cnlog(n)+cn,然後我們忽略低階和常量c,就可以得到o(log(n)),其中log均為以2為底的對數。

第二章 演算法基礎

2.1 插入排序 insertion sort 時間複雜度 o n 對於少量元素的排序,是乙個有效的演算法。為什麼叫插入排序呢?可以模擬撲克牌整牌 將未排序的數字通過遍歷插入到已排好序的數字中的對應位置 如何實現呢 num j 1 key 插入 for int i 1 i n i printf n ...

第二章 演算法基礎

本章首先講了插入排序演算法,然後分析了插入排序演算法的時間複雜性,引出了分治法的設計思想,分析了分治演算法。1.插入排序 插入排序採用了增量演算法 在排序子陣列a 1.j 1 後,將單個元素a j 插入到子陣列的適當位置,產生排序好的子陣列a 1.j 1 package sort 23 import...

第二章 演算法

本章內容了解即可。如果大家對資料結構完全不了解,我建議你先去看一下b站上郝斌老師的課程。演算法是解決特定問題求解步驟的描述,在計算機中表現為指令的優先序列,並且每條指令表示乙個或多個操作。只聽資料結構課程,當然可以,但是聽完後你可能沒有什麼感覺,因為你不知道他是幹嘛的。但是如果配合演算法來講解,你就...