归并排序
归并排序是另一种不同的排序方法,因为归并排序使用了递归分治的思想,所以理解起来比较容易。其基本思想是,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成由两个有序序列。。。。。倒着来看,其实就是先两两合并,然后四四合并。。。最终形成有序序列。空间复杂度为O(n),时间复杂度为O(nlogn)。
举个栗子:
#include <iostream> #include <vector> #include <algorithm> #include <limits.h> #include "Solution.h" using namespace std; void MergeArray(int array[], int start, int mid, int end, int temp[]) { int i = start; int j = mid + 1; int k = 0; while (i <= mid && j <= end ) { if (array[i] < array[j]) { temp[k++] = array[i++]; }else { temp[k++] = array[j++]; } } while (i <= mid) { temp[k++] = array[i++]; } while (j <= end) { temp[k++] = array[j++]; } for (int i = 0; i < k; i ++) { array[start + i] = temp[i]; } } // 归并排序,将数组前半部分后半部分分成最小单元,然后在合并 void MergeSort(int array[], int start, int end, int temp[]) { if(start < end) { int mid = (start + end)/ 2; MergeSort(array, start, mid, temp); MergeSort(array, mid + 1, end, temp); MergeArray(array, start, mid, end, temp); } } // 在这里创建临时数组,节省内存开销,因为以后的temp都是在递归李使用的。 void MergeSortmain(int array[], int len) { int start = 0; int end = len - 1; int *temp = new int[len]; MergeSort(array, start, end, temp); } void PrintArray(int array[], int len) { for (int i = 0 ; i < len; ++i) { cout << array[i] << " " ; } cout << endl; } int main() { int array[] = {3,5,3,6,7,3,7,8,1,2}; MergeSortmain(array, 10); PrintArray(array, 10); return 0; }
快速排序
快速排序一听名字就觉得很高端,在实际应用当中快速排序确实也是表现最好的排序算法。冒泡排序虽然高端,但其实其思想是来自冒泡排序,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。
举个栗子:对5,3,8,6,4这个无序序列进行快速排序,思路是右指针找比基准数小的,左指针找比基准数大的,交换之。
5,3,8,6,4 用5作为比较的基准,最终会把5小的移动到5的左边,比5大的移动到5的右边。
5,3,8,6,4 首先设置i,j两个指针分别指向两端,j指针先扫描(思考一下为什么?)4比5小停止。然后i扫描,8比5大停止。交换i,j位置。
5,3,4,6,8 然后j指针再扫描,这时j扫描4时两指针相遇。停止。然后交换4和基准数。
4,3,5,6,8 一次划分后达到了左边比5小,右边比5大的目的。之后对左右子序列递归排序,最终得到有序序列。
上面留下来了一个问题为什么一定要j指针先动呢?首先这也不是绝对的,这取决于基准数的位置,因为在最后两个指针相遇的时候,要交换基准数到相遇的位置。一般选取第一个数作为基准数,那么就是在左边,所以最后相遇的数要和基准数交换,那么相遇的数一定要比基准数小。所以j指针先移动才能先找到比基准数小的数。
快速排序是不稳定的,其时间平均时间复杂度是O(nlgn)。
实现代码:
#include <iostream> #include <vector> #include <algorithm> #include <limits.h> #include "Solution.h" using namespace std; int partition(vector<int> &vi, int low, int up) { int pivot = vi[up];//选择最后一个元素作为比较元素 int i = low-1;//这个慢速移动下标必须设定为比最小下表p小1,否则两个元素的序列比如2,1无法交换 for (int j = low; j < up; j++) { if(vi[j] <= pivot) { i++; swap(vi[i], vi[j]); } } swap(vi[i+1], vi[up]); return i+1; } //C++'s array range should be [low, up], the same as [low, up+1) void quickSort(vector<int> &vi, int low, int up) { if(low < up) { int mid = partition(vi, low, up); //Watch out! The mid position is on the place, so we don't need to consider it again. //That's why below is mid-1, not mid! Otherwise it will occur overflow error!!! quickSort(vi, low, mid-1); quickSort(vi, mid+1, up); } } void qSort(vector<int> &vi) { quickSort(vi, 0, vi.size()-1); } int main() { int a[] = {3,5,7,9,2,3,1,0,7,5,4}; vector<int> va(a, a+11); cout<<"Before quicksort: "; for(auto x:va) cout<<x<<" "; cout<<endl; qSort(va); cout<<"After quicksort: "; for(auto x:va) cout<<x<<" "; cout<<endl; return 0; }