這篇文章僅僅為心中自證,不是算法教學,也不想誤人子弟,謝謝各位。
第一章:一些感慨
我斷斷續續學習算法兩年多了,這說起來是多么苦澀,是我笨嘛?一直不知道算法是什么東西。
從《算法導論》再到《C算法》不清楚看了多少遍,它們就是我過不去的坎嗎?
不敢說什么大話,但是我有一個心得,學習算法,一定要理解,理解比會寫更重要,會寫,很有可能僅僅是記憶好,
但是過一段時間忘了, 就對這個算法完全沒有印象了,我就是這樣。
所以我以后學習算法,一定抱着理解的心態,理解了,就很好。
第二章:基本排序算法
2.1 冒泡排序
人們常說,冒泡排序是最初級的排序算法,人們說這句話的時候是從時間復雜度這個角度來說的,這么說或許沒錯,
但是,冒泡排序是相當漂亮的排序,這個“漂亮”是說,假如把排序算法可視化起來,x軸從小到大,依次對應0到n,y軸對應的是a[x],
然后排序開始了,你會發現冒泡很飄逸,看起來很賞心悅目,大概人們都喜歡美麗的泡泡吧。
冒泡排序的中心思想:不寫中心思想至少也有十年了吧,記得高中之后語文老師就不搞什么中心思想之類的。
但是我還是要寫一寫冒泡排序的中心思想,而且,現在我不看資料,我想描述一下,看一看我到底能不能描述。
冒泡排序的思想是這樣的,從數組a[0]開始,比較相鄰的兩項的大小順序,假如順序不對,則交換兩項。
5 | 4 | 6 | 3 | 2 |
比如上面的數組,先比較a[0]和a[1],發現a[0]大於a[1],我們認為從小到大排,所以順序就不對了,那就交換吧,交換之后就是
4 | 5 | 6 | 3 | 2 |
再然后就是比較a[1]和a[2]了,就這樣,一次冒泡,最大項就冒到了頂部。
很明顯,我們需要兩層循環,可是第二次循環的時候,我們就沒必要從0到n了,因為我們知道a[n-1]項已經是最大的了。
1 void BubbleSort(Item a[],int left,int right) 2 { 3 int i = 0,j = 0; 4 for(i = left; i < right; i++) 5 { 6 for(j = left;j < right -i - 1; j++) 7 { 8 compexch(a[j],a[j+1]); 9 } 10 } 11 } 12 void BubbleSort(Item a[],int left,int right) 13 { 14 int i = 0,j = 0; 15 for(i = left; i < right; i++) 16 { 17 for(j = right;j > i; j--) 18 { 19 compexch(a[j-1],a[j]); 20 } 21 } 22 }
2.2 快速排序
記得有誰說過,快速排序是在冒泡排序的基礎上的,這個,我信了。
快速排序的中心思想:在我看來快速排序的中心思想是很簡單的,我不知道快排的發明人是怎么想出來的。
快排的思想是,選擇數組中的一個數,把它放到排序結束后它應該在的位置。
這是一句很有意思的話,我怎么知道一個數排序結束后它在哪呢?其實可以知道的,
我們希望一次排序結束后,這個數左邊的所有數都比這個數小,這個數右邊所有的數都比這個數大。
然后我們對它的左邊用快排,然后對他的右邊用快排。
最原始的寫法是這樣的。
1 int partition(int * a,int l,int r) 2 { 3 int i = l-1,j = r; 4 int v = a[r]; 5 for(;;) 6 { 7 while(a[++i] < v); 8 while(v < a[--j]) 9 if(j == l) 10 break; 11 if(i >= j) 12 break; 13 EXCH(a[i],a[j]); 14 } 15 EXCH(a[i],a[r]); 16 return i; 17 } 18 void QuickSort(int * a,int l,int r) 19 { 20 int i; 21 if(r <= l) 22 return; 23 i = partition(a,l,r); 24 QuickSort(a,l,i-1); 25 QuickSort(a,i+1,r); 26 }
partition函數的中心思想:partition函數的功能,就是從數組中選取一個數,然后讓左邊的全部小於它,右邊的全部大於它,然后返回位置就行了。
但是上面的partition真是大有問題,在正序和逆序的時候,很多人都哭了,所以常見的做法是,選取的這個數是隨機選取的。
一個或許可能的解決方案是這個,(我又空手敲代碼了,我有可能寫出來嗎?)(沒有,我又看了好多資料,調試了好久。)
int partition(int * a,int l,int r) { int i = l-1; int j = r; int m = l+rand()%(r-l+1); EXCH(a[m],a[r]); int v = a[r]; for(;;) { while(a[++i] < v); while(v < a[--j]) if(j == l) break; if(i >= j) break; EXCH(a[i],a[j]); PrintArray(a); } EXCH(a[i],a[r]); PrintArray(a); return i; }
2.3 插入排序
或許,我根本不適合學習算法,還是放棄吧,寫完這一章。
插入排序的中心思想:我是這樣理解的,待排序項一直左移,已經排好的一直右移,直到某一項小於待排序項。
從第二項開始一直執行這樣的操作。
缺點:數組移動。
1 void InsertSort(int * a,int l,int r) 2 { 3 for(int i = l+1;i < r; i++) 4 { 5 int j = i; 6 int v = a[j]; 7 while(--j >= 0) 8 { 9 if(v < a[j]) 10 { 11 a[j+1] = a[j]; 12 } 13 else 14 { 15 break; 16 } 17 } 18 ++j; 19 a[j] = v; 20 PrintArray(a); 21 } 22 }
2.4 希爾排序
希爾排序是分組的插入排序,
划分公式可以是n*x+1,一個划分可能的划分是1 4 7 10 13 16,另一個可能的划分是1 5 9 13 17 21。
2.5 選擇排序
選擇排序,首先選取數組第一小元素放到第一項,數組第二小的元素放到第二項。
void Array::SelectSort() { int i = 0; int j = 0; for(i = 0; i < 10; i++) { int min = i; for(j = i+1;j < 10; j++) { if(first[j] < first[min]) { min = j; } } first[i].Exchange(first[min]); } }
2.6 歸並排序
歸並排序和快速排序的形式相反,兩個遞歸之后跟一個歸並函數。
歸並函數的要點是,對已經排序過的數組進行排序。可以原位歸並,或者借助一個數組。
下面的代碼來自《C算法》不知道打錯了沒有。
1 void mergesort(Item * a,int l,int r) 2 { 3 int m = (r+l)/2; 4 if(r <= l) 5 { 6 mergesort(a,l,m); 7 mergesort(a,m+1,r); 8 merge(a,l,m,r); 9 } 10 } 11 merge(Item * a, int l, int m, int r) 12 { 13 int i,j,k; 14 for(i = m+1; i > l; i--) 15 aux[i-1] = a[i-1]; 16 for(i = m; j < r;j++) 17 aux[r+m-j] = a[j+1]; 18 //上面兩行代碼,為的是生成bitonic序列,下面才是排序開始。 19 for(k = l; k < r;k++) 20 if(less(aux[j],aux[i])) 21 a[k] = aux[j--]; 22 else 23 a[k] = aux[i++]; 24 }
第三章:總結
心有千千結,心思總難知,這篇文章僅僅為心中自證,不是算法教學,也不想誤人子弟,謝謝各位。