排序方法的分类和实现
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]交换的时候)、希尔排序、堆排序