C Tips之快速排序

2021-10-01 15:25:14 字數 3991 閱讀 1806

c# tips是博主開啟的第乙個專案,以c#為示例語言,旨在普及程式設計技巧、經典演算法以及計算機視覺、圖形學知識。

博主學習語言的歷程是c直接到c#,為什麼跳過了c++?因為c#比c++更能夠滿足教學需要,比如arcgis engine、資料庫等課程都要和c#打交道。所以選擇c#的第乙個原因是:不能白白浪費了學c#交的學費。

c#經常以速度不如c++而被人詬病(實際上在某些方面c#的速度要快於c++,深有體會的就是編譯執行速度),但是visual studio這款強大的ide又對c#偏心(vs對c++智慧型提示幾乎為零),博主比較喜歡c#在vs裡的程式設計風格,綜上選擇了c#語言作為示例。

下面這篇部落格對線性資料結構的排序演算法的介紹已經非常詳細,在此不再贅述。

十大經典排序演算法

天底下沒有兩全其美的事,排序演算法也如此,一般情況下,時間複雜度低的空間複雜度高(非比較排序),占用空間少的排序時間長(比較排序)。快速排序演算法在這些演算法中算是比較實在的,時間和空間複雜度都相對居中,所以咱今天就欺負欺負老實人,拿它來說事。

這一部分寫給快排沒掌握好或者曾經掌握過但是忘了的人~~

2.1 原理

快速排序演算法實際上用到了二分的思想,以下以公升序為例:

從資料集a中選取某基準元素a0,剩下的元素集合記為a-;

將a-中比a0小的元素放在a0左邊,記為a;

令a分別=a,如果a中元素數量<2,結束;否則回到1。

想要實現以上操作,需要明確以下兩個問題:

基準元素a0怎麼選?

怎麼將a-中的元素放到a0的兩側?

下面就選a中第乙個元素為基準元素(其他元素可自行嘗試),利用快速排序給資料集"6 4 5 1 3 2"排序。

6 4 5 1 3 2->2 4 5 1 3 6(1次排序結束)

2 4 5 1 36->1 2 5 4 36(2次排序結束)

1 25 4 36->1 23 4 56(3次排序結束)

1 23 45 6->1 2 3 4 5 6(4次排序結束)

看到這裡,有同學肯定要問了:為什麼1次排序的結果是2 4 5 1 3 6而不是4 5 1 3 2 6?這就涉及到了第二個問題。

快速排序演算法是基於元素交換的演算法,所以並不會真正地將a-中的元素全部拿出來,分別與a0比較,其採取的策略如下:

如圖圓圈代表a《集合的哨兵,方塊代表a>集合的哨兵,兩個哨兵分別向中間移動,當兩個哨兵剛好錯過時(圓圈跑到了方塊的右邊),哨兵移動結束,基準元素與方塊所在元素進行交換。

哨兵移動的情況分為以下幾種:

圓圈元素<=基準元素 || 圓圈索引==基準索引,則向右移動圓圈;

若1不滿足,滿足 方塊元素》=基準元素 || 方塊索引==基準索引,則向左移動方塊;

若1、2都不滿足,說明圓圈元素》基準元素 && 方塊元素《基準元素,則交換圓圈和方塊元素,兩哨兵分別向中間移動一步。

2.2 **實現

快速排序演算法可以基於遞迴和非遞迴兩種方式實現,無論是哪一種實現方法,都需要實現交換,所以先「交換」為敬!

/// 

/// 交換兩個資料

///

/// 源資料

/// 索引1

/// 索引2

public

void

swap

(dynamic data,

int index1,

int index2)

這種交換方式適用於任意型別的一維陣列、列表。但注意,dynamic可能會在資料量很大時增加運算時間,所以在資料型別確定時最好使用確定的資料型別。

除此之外,哨兵移動後都需要找到方塊哨兵最終落腳的位置。

/// 

/// 獲得方塊哨兵落腳索引

///

/// 源資料

/// 頭索引

/// 尾索引

/// 索引

public

intpartition

(dynamic data,

int l,

int r)

}return r;

}

同樣注意,在資料型別確定時,盡量不要使用compareto。

2.2.1 基於遞迴

由下面**實現遞迴:

/// 

/// 遞迴快速排序

///

/// 源資料

/// 頭索引

/// 尾索引

public

void

quicksort

(dynamic data,

int l,

int r)

2.2.2 基於非遞迴

遞迴演算法都可以利用非遞迴演算法實現,快速排序演算法可以借助佇列實現非遞迴。

/// 

/// 非遞迴快速排序

///

/// 源資料

/// 頭索引

/// 尾索引

public

void

quicksort2

(dynamic data,

int l0,

int r0)

if(p +

1< r)

}}

這一部分寫給以為看完了上一部分就學會快排的人~~

3.1 快排弊端

快排好快啊!這是第一次使用快排時發出的感嘆。但是快排真的有這麼完美嗎?要知道,在最壞的情況下,快排的時間複雜度也會達到o(n2),拿最簡單的兩種情況舉例來說:

1)1 2 3 4 5 6

2)6 5 4 3 2 1

很容易發現,這兩種情況下的資料都是有序的,因此在快排前做個有序性檢驗還是有必要的。

/// 

/// 判斷資料是否有序

///

/// 源資料

/// 資料量

/// 1為公升序,-1為降序,0為無序

public

intcheckinorder

(dynamic data,

int count)

else

if(data[i]

.compareto

(data[i -1]

)<0)

break;}

i++;if

(order==1)

}else

}return order;

}

但是這就完了嗎?並沒有,因為有時候檢驗出來資料是公升序的,但是我們需要的是降序,那麼還需要新增乙個資料反轉的方法。

/// 

/// 反轉資料

///

/// 源資料

/// 資料量

public

void

reverse

(dynamic data,

int count)

3.2 時間比較

測試資料資料量為10000,範圍為0-9999的整數,需求是按公升序排序,快速排序採用非遞迴方式(有序資料遞迴會導致棧溢位),測試時間計算方式為5次測試結果去最大最小平均值。不知道怎麼插入三線表,就用代替啦。

由測試結果可以明顯看出:對於有序資料,粗暴的快排真的是無能為力,而有序性檢驗發揮了強大的作用;對於無序資料,快排是真快,但有序性檢驗並未占用太多時間。

排序之快速排序

快速排序的在內排中起到比較重要的作用,平均時間複雜度達到o nlogn 公升序快速排序 1 int partition vector vi,int start,int end 11 vi start key 12return start 13 14void quickcore vector vi,i...

排序之快速排序

有沒有既不浪費空間又可以快一點的排序演算法呢?那就是 快速排序 啦!光聽這個名字是不是就覺得很高階呢。假設我們現在對 6 1 2 7 9 3 4 5 10 8 這個10個數進行排序。首先在這個序列中隨便找乙個數作為基準數 不要被這個名詞嚇到了,就是乙個用來參照的數,待會你就知道它用來做啥的了 為了方...

排序之快速排序

該方法的基本思想是 1 先從數列中取出乙個數作為基準數。2 分割槽過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。3 再對左右區間重複第二步,直到各區間只有乙個數 o nlogn 出處 includeusing namespace std void quicksort int ...