排序算法
各种排序算法的平均时间复杂度和最好最坏情况,空间复杂度如图所示,本文主要总结下常用的效率高的三种排序算法,分别是快速排序,归并排序和堆排序。
快速排序
快排是基于分治的思想去实现的,每次找到中间的数据,再根据中间的数据分别排序左右两边,使得左边的数都小于(或大于)右边的数,然后再递归进入下一层循环去继续比较,具体实现代码如下所示:
void quick_sort(int a[], int l, int r){
if(l >= r) return;
int i = l - 1, j = r + 1, x = a[l + r >> 1];
while(i < j){
do i ++; while(a[i] < x);//do while是while中为真就继续执行,为假就退出。
do j --; while(a[j] > x);
if(i < j) swap(a[i], a[j]);
}
quick_sort(a, l, j); quick_sort(a, j + 1, r);
}
归并排序
归并排序也是基于分治的思想,使用数组中心位置来划分,分成最少最小的组合后再把左右两边分别和中间值比较,用一个新的数组去存取新排列后的数据,具体实现如下所示:
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
堆排序
堆可以想象成是一个完全二叉树,一般都会直接使用STL中的模板,在STL中的堆就是优先队列,下面代码分别为小根堆和大根堆还有重构的使用方法:
priority_queue<pair<int, int>> a//这个默认就是用大根堆的方式排序,从大到小,堆顶元素为大
priority_queue<pair<int, int>, vector<pair<int, int>> greater<pair<int, int>>> b//这个就是用小根堆的方式排列,从小到大,堆顶元素为小
//重构的写法
class mycompare{
public:
bool operator()(const pair<int, int>& a, const pair<int, int>& b){
if(a.first == b.first) //这里实现的效果是按照第一个元素的大小从大到小排列,小的在最上面
return a.second > b.second; //如果第一个元素相等就按照第二个元素排列,从大到小排列,小的在最上。
return a.first > b.first;
}
}
//重构完成之后这样使用:
priority_queue<pair<int, int>, vector<pair<int, int>>, mycompare> heap;
sort函数
sort并不是简单的快速排序,它对快速排序进行了优化。此外,它还结合了插入排序和推排序。系统会根据数据形式和数据量自动选择合适的排序方法。它每次排序中不只选择一种方法,比如给一个数据量较大的数组排序,开始采用快速排序,分段递归,分段之后每一段的数据量达到一个较小值后它就不继续往下递归,而是选择插入排序(插入排序的基本思想是每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止),如果递归的太深,他会选择推排序。
vector<int> nums;
sort(nums.begin(), nums.end());//升序
sort(nums.end(), nums.begin());//降序