[C++]排序模板(含C++模板程式碼)
排序模板
一、插入排序
- 特點:stable sort、In-place sort
- 最優複雜度:當輸入陣列就是排好序的時候,複雜度為O(n),而快速排序在這種情況下會產生O(n^2)的複雜度。
- 最差複雜度:當輸入陣列為倒序時,複雜度為O(n^2)
- 插入排序比較適合用於“少量元素的陣列”。
虛擬碼:
C++模板:
template <typename T>
void Insertion_Sort(T *array, size_t length) {
if (length <= 1) {
return;
} else {
for (int i = 1; i != length; i++) {
int j = i - 1;
T key = array[i];
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
}
}
證明演算法正確性:
迴圈不變式:在每次迴圈開始前,A[1…i-1]包含了原來的A[1…i-1]的元素,並且已排序。
初始:i=2,A[1…1]已排序,成立。
保持:在迭代開始前,A[1…i-1]已排序,而迴圈體的目的是將A[i]插入A[1…i-1]中,使得A[1…i]排序,因此在下一輪迭代開 始前,i++,因此現在A[1…i-1]排好序了,因此保持迴圈不變式。
終止:最後i=n+1,並且A[1…n]已排序,而A[1…n]就是整個陣列,因此證畢。
二、氣泡排序
- 特點:stable sort、In-place sort
- 思想:通過兩兩交換,像水中的泡泡一樣,小的先冒出來,大的後冒出來。
- 最壞執行時間:O(n^2)
- 最佳執行時間:O(n^2)(當然,也可以進行改進使得最佳執行時間為O(n))
虛擬碼:
C++模板:
template <typename T>
void Bubble_Sort(T *array, size_t length) {
for (int i = 0; i != length - 1; i++) {
for (int j = 0; j + i != length - 1; j++) {
if (array[j] > array[j + 1]) {
T temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
證明演算法正確性:
運用兩次迴圈不變式,先證明第4-6行的內迴圈,再證明外迴圈。
內迴圈不變式:在每次迴圈開始前,A[j]是A[j…n]中最小的元素。
初始:j=n,因此A[n]是A[n…n]的最小元素。
保持:當迴圈開始時,已知A[j]是A[j…n]的最小元素,將A[j]與A[j-1]比較,並將較小者放在j-1位置,因此能夠說明A[j-1]是A[j-1…n]的最小元素,因此迴圈不變式保持。
終止:j=i,已知A[i]是A[i…n]中最小的元素,證畢。
接下來證明外迴圈不變式:在每次迴圈之前,A[1…i-1]包含了A中最小的i-1個元素,且已排序:A[1]<=A[2]<=…<=A[i-1]。
初始:i=1,因此A[1..0]=空,因此成立。
保持:當迴圈開始時,已知A[1…i-1]是A中最小的i-1個元素,且A[1]<=A[2]<=…<=A[i-1],根據內迴圈不變式,終止時A[i]是A[i…n]中最小的元素,因此A[1…i]包含了A中最小的i個元素,且A[1]<=A[2]<=…<=A[i-1]<=A[i]
終止:i=n+1,已知A[1…n]是A中最小的n個元素,且A[1]<=A[2]<=…<=A[n],得證。
在演算法導論思考題2-2中又問了”氣泡排序和插入排序哪個更快“呢?
一般的人回答:“差不多吧,因為漸近時間都是O(n^2)”。
但是事實上不是這樣的,插入排序的速度直接是逆序對的個數,而氣泡排序中執行“交換“的次數是逆序對的個數,因此氣泡排序執行的時間至少是逆序對的個數,因此插入排序的執行時間至少比氣泡排序快。
三、選擇排序
- 特性:In-place sort,unstable sort。
- 思想:每次找一個最小值。
- 最好情況時間:O(n^2)。
- 最壞情況時間:O(n^2)。
虛擬碼:
C++模板:
template <typename T>
void Selection_Sort(T *array, size_t length) {
for (int i = 0; i != length; i++) {
int min = i;
for (int j = i + 1; j != length; j++) {
if (array[min] > array[j]) {
min = j;
}
}
T temp = array[i];
array[i] = array[min];
array[min] = temp;
}
}
證明演算法正確性:
迴圈不變式:A[1…i-1]包含了A中最小的i-1個元素,且已排序。
初始:i=1,A[1…0]=空,因此成立。
保持:在某次迭代開始之前,保持迴圈不變式,即A[1…i-1]包含了A中最小的i-1個元素,且已排序,則進入迴圈體後,程式從 A[i…n]中找出最小值放在A[i]處,因此A[1…i]包含了A中最小的i個元素,且已排序,而i++,因此下一次迴圈之前,保持 迴圈不變式:A[1..i-1]包含了A中最小的i-1個元素,且已排序。
終止:i=n,已知A[1…n-1]包含了A中最小的i-1個元素,且已排序,因此A[n]中的元素是最大的,因此A[1…n]已排序,證畢。
四、歸併排序
- 特點:stable sort、Out-place sort
- 思想:運用分治法思想解決排序問題。
- 最壞情況執行時間:O(nlgn)
- 最佳執行時間:O(nlgn)
分治法介紹:分治法就是將原問題分解為多個獨立的子問題,且這些子問題的形式和原問題相似,只是規模上減少了,求解完子問題後合併結果構成原問題的解。
分治法通常有3步:Divide(分解子問題的步驟) 、 Conquer(遞迴解決子問題的步驟)、 Combine(子問題解求出來後合併成原問題解的步驟)。
假設Divide需要f(n)時間,Conquer分解為b個子問題,且子問題大小為a,Combine需要g(n)時間,則遞迴式為:
T(n)=bT(n/a)+f(n)+g(n)
演算法導論思考題4-3(引數傳遞)能夠很好的考察對於分治法的理解。
就如歸併排序,Divide的步驟為m=(p+q)/2,因此為O(1),Combine步驟為merge()函式,Conquer步驟為分解為2個子問題,子問題大小為n/2,因此:
歸併排序的遞迴式:T(n)=2T(n/2)+O(n)
而求解遞迴式的三種方法有:
- (1)替換法:主要用於驗證遞迴式的複雜度。
- (2)遞迴樹:能夠大致估算遞迴式的複雜度,估算完後可以用替換法驗證。
- (3)主定理:用於解一些常見的遞迴式。
虛擬碼:
C++模板:
template <typename T>
void Merge(T *sourceArray, T *temp, int Start_Index, int Mid_Index, int End_Index) {
int i = Start_Index, j = Mid_Index + 1, k = Start_Index;
while (i != Mid_Index + 1 && j != End_Index + 1) {
if (sourceArray[i] > sourceArray[j]) {
temp[k++] = sourceArray[j++];
} else {
temp[k++] = sourceArray[i++];
}
}
while (i != Mid_Index + 1) {
temp[k++] = sourceArray[i++];
}
while (j != End_Index + 1) {
temp[k++] = sourceArray[j++];
}
for (int i = Start_Index; i != End_Index + 1; i++) {
sourceArray[i] = temp[i];
}
}
template <typename T>
void Merge_Sort(T *sourceArray, T *temp, int Start_Index, int End_Index) {
if (Start_Index < End_Index) {
int Mid_Index = (Start_Index + End_Index) / 2;
Merge_Sort(sourceArray, temp, Start_Index, Mid_Index);
Merge_Sort(sourceArray, temp, Mid_Index + 1, End_Index);
Merge(sourceArray, temp, Start_Index, Mid_Index, End_Index);
}
}
C++ 連結串列的歸併排序法:
// 連結串列的歸併排序。
void LinkedList::sort(void) {
if (this->size() > 1) {
node* fast = this->head;
node* slow = this->head;
LinkedList li_left;
LinkedList li_right;
li_left.head = this->head;
while (fast != NULL && fast->next != NULL) {
li_left._size++;
fast = fast->next->next;
slow = slow->next;
}
li_left.tail = slow->prev;
li_left.tail->next = NULL;
li_right.head = slow;
li_right.head->prev = NULL;
li_right.tail = this->tail;
li_right._size = this->_size - li_left._size;
this->head = NULL;
this->tail = NULL;
li_left.sort();
li_right.sort();
node* pointer_left = li_left.head;
node* pointer_right = li_right.head;
node* pointer_head = NULL;
node* pointer_tail = NULL;
while (pointer_left != NULL && pointer_right != NULL) {
node* temp;
if (pointer_left->data <= pointer_right->data) {
temp = pointer_left;
pointer_left = pointer_left->next;
} else {
temp = pointer_right;
pointer_right = pointer_right->next;
}
if (pointer_head == NULL) {
pointer_head = pointer_tail = temp;
} else {
pointer_tail->next = temp;
temp->prev = pointer_tail;
pointer_tail = temp;
}
pointer_head->prev = NULL;
pointer_tail->next = NULL;
}
while (pointer_left != NULL) {
pointer_tail->next = pointer_left;
pointer_left->prev = pointer_tail;
pointer_tail = pointer_left;
pointer_left = pointer_left->next;
}
while (pointer_right != NULL) {
pointer_tail->next = pointer_right;
pointer_right->prev = pointer_tail;
pointer_tail = pointer_right;
pointer_right = pointer_right->next;
}
this->head = pointer_head;
this->tail = pointer_tail;
li_left.head = li_left.tail = NULL;
li_right.head = li_right.tail = NULL;
}
舉例說明:
問:歸併排序的缺點是什麼?
答:他是Out-place sort,因此相比快排,需要很多額外的空間。
問:為什麼歸併排序比快速排序慢?
答:雖然漸近複雜度一樣,但是歸併排序的係數比快排大。
問:對於歸併排序有什麼改進?
答:就是在陣列長度為k時,用插入排序,因為插入排序適合對小陣列排序。在演算法導論思考題2-1中介紹了。複雜度為O(nk+nlg(n/k)) ,當k=O(lgn)時,複雜度為O(nlgn)
五、快速排序
演算法介紹:
設要排序的陣列是A[0]……A[N-1],首先任意選取一個數據(通常選用陣列的第一個數)作為關鍵資料,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一趟快速排序。值得注意的是,快速排序不是一種穩定的排序演算法,也就是說,多個相同的值的相對位置也許會在演算法結束時產生變動。
一趟快速排序的演算法是:
1)設定兩個變數i、j,排序開始的時候:i=0,j=N-1;
2)以第一個陣列元素作為關鍵資料,賦值給key,即key=A[0];
3)從j開始向前搜尋,即由後開始向前搜尋(j–),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向後搜尋,即由前開始向後搜尋(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指標位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令迴圈結束)。
QUICK_SORT(A,p,r)
if p<r
then q ←PARTITION(A,p,r)
QUICKSORT(A,p,q-1)
QUICKSORT(A,q+1,r)
/*
為排序一個完整的陣列A,最初的呼叫是QUICKSORT(A,1,length[A])。
快速排序演算法的關鍵是PARTITION過程,它對子陣列A[p..r]進行就地重排:
*/
PARTITION(A,p,r)
x←A[r]
i←p-1
for j←p to r-1
do if A[j]≤x
then i←i+1
exchange A[i]←→A[j]
exchange A[i+1]←→A[r]
return i+1[2]
C++模板:
template <typename T>
void Quick_Sort(T *array, int Start_Index, int End_Index) {
if (End_Index >= Start_Index) {
int first = Start_Index;
int last = End_Index;
T key = array[first];
while (first < last) {
while (first < last && array[last] >= key) {
last--;
}
array[first] = array[last];
while (first < last && array[first] <= key) {
first++;
}
array[last] = array[first];
}
array[first] = key;
Quick_Sort(array, Start_Index, first - 1);
Quick_Sort(array, first + 1, End_Index);
}
}
相關推薦
[C++]排序模板(含C++模板程式碼)
排序模板 一、插入排序 特點:stable sort、In-place sort 最優複雜度:當輸入陣列就是排好序的時候,複雜度為O(n),而快速排序在這種情況下會產生O(n^2)的複雜度。 最差複雜度:當輸入陣列為倒序時,複雜度為O(n^2) 插入排
如何讓C#編譯不安全程式碼(含unsafe的程式碼)
背景:有關鍵字unsafe,其程式碼在vs編譯環境中會報錯。報錯資訊:“不安全程式碼只會在使用/unsafe編譯的情況下出現” 解決方案: 1、vs選單“專案”中找到“(解決方案名稱)屬性”項,單擊進入專案屬性設定介面; 2、在專案屬性介面中找到“生成”選項卡 3、在“生成
C++控制檯操作(基本操作的程式碼)
控制檯視窗介面程式設計控制 〇、摘要 一、概述 二、控制檯文字視窗的一般控制步驟 三、控制檯視窗操作 四、文字屬性操作 五、文字輸出 六、文字操作示例 七、滾動和移動 八、游標操作 九、讀取鍵盤資訊 十、讀取滑鼠資訊 十一、結語 補充篇--經典程式
Python分散式爬蟲打造搜尋引擎網站(含課件&程式碼)
未來是什麼時代?是資料時代!資料分析服務、網際網路金融,資料建模、自然語言處理、醫療病例分析……越來越多的工作會基於資料來做,而爬蟲正是快速獲取資料最重要的方式,相比其它語言,Python爬蟲更簡單、高效 ----------------------課程目錄--
【Java】《Java程式設計思想》筆記(含練習題答案程式碼)-第二章 一切都是物件
2.1 用引用操縱物件【String】 遙控器(引用)- 電視機(資料)建立String引用,如果向無物件的引用傳送資訊會返回一個執行時錯誤,所以安全的做法是:建立一個引用的同時便進行初始化Stri
ajax請求返回xml資料(含服務端程式碼)
ajax技術目前已經被jquery包裝的比較完美,但自己使用的地方仍然很多,所以需要自己能夠自定義ajax方法。 以下是從前端ajax發出請求,後臺響應返回xml資料,ajax接收並處理的完整過程,例
自己寫 localtime 函式(含完整註釋,程式碼)
自己寫 localtime 函式(含完整註釋,程式碼) //對時間轉換有興趣的可以看看。//曾在某平臺下多執行緒中使用localtime庫函式。可惡的是,每當程式執行//一段時間後,都要出現記憶體洩露。查了數個夜晚為什麼,無從斷定。//只能狠狠心,自己寫了個localtime
百度語音識別REST API使用方法(含C++程式碼)——不需要整合SDK的方法
本文程式碼為C++版,可以用於C環境的應用開發中,下面介紹其中重要的程式碼。 下面程式碼是一個可以使用該方式進行語音識別功能的例項程式碼 #include <stdio.h> #include <stdlib.h> #include "curl
C# winform從資料庫匯出資料到Excel模板(預覽及儲存)
1、新增Microsoft.Office.Interop.Excel檔案(dll)到引用中,並在名稱空間中引用(using Microsoft.Office.Interop.Excel;)該檔案在我的資源(做的一個例程)中有,見下圖。 例程下載http://download
買什麼資料結構與演算法,這裡有:動態圖解十大經典排序演算法(含JAVA程式碼實現)
上篇的動圖資料結構反響不錯,這次來個動圖排序演算法大全。資料結構與演算法,齊了。 幾張動態圖捋清Java常用資料結構及其設計原理 本文將採取動態圖+文字描述+正確的java程式碼實現來講解以下十大排序演算法: 氣泡排序 選擇排序 插入排序 希爾排序
《程式設計基礎》實驗題目2 c檔案讀取(反序列化?) 連結串列排序
題目: 每個學生的資訊卡片包括學號、姓名和成績三項。定義儲存學生資訊的單向連結串列的結點型別;編寫函 數,由檔案依次讀入 n(n≥0)個學生的資訊,建立一個用於管理學生資訊的單向連結串列;編寫函式,對 該連結串列進行整理,保證該單向連結串列的結點順序滿足學號從小到大的順序。 演算法的設計與
堆排序法(Java & C/C++ 實現)
一、前言 堆排序是利用堆這種資料結構而設計的一種排序演算法。時間複雜度為 O(n * lg n)。 介紹堆排序前,我們先介紹一下堆的相關概念,如果你對堆的概念還不熟悉的話可以看看。 二、堆 1. 示意圖 2. 性質 除最底層外,該樹是完全充滿的,且是從左到右填充。 樹的根結點是 A[
快速排序演算法(QSort,快排)及C語言實現
上節介紹瞭如何使用起泡排序的思想對無序表中的記錄按照一定的規則進行排序,本節再介紹一種排序演算法——快速排序演算法(Quick Sort)。 C語言中自帶函式庫中就有快速排序——qsort函式 ,包含在 <stdlib.h> 標頭檔案中。 快速排序演算法是在起泡排序的基礎上進行改進的一種演算
藍橋杯練習系統試題集(二)--基礎練習(含C/C++答案)
藍橋杯練習系統試題集(二)–基礎練習(含C/C++答案) 1 基礎練習 閏年判斷 時間限制:1.0s 記憶體限制:256.0MB 提交此題 錦囊1 錦囊2 問題描述 給定一個年份,判斷這一年是不是閏年。 當以下情況之一滿足時
把C#程式(含多個Dll)合併成一個Exe的超簡單方法
開發程式的時候經常會引用一些第三方的DLL,然後編譯生成的exe檔案就不能脫離這些DLL獨立運行了。 但是,很多時候我們本想開發一款只需要一個exe就能完美執行的小工具。那該怎麼辦呢? 下文介紹一種超簡單的方法,不用寫一行程式碼就可輕鬆實現。 這裡我們需要用到一款名為Fody.Costura的工具。Fo
十大經典排序演算法(含JAVA程式碼實現)
排序演算法說明0.1 排序的定義對一序列物件根據某個關鍵字進行排序。0.2 術語說明穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面;不穩定:如果a原本在b的前面,而a=b,排序之後a可能會出現在b的後面;內排序:所有排序操作都在記憶體中完成;外排序:由於資料太大
《程式設計師的第一年》---------- 資料探勘之資料處理(C#基於熵的離散化演算法程式碼)
熵(entropy)是最常用的離散化度量之一。它由Claude Shannon在資訊理論和資訊增益概念的開創性工作中首次引進。基於熵的離散化是一種監督的、自頂向下的分裂技術。它在計算和確定分裂點(劃分屬性區間的資料值)時利用類分佈資訊。為了離散數值屬性A,該方法選擇A的具
C# 讀取Word文字框中的文字、圖片和表格(附VB.NET程式碼)
【概述】 Word中可插入文字框,在文字框中可新增文字、圖片、表格等內容。本篇文章通過C#程式程式碼介紹如何來讀取文字框中的文字、圖片和表格等內容。附VB.NET程式碼,有需要可作參考。 【程式環境】 程式中所需必要的程式集檔案Spire.Doc.dll,及其他相關dll檔案(見下文)。 用於測試的Word源
AC自己主動機模板(數組實現版)
函數 模板 多次 做了 ems har using 實現 art BY 九野 做了一道題,用我的那種寫法華麗麗的超時了。,無奈學一學數組實現的 #include<stdio.h> #include<string.h> #include&l
c#開源工具(或者C# 開源框架)
stack windows 框架 ado doc 2.0 release dal .com 1.轉載聲明:本篇內容轉載自http://www.cnblogs.com/gaoyuchuanIT/articles/5612268.html。 2. C# 開源框架(整理)