資料結構與演算法分析 七 排序

2021-10-10 14:55:43 字數 4143 閱讀 9130

●演算法的輸入可以互換。每個演算法接收乙個含有元素的陣列和乙個包含元素個數的整數。

●傳入的元素個數n是合法的,資料從0處開始

●假設《和》運算子都存在

首先將第乙個關鍵字和第二個關鍵字進行比較,若為逆序則將兩個記錄交換,然後比較第二個記錄和第三個記錄,依次類推,知道第n-1個記錄和第n個記錄的關鍵字進行比較為止。此過程稱為第一趟起泡排序,其結果為使得關鍵字最大的記錄被安置到最後乙個記錄的位置,然後進行第二趟排序。

/*氣泡排序*/

void

bubblesort

(vector<

int>

&vec,

int n)

}

氣泡排序的平均時間複雜度是o(n^2)

//插入排序由n-1趟(pass)排序組成。對與p = 1到n-1趟,插入排序保證0到p-1位置上的元素是已排序的

//在第p趟,為了將位置p上的元素向左移動到正確的位置上。先儲存p上的元素,然後把比它大的元素都往右挪,直到有小於等於它的,

void

insertionsort

(vector<

int>

&vec,

int n)

}

●時間複雜度:2+3+4+…+n = o(n^2)

●輸入已預先排序的時間複雜度:o(n),因為內層for迴圈總是立即終止。所以如果輸入幾乎被排序,那麼插入排序將會執行的很快。

希爾排序是衝破二次時間屏障的第一批演算法之一。

這裡使用流行的增量序列:初始為n/2,每次/2,即希爾增量

void

shellsort

(vector<

int>

&vec,

int n)

}

●最壞情況:

使用希爾增量時希爾排序的最壞情形執行時間為o(n^2),hibbard增量和sedgewick增量序列的最壞情形比希爾增量要好。

利用之前的堆,先建堆,再deletemax。為了節省空間,將deletemax得到的值放到陣列後面

// 下濾

void

percdown

(vector<

int>

& vec,

int i,

int n)

}void

heapsort

(vector<

int>

& vec,

int n)

}

●時間複雜度:

建立n個元素的二叉堆花費o(n),執行n次deletemin操作,每次花費o(logn),總執行時間是o(nlogn)。

所以堆排序花費o(nlogn)的時間,但是在實踐中慢於使用sedgewick增量序列的希爾排序。

●空間複雜度:o(n)

增加了儲存需求,這是乙個弊端。從第二個陣列拷貝回第乙個 陣列消耗o(n)時間,這不會顯著影響執行時間。

可以避免使用第二個陣列,每次deletemin之後,堆縮小了1.因此位於堆中最後的元素可以用來存放剛剛刪去的元素。

所以採用大頂堆的話,最後陣列將會為公升序。

如將8個元素的陣列排序,可以遞迴地將前四個資料和後四個資料進行排序,然後將兩部分合併。這是經典的分治思想。

/*歸併排序*/

//left_end等於right - 1,所以不用傳入

void

merge

(vector<

int>

&vec, vector<

int>

&tmp,

int left,

int right,

int right_end)

//有乙個到底了

while

(left <= left_end)

tmp[tmp_pos++

]= vec[left++];

while

(right <= right_end)

tmp[tmp_pos++

]= vec[right++];

//因為right_end沒變,所以從right_end拷貝回vec

for(

int i =

0; i < num; i++

, right_end--

) vec[right_end]

= tmp[right_end];}

//將vec中的left到right排序

void

msort

(vector<

int>

&vec, vector<

int>

&tmp,

int left,

int right)

}//tmp的分析見下面

void

mergesort

(vector<

int>

&vec,

int n)

●時間複雜度:由t(n) = 2*t(n/2) + n可以得到t(n) = o(nlogn)

●缺點:很難用於主存排序,主要問題在於兩個排序的表需要線性附加記憶體,還要花費時間將資料拷貝,放慢了排序的速度。所以人們大多使用快速排序來進行內部排序。

●tmp的作用:如果每個遞迴呼叫均區域性宣告乙個臨時陣列,那麼在任一時刻可能就有logn個臨時陣列處在活動期,這樣會消耗很多記憶體。使用tmp在任一時刻只需要乙個臨時陣列活動。

錯誤方法

直接選取第乙個元素時,如果輸入是預排序或者反序的,會產生劣質的分割,因為所有的元素不是都在s1就是都在s2。

隨機選取

指標方法比較安全,但是隨機數的生成比較昂貴,減少不了演算法其餘部分的平均時間。

三數中值

樞紐元的最好的選擇是所有數的中值,但是這樣代價昂貴。因此使用左端、右端和中心位置上三個元素的中值作為樞紐元

/*快速排序*/

intpartition

(vector<

int>

&nums,

int left,

int right)

//此時i = j

nums[i]

= k;

return i;

}// 快排

void

quicksort

(vector<

int>

&nums,

int left,

int right)

小陣列時小陣列(n<=20),快速排序不如插入排序好,因為快速排序是遞迴的。所以在小陣列時採用插入排序這種對小陣列有效的排序演算法。

時間複雜度

●最壞情況:樞紐元始終是最小元素。t(n) = t(n - 1) + cn,n > 1,時間複雜度為o(n^2)

●最好情況:樞紐元正好位於中間。t(n) = 2t(n/2) + cn,時間複雜度為o(nlogn)

快速選擇

用於求得第k個最大(最小)元

桶排序將輸入資料的區間均勻分成若干份,每乙份稱作「桶」。分別對每乙個桶的內容進行排序,再按桶的順序輸出則完成排序。

/*桶式(鍊錶)排序*/

intfind_max

(vector<

double

>vec,

int n)

//輸入為大於0的double型別資料

void

bucketsort

(vector<

double

>

&vec,

int n)

}

●桶排序是穩定的

●桶排序是常見排序裡最快的一種,比快排還要快…大多數情況下

●桶排序非常快,但是同時也非常耗空間,基本上是最耗空間的一種排序演算法

資料結構複習之七 排序

重要參考資料 主要複製自這裡 一 排序的基本概念 穩定排序 如果排序表中有兩個相等元素,經過排序之後它們的相對位置不變,那麼這個演算法就是穩定的。二 插入排序 直接插入排序 插入排序是在乙個已經有序的小序列的基礎上,一次插入乙個元素。當然,剛開始這個有序的小序列只有1個元素,就是第乙個元素。比較是從...

專題七 排序演算法

1 基本思想 每一趟從待排序的資料元素中選出最小 或最大 的乙個元素,順序放在待排序的數列的最前,直到全部待排序的資料元素排完。2 排序過程 示例 初 始 關鍵字 49 38 65 97 76 13 27 49 第一趟排序後 13 38 65 97 76 49 27 49 第二趟排序後 13 27 ...

HLS 開發學習(七)排序

歸併排序 學習自parallel programming for fpgas the hls book 大夥應該都很了解了,就是將乙個新的元素插入到乙個有序陣列中,並繼續保持有序。每步將乙個待排序的記錄,按其關鍵碼值的大小插入前面已經排序的檔案中適當位置上,直到全部插入完為止。現在來考慮它的優化問題...