資料結構和演算法詳解(三) 遞迴 排序 雜湊表

2021-09-28 20:47:32 字數 4006 閱讀 1657

一、什麼是遞迴?

1.遞迴是一種非常高效、簡潔的編碼技巧,一種應用非常廣泛的演算法,比如dfs深度優先搜尋、前中後序二叉樹遍歷等都是使用遞迴。

2.方法或函式呼叫自身的方式稱為遞迴呼叫,呼叫稱為遞,返回稱為歸。

3.基本上,所有的遞迴問題都可以用遞推公式來表示,比如

f(n) = f(n-1) + 1; 

f(n) = f(n-1) + f(n-2);

f(n)=n*f(n-1);

二、為什麼使用遞迴?遞迴的優缺點?

1.優點:**的表達力很強,寫起來簡潔。

2.缺點:空間複雜度高、有堆疊溢位風險、存在重複計算、過多的函式呼叫會耗時較多等問題。

三、什麼樣的問題可以用遞迴解決呢?

乙個問題只要同時滿足以下3個條件,就可以用遞迴來解決:

1.問題的解可以分解為幾個子問題的解。何為子問題?就是資料規模更小的問題。

2.問題與子問題,除了資料規模不同,求解思路完全一樣

3.存在遞迴終止條件

四、如何實現遞迴?

1.遞迴**編寫

寫遞迴**的關鍵就是找到如何將大問題分解為小問題的規律,並且基於此寫出遞推公式,然後再推敲終止條件,最後將遞推公式和終止條件翻譯成**。

2.遞迴**理解

對於遞迴**,若試圖想清楚整個遞和歸的過程,實際上是進入了乙個思維誤區。

那該如何理解遞迴**呢?如果乙個問題a可以分解為若干個子問題b、c、d,你可以假設子問題b、c、d已經解決。而且,你只需要思考問題a與子問題b、c、d兩層之間的關係即可,不需要一層層往下思考子問題與子子問題,子子問題與子子子問題之間的關係。遮蔽掉遞迴細節,這樣子理解起來就簡單多了。

因此,理解遞迴**,就把它抽象成乙個遞推公式,不用想一層層的呼叫關係,不要試圖用人腦去分解遞迴的每個步驟。

遞迴的關鍵是終止條件

五、遞迴常見問題及解決方案

1.警惕堆疊溢位:可以宣告乙個全域性變數來控制遞迴的深度,從而避免堆疊溢位。

2.警惕重複計算:通過某種資料結構來儲存已經求解過的值,從而避免重複計算。

六、如何將遞迴改寫為非遞迴**?

籠統的講,所有的遞迴**都可以改寫為迭代迴圈的非遞迴寫法。如何做?抽象出遞推公式、初始值和邊界條件,然後用迭代迴圈實現。

一、排序方法與複雜度歸類

(1)幾種最經典、最常用的排序方法:氣泡排序、插入排序、選擇排序、快速排序、歸併排序、計數排序、基數排序、桶排序。

(2)複雜度歸類

o(n^2):氣泡排序、插入排序、選擇排序

o(nlogn):快速排序、歸併排序                   

o(n):計數排序、基數排序、桶排序   

二、如何分析乙個「排序演算法」?

<1>演算法的執行效率

1. 最好、最壞、平均情況時間複雜度。

2. 時間複雜度的係數、常數和低階。

3. 比較次數,交換(或移動)次數。

<2>排序演算法的穩定性

1. 穩定性概念:如果待排序的序列中存在值相等的元素,經過排序之後,相等元素之間原有的先後順序不變。

2. 穩定性重要性:可針對物件的多種屬性進行有優先順序的排序。

3. 舉例:給電商交易系統中的「訂單」排序,按照金額大小對訂單資料排序,對於相同金額的訂單以下單時間早晚排序。用穩定排序演算法可簡潔地解決。先按照下單時間給訂單排序,排序完成後用穩定排序演算法按照訂單金額重新排序。

<3>排序演算法的記憶體損耗

原地排序演算法:特指空間複雜度是o(1)的排序演算法。

常見的排序演算法:

