演算法導論複習與實踐(一) 插入排序與歸併排序

2021-09-14 00:06:27 字數 3776 閱讀 8696

是否擁有紮實的演算法知識和技術基礎,是區分真正熟練的程式設計師與新手的一項重要特徵。雖然利用當代的計算技術,無需了解很多演算法方面的知識,也可以完成一些任務,但是如果有良好的演算法基礎和背景的話可以做更多的事情,並且擁有更強的思維能力。學生時期雖然全面學習過演算法導論裡的相關知識,但是學生時沒有太多的程式設計經驗,實踐演算法時候需要邊看書上的偽**邊寫程式才能實現,經過工作幾年,很多知識已經不記得了。現在想重新拿起,因此將實踐過程做些記錄方便加深理解與記憶。

根據二者排序演算法的時間複雜度與實踐中對同樣規模問題計算所花費的時間可以看到,隨著問題規模的增長,歸併排序比插入排序有非常明顯的優勢,對於規模很大的問題,一台效能非常良好的計算機a使用插入排序可能要花費好幾個小時的時間,而一台效能普通的計算機b使用歸併排序則只需花費幾秒的計算時間,差距就是這麼明顯。因此對於大規模的問題,演算法的設計至關重要。

以我們玩撲克牌時摸牌過程可以非常形象地理解插入排序,我們每從牌堆裡摸起一張牌,邊從右往左比較每張排的大小,然後將當前的牌插入到第一張比它小的牌後面,這樣的排序過程保證手裡的牌總是排好序。

下圖是書中形象地介紹插入排序實現過程:

插入排序過程從第2個數開始遍歷輸入陣列,圖中深黑色部分為每趟排序過程的「當前牌」,將「當前牌」逐個與前面灰色元素的數值比較,如果該數值比「當前牌大」,則將該元素往後移動乙個位置,直到找到第乙個比「當前牌」小的元素,然後將「當前牌」插入到該元素的後面。

插入排序c++實現:

插入排序最壞情況是輸入資料為逆序的時候,這時候每趟排序,「當前牌」都需要與前面所有的元素進行比較,時間複雜度為:

我實踐中對100萬個逆序的輸入資料進行排序,需要時間為1143.4秒

#include

#include

void

insertionsort

(double

* data,

const

int len)

data[ i +1]

= key;}}

intmain()

;for

(int i = len, j =

0; i >

0&& j < len;

--i,

++j)

struct timeval time_start, time_end;

gettimeofday

(&time_start,

null);

//1000000 time : 1143.4 (s)

insertionsort

( data , len)

;gettimeofday

(&time_end,

null);

double time =

1000000

*(time_end.tv_sec -time_start.tv_sec)

+(time_end.tv_usec -time_start.tv_usec)

; time /

=1000000

; std::cout<<

"time : "

<

" (s) "

<<:endl>

return0;

}

歸併排序是一種分治策略:將原問題劃分為n個規模更小而結構與原問題相似的子問題,遞迴地解決這些子問題,然後再合併其結果,得到原問題的解。

分治模式的三個步驟:

分解:將原問題分解為一系列子問題。

解決:遞迴地解決各子問題,若子問題足夠小,則直接求解。

合併:將子問題的結果合併成原問題的解。

對於歸併排序來說:

分解:將n個元素分成各n/2個元素的子串行

解決:用歸併排序對兩個子串行遞迴地排序

合併:合併兩個已排序的子串行以得到排序結果。

形象地理解合併過程:兩堆已排好序的撲克牌,最小的牌放在牌堆的最上面,每次比較兩堆牌最上面的兩張牌,取出面值小的牌插入手中,這樣每次取出牌後牌堆頂部的牌都分別是兩堆牌中最小的牌,而手上的牌是排好序的,當其中一堆牌全部取出後將另一堆所有的牌一次取出放入手中即可。

歸併排序c++實現:

歸併排序時間複雜度為:

實踐中同樣對100萬個逆序的輸入資料進行排序,需要時間為0.13759秒

#include

#include

///合併過程:輸入陣列p~q和q+1~r的元素分別是已排好序的兩個子串行

void

merge

(double

*data,

int p,

int q,

int r)

for(

int i =

0; i < n2;

++i)

int i =0;

int j =0;

///將輸入陣列中p~r元素合併為已排序的序列

for(

int k = p; k <= r;

++k)

}else

//若右序列已經全部放入陣列中,

//則將左序列剩下的元素依次放入陣列中完成排序過程if(

( j == n2)

&&( i < n1 ))}

elseif(

( i < n1)

&&( j < n2 )

)else}}

delete

l;delete

r;}void

mergesort

(double

*data,

int p,

int r)

}int

main()

;for

(int i = len, j =

0; i >

0&& j < len;

--i,

++j)

struct timeval time_start, time_end;

gettimeofday

(&time_start,

null);

//1000000 time : 0.13759 (s)

mergesort

(data,

0, len -1)

;gettimeofday

(&time_end,

null);

double time =

1000000

*(time_end.tv_sec -time_start.tv_sec)

+(time_end.tv_usec -time_start.tv_usec)

; time /

=1000000

; std::cout<<

"time : "

<

" (s) "

<<:endl>

return0;

}

下圖幫助理解歸併排序分解合併過程:

原問題是對陣列a中的所有元素的進行由小到大排序,呼叫歸併函式是輸入引數p1=1, r1 = 10,然後將輸入序列從中間q=(p1+r1)/2處分為左序列p2 = 1, r2 = 5, p3 = 6, r3 = 10, 以此類推遞迴地對子序列進行排序,直到子串行為單個元素時則為已排好序的序列,然後從下往上進行合併,最終得到排好序的陣列。

演算法 一 插入排序

插入排序演算法類似於玩撲克時抓牌的過程,玩家每拿到一張牌都要插入到手中已有的牌裡,使之從小到大排好序。撲克牌的插入排序 也許你沒有意識到,但其實你的思考過程是這樣的 現在抓到一張7,把它和手裡的牌從右到左依次比較,7比10小,應該再往左插,7比5大,好,就插這裡。為什麼比較了10和5就可以確定7的位...

基礎演算法複習篇(一) 插入排序演算法

經過乙個假期對基礎演算法知識的學習,總算在近期告一段落,正好在剛開學的這一段冷卻期,為了加深對演算法的記憶與理解,準備對假期所學習的演算法知識進行一定的總結與分析,算是對前一階段所學知識的總結吧,也許在這期間還能發掘出當初沒有想到的想法,算是很隨性的總結吧,那麼從今天開始,便由淺入深的對基本的演算法...

演算法設計與分析 排序演算法(一) 插入排序

分類目錄 演算法設計與分析 總目錄 排序演算法 二 歸併排序 插入排序對於少量元素的排序是乙個有效的演算法。插入排序的工作方式像許多人排序一手撲克牌。開始時,我們的左手為空並且桌子上的牌面向下。然後,我們每次從桌子上拿走一張牌並將它插入手中正確的位置。為了找到一張牌的正確位置,我們從右到左將它與已在...