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 ...