氣泡排序只會操作相鄰的兩個資料。每次冒泡操作都會對相鄰的兩個元素進行比較,看是否滿足大小關係要求,如果不滿足就讓它倆互換。

}插入排序將陣列資料分成已排序區間和未排序區間。初始已排序區間只有乙個元素,即陣列第乙個元素。在未排序區間取出乙個元素插入到已排序區間的合適位置,直到未排序區間為空。

**:

//插入排序

選擇排序將陣列分成已排序區間和未排序區間。初始已排序區間為空。每次從未排序區間中選出最小的元素插入已排序區間的末尾,直到未排序區間為空。

void selectionsort(int arr,int n)

}// 申請乙個計數陣列c,下標大小[0,max]

int c = new int[max + 1];

for (int i = 0; i < max + 1; ++i)

// 計算每個元素的個數,放入c中

for (int i = 0; i < n; ++i)

// 依次累加

for (int i = 1; i < max + 1; ++i)

// 臨時陣列r,儲存排序之後的結果

int r = new int[n];

// 計算排序的關鍵步驟了,有點難理解

for (int i = n - 1; i >= 0; --i)

// 將結果拷貝會a陣列

for (int i = 0; i < n; ++i)

}

什麼是雜湊表:

雜湊錶用的是陣列支援按照下標隨機訪問資料的特性,所以雜湊表其實就是陣列的一種擴充套件,由陣列演化而來。可以說,如果沒有陣列,就沒有雜湊表。

原理:雜湊錶用的就是陣列支援按照下標隨機訪問的時候,時間複雜度是0(1)的特性。我們通過雜湊函式把元素的鍵值對映為下標,然後將資料儲存在陣列中對應下標的位置。當我們按照鍵值查詢元素時,我們用同樣的雜湊函式,將鍵值轉化陣列標標,從對應的陣列下標的位置取資料。

雜湊函式的設計要求:

雜湊函式計算得到的雜湊值是乙個非負整數;.

如果key1 = key2,那hash(key1) == hash(key2);

如果key1 != key2,那hash(key1)  !=  hash(key2),

雜湊函式的設計不能太複雜,雜湊函式生成值要盡可能隨機並且均勻分布

如果不符合3 那麼就出現了雜湊衝突,雜湊衝突是無法避免的

解決雜湊衝突的方法有兩種: 

開放定址法(open addressing)和鍊錶法(chaining)

開放定址法:如果出現了雜湊衝突,我們就重新探測乙個空閒位置,將其插入。

裝在因子:  雜湊表中一定比例的空閒槽位。公式: 雜湊表的裝載因子 = 填入表中的元素個數 / 雜湊表的長度

裝載因子越大,說明空閒位置越少,衝突越多,雜湊表的效能會下降。

鍊錶法:

鍊錶法是一種更加常用的雜湊衝突解決辦法,相比開放定址法,它要簡單很多。我們來看這個圖,在雜湊表中,每個"桶(bucket) "或者"槽(slot) "會對應一條鍊錶,所有雜湊值相同的元素我們都放到相同槽位對應的鍊錶中。

資料結構和演算法 遞迴

標籤 空格分隔 資料結構和演算法 include int main return 0 include int fib int i int main 例計算 n 的階乘 n include int factorial n int main 例編寫乙個遞迴函式,實現將輸入的任意長度的字串反向輸出的功能。...

資料結構和演算法 遞迴

遞迴,其實就是自己呼叫自己,實現乙個不斷重複的工作,直接上 給你們講解一下,應該就明白遞迴是什麼東東了,遞迴其實也是一種迴圈,他也是有終止條件的,否則就變成了死迴圈,這個 的終止條件就是 因為這裡就沒有再次呼叫自己,所以就終止了。從這個 我們也可以看出來,這裡的遞迴就實現了抱著,我的,這兩個詞語的多...

資料結構和演算法(三)希爾排序

希爾排序是一種基於插入排序的排序演算法。對於大規模的亂序陣列,插入排序很慢,因為它只會交換相鄰的元素,因此元素只能一點一點從陣列的一端移動到另一端。希爾排序為了加快速度簡單的改進了插入排序,交換不相鄰的元素以對陣列的區域性進行排序,並最終用插入排序將區域性有序的陣列排序 希爾排序的思想是使陣列中任意...