排序方法的分類和實現
1.冒泡排序
冒泡排序的原理是對臨近的兩個數字進行比較,按照從小到大或者從大到小的順序進行交換,這樣一趟過去後,最大或者最小的數字就被交換到了最後一位了。然後再從頭開始進行這種比較和交換,一直到完成排序。
代碼如下:
void BubbleSort(vector<int> &unsorted) { for (int i = 0; i < unsorted.size() - 1; i++) { for (int j = 0; j < unsorted.size() - 1; j++) { if (unsorted[j] > unsorted[j + 1]) { int temp = unsorted[j]; unsorted[j] = unsorted[j + 1]; unsorted[j + 1] = temp; } } } }
要註意的是,所謂冒泡,就是要相鄰的兩個元素比較,假如一個數是最大的,它會一直冒泡到最上面。網上很多關於冒泡排序的代碼都不是真的冒泡排序,真正的代碼肯定是unsorted[j]和unsorted[j+1]比較的。
時間復雜度:O(n2)
2.選擇排序
選擇排序就是從第一位開始遍歷一次整個序列,選擇最小的值放在第一位。接著從第二位開始進行同樣的操作,重復這種操作一直到最後一位。
代碼如下:
void SelectSort(vector<int> &unsorted) { for (int i = 0; i < unsorted.size() - 1; i++) { int min = unsorted[i]; for (int j = i + 1; j < unsorted.size(); j++) { if (min > unsorted[j]) { int temp = min; min = unsorted[j]; unsorted[j]= temp; } } unsorted[i] = min; } }
時間復雜度:O(n2)
3.插入排序
插入排序就是將一個數據插入到已經排好序的一個序列中,為它找到一個合適的位置,形成一個新的有序序列。在實現中,我們先假設這個序列的大小為1(那肯定是有序的了),後面就一直將當前元素插入到合適的位置就可以了。
代碼如下:
void InsertSort(vector<int> &unsorted) { for (int i = 1; i < unsorted.size(); i++) { int j = i; while (j > 0 && unsorted[j] < unsorted[j - 1]) { int temp = unsorted[j]; unsorted[j] = unsorted[j - 1]; unsorted[j - 1] = temp; j--; } } }
時間復雜度:O(n2)
4.歸並排序
歸並排序首先利用到的方法,是將兩個有序的序列合並。只要比較兩個序列的第一個數,誰小就先取誰,取了之後就在對應的序列中刪除這個數。然後再進行比較,如果有序列為空,那就直接將另一個序列的數據依次取出即可。
知道了如何將兩個有序序列合並後,我們可以采用分治的方法,將一個要排序的數組一分二,二分四,一直到分成單個數為止(這時就是有序的了),然後用上面的方法進行合並,最終的到排序後的數組。
代碼如下:
void MergeArray(vector<int> &unsorted, int bottom, int mid, int top) { int i = bottom, j = mid + 1, m = mid, n = top; vector<int> temp(unsorted.size()); int k = 0; while (i <= m && j <= n) { if (unsorted[i] <= unsorted[j]) temp[k++] = unsorted[i++]; else temp[k++] = unsorted[j++]; } while (i <= m) temp[k++] = unsorted[i++]; while (j <= n) temp[k++] = unsorted[j++]; for (int i = 0; i < k; i++) { unsorted[bottom + i] = temp[i]; } } void MergeSort(vector<int> &unsorted, int bottom, int top) { if (bottom < top) { int mid = (bottom + top) / 2; MergeSort(unsorted, bottom, mid); MergeSort(unsorted, mid + 1, top); MergeArray(unsorted, bottom, mid, top); } }
剛開始很難理解的一點是:怎麽把一個數組“一分成二”?看代碼就懂了,采用遞歸的方法。
時間復雜度:O(nlogn)
5.快速排序
也是采用分治的方法。找基準點、把比基準點小的數放到它的左邊,大的放在右邊、遞歸下去即可。具體的解釋可以看:http://developer.51cto.com/art/201403/430986.htm
代碼如下:
void QuickSort(vector<int> &unsorted, int first, int last) { if (first < last) { int i = first/*假如first + 1,考慮只剩下兩個正確數的情況,會交換變為錯的;*/, j = last; int key = unsorted[first]; while (i < j) { while (i < j && unsorted[j] >= key) { j--; } while (i < j && unsorted[i] <= key) { i++; } if (i < j) { int temp = unsorted[i]; unsorted[i] = unsorted[j]; unsorted[j] = temp; } } int temp = unsorted[first]; unsorted[first] = unsorted[i]; unsorted[i] = temp; QuickSort(unsorted, first, i - 1); QuickSort(unsorted, i + 1, last); } }
代碼中循環的地方為什麽要先處理j呢?我們的基準點選的是第一個元素,最後要將基準點與i與j相遇的點交換,顯然這個點交換後為與基準點的左邊,因此它的值需要比基準點值小。假如我們先處理i,這個值會比基準點的值大,排序失敗。
時間復雜度:O(nlogn)
6.桶排序
這是一種用空間換時間的方法,代碼如下:
void BucketSort(vector<int> &unsorted) { int * arr = new int[200001]; //-100000到100000 for (int i = 0; i < 200000; i++) { arr[i] = 0; } for (int i = 0; i < unsorted.size(); i++) { arr[unsorted[i] + 100000]++; } int k = 0; for (int i = 0; i < 200000; i++) { while (arr[i]) { arr[i]--; unsorted[k++] = i - 100000; } } delete [] arr; }
這種思想在很多地方都運用到。
另外,采用C++的容器去當“桶”會好一點。
這篇文章沒有提到所有的排序方法,排序方法還有:堆排序、基數排序、希爾排序……因為我不會。
**排序方法是否穩定?**
假如一種排序方法能保證排序前2個相等的數其在序列的前後位置順序和排序後它們兩個的前後位置順序相同,那這種排序算法就是穩定的。
會不會判斷一個排序算法是否穩定就看理不理解這個算法了。
直接給出結果:
穩定的:冒泡排序、插入排序、歸並排序、基數排序
不穩定的:選擇排序、快速排序(破壞穩定性發生在基準點和unsorted[i]交換的時候)、希爾排序、堆排序
排序方法的分類和實現