偽代碼:
C++模板:
template
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]就是整個數組,因此證畢。
偽代碼:
C++模板:
template
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)”。
但是事實上不是這樣的,插入排序的速度直接是逆序對的個數,而冒泡排序中執行“交換“的次數是逆序對的個數,因此冒泡排序執行的時間至少是逆序對的個數,因此插入排序的執行時間至少比冒泡排序快。
偽代碼:
C++模板:
template
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]已排序,證畢。
分治法介紹:分治法就是將原問題分解為多個獨立的子問題,且這些子問題的形式和原問題相似,只是規模上減少了,求解完子問題後合並結果構成原問題的解。
分治法通常有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
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
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;
}
舉例說明:
問:歸並排序的缺點是什麼?<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrTwo7rL+8rHT3V0LXBsYWNlIHNvcnSjrNLytMvP4LHIv+zFxaOs0OjSqrrctuC27s3itcS/1bzkoaM8L3A+DQo8cD7OyqO6zqrKssO0uemyosXF0PKxyL/sy9nFxdDywv2jvzwvcD4NCjxwPrTwo7rL5Mi7vaW9/Li01NO2yNK70fmjrLWryse56bKixcXQ8rXEz7XK/bHIv+zFxbTzoaM8L3A+DQo8cD7OyqO6ttTT2rnpsqLFxdDy09DKssO0uMS9+KO/PC9wPg0KPHA+tPCjur7NysfU2sr91+mzpLbIzqpryrGjrNPDsuXI68XF0PKjrNLyzqqy5cjrxcXQ8srKus+21NChyv3X6cXF0PKho9Tay+O3qLW8wtvLvL+8zOIyLTHW0L3pydzBy6GjuLTU07bIzqpPKG5rK25sZyhuL2spKSCjrLWxaz1PKGxnbinKsaOsuLTU07bIzqpPKG5sZ24pPC9wPg0KPGgzIGlkPQ=="五快速排序">五、快速排序
算法介紹:
設要排序的數組是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
C++模板:
template
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);
}
}