我覺得冒泡排序是比較簡單的;
所以今天我們實現一個叫做快速排序的;
Problem
你想要將(4,3,5,1,2)排序成(1,2,3,4,5)
你決定使用最簡單的快速排序;
Solution
首先,打開你的terminal,我寫得C代碼通常都是用vi編輯,gcc編譯;
vim quickSortSample.c
因為,快速排序需要對數據進行分割然后處理,再分割再處理;
顯然需要一個遞歸的過程;
所以,我們先把遞歸結束條件寫好;
#include <stdio.h> void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; }
也就是說,當輸入只有一個數字就沒有必要排序啦!直接返回;
void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { //TODO ... } }
注意: p = a[n / 2];的目地是獲取數組中得中間位置的數據;
我們都知道,數組下標是整數,因此,如果n=3那么n/2應該是1,此時正好平分數組;
若是,n=4那么n/2應該是2,而此時它們就不完全是平分數組啦;
for循環的作用是為了從前后兩個方向分別遍歷;
void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; } }
注意:我們在for循環中又進行啦while循環;
它的作用是對元素進行遍歷,假設左邊的數據沒有p中保存的元素大,那么繼續尋找,並且把i自增;
當找到比p大的或者相等得則停止第一個while循環;
例如:如果數組(2,5,3,1,4)那么p顯然是3,當while循環發現5大於3時停止循環,此時i=1;
同理,第二個while循環中,p依然是3,當while循環第一次發現4大於3則繼續循環此時j=4;
當while循環到j=3時,得到的元素是1,1不大於3所以停止循環,此時得到的j=3;
好啦!那么我們知道在for循環中沒有明確指定循環停止條件;
它該什么時候停止呢?
void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; if ( i >= j) break; } }
注意:當i>=j意味着此時數據已經全部遍歷完了;
因為i與j分布在數組左右兩部分,當它們重合,當然也就表明它們遍歷完啦;
剛才,我們的兩個while循環分別找到啦一個左邊比p大得元素,和一個右邊比p小的數字;
顯然這兩個元素應該交換位置的;
那么怎么實現呢;
void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; if ( i >= j) break; tmp = a[i]; a[i] = a[j]; a[j] = tmp; //swap both .. } }
我們看到了,在冒泡排序中也用到得交換語句;
此時我們完全遍歷了一遍數組;
我們在main函數中測試一下;
#include <stdio.h> void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; if ( i >= j) break; tmp = a[i]; a[i] = a[j]; a[j] = tmp; //swap both .. } } int main(void) { int a[] = { 2, 5, 3, 1, 4}; int n = sizeof a /sizeof a[0]; int i; for (i = 0; i < n; i++) printf("%d%s", a[i], i == n -1 ? "\n" : " "); quick_sort(a, n); for (i = 0; i < n; i++) printf("%d%s", a[i], i == n -1 ? "\n" : " "); return 0; }
main函數的語句都非常容易懂,就不解釋啦;
看看編譯運行的結果:
顯然,運行的結果是我們預期的;
3左邊的都比3小啦!那若果數據多一些呢;
比如{ 2, 5, 7, 3, -1, 1, 4}我們會發現左邊有兩個元素都比3大,理論上要進行兩次交換;
顯然當它交換完[1]與[5]元素后,它又是怎么繼續的我們關心這個問題;
顯然交換完數據后,又進入for循環;
for循環沒有結束條件,因此,i++,j--完成后,此時i=2,j=4;
接下來執行兩個while發現,第一個while發現a[2]是7 不小於p此時不執行i++;
同樣在第二個while中也發現a[4]是-1不大於p此時不執行j--;
然后到if語句不成立,因此繼續進行數據交換;
測試如下數據;
#include <stdio.h> void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; if ( i >= j) break; tmp = a[i]; a[i] = a[j]; a[j] = tmp; //swap both .. } } int main(void) { int a[] = { 2, 5, 7, 3, -1, 1, 4}; int n = sizeof a /sizeof a[0]; int i; for (i = 0; i < n; i++) printf("%d%s", a[i], i == n -1 ? "\n" : " "); quick_sort(a, n); for (i = 0; i < n; i++) printf("%d%s", a[i], i == n -1 ? "\n" : " "); return 0; }
我們會得到如下結果:
顯然,我們已經得到啦p左邊都是小於p的;
右邊則是大於p的;
符合我們的設想;
為了把整個數組正確排序,我們需要分別又對兩部分重復一遍剛才的操作;
好,我們遞歸的調用函數,並且把數組的兩部分元素傳遞到函數中;
void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; if ( i >= j) break; tmp = a[i]; a[i] = a[j]; a[j] = tmp; //swap both .. } quick_sort( a, i); quick_sort( a + i, n - i); }
注意:
第一個遞歸調用處理數組的前部分,a對應的是數組的首地址,i是指對應的元素個數;
第二個遞歸調用處理數組后部分,a + i指的當然是首地址加偏移地址,如果你對偏移地址有問題的話,應該看看計算機尋址方式的資料啦;
接下來看運行結果:
最后,我們把所有代碼附上:
#include <stdio.h> void quick_sort( int *a, int n) { int i, j, p, tmp; if (n < 2) return; p = a[n / 2]; // Get the middle element as pivot .. for ( i = 0, j = n -1;; i++, j--) { while (a[i] < p) i++; while (p < a[j]) j--; if ( i >= j) break; tmp = a[i]; a[i] = a[j]; a[j] = tmp; //swap both .. } quick_sort( a, i); quick_sort( a + i, n - i); } int main(void) { int a[] = { 2, 5, 7, 3, -1, 1, 4}; int n = sizeof a /sizeof a[0]; int i; for (i = 0; i < n; i++) printf("%d%s", a[i], i == n -1 ? "\n" : " "); quick_sort(a, n); for (i = 0; i < n; i++) printf("%d%s", a[i], i == n -1 ? "\n" : " "); return 0; }