十大排序算法对比和C++实现
Posted mingogo乔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十大排序算法对比和C++实现相关的知识,希望对你有一定的参考价值。
十大排序算法复杂度对比和C++实现--更新中
一、性能对比
算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 稳定性 | 优点 | 缺点 |
---|---|---|---|---|---|---|
插入排序 | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( n 2 ) O(n^2) O(n2) | 稳定 | 比较次数少;适用于基本有序的数列 | 交换次数多,插入成本大 |
选择排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | 稳定 | 交换次数少 | 比较次数多,任何情况都多 |
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | 稳定 | - | 费时费力 |
堆排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | 不稳定 | ||
快速排序 | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n l o g n ) O(nlogn) O(nlogn) | O ( n 2 ) O(n^2) O(n2) | 不稳定 | ||
希尔排序 | < O ( n 2 ) <O(n^2) <O(n2) | < O ( n 2 ) <O(n^2) <O(n2) | < O ( n 2 ) <O(n^2) <O(n2) | 不稳定 | ||
归并排序 | ||||||
基数排序 | ||||||
计数排序 |
二、基本的排序算法实现
1. 插入排序
将第i个元素作为label,插入到 i 之前的合适位置;
每一趟排序结束后,i 位置前元素有序;
缺点是需要进行多次元素的赋值,如果有数据插入,所有之前比它大的数字都要移动;
优点是有需要时候才会进行排序,最好情况下只需要
O
(
n
)
O(n)
O(n)次比较,不需要移动,适合用在元素基本有序的情况。
void insertionsort(vector<int>& data) {
int sz = data.size();
for (int i = 1; i < sz; ++i) {
int label = data[i],j = i;
for (; j > 0 && data[j - 1] > label; --j) {
data[j] = data[j - 1];
}
data[j] = label;
}
}
2. 选择排序
在第i个元素之后选择最小的元素,放在第i个位置;
每一趟排序后,第i个元素前元素全局有序;
进行元素的交换;
优点是赋值次数少;
缺点是最好情况和最坏情况复杂度相同,不论任何情况都要进行
O
(
n
2
)
O(n^2)
O(n2)次比较。
void selection(vector<int>& data) {
int sz = data.size();
for (int i = 0; i < sz; ++i) {
int label = i;//最小元素的坐标
for (int j = i + 1; j < sz; ++j) {
if (data[j] < data[label]) {
label = j;
}
}
swap(data, i, label);
}
}
3. 冒泡排序
笨蛋版本的选择排序,每趟排序后的结果和选择排序相同,但增加了很多交换次数。
貌似没有发现什么优点的亚子。。。
缺点是费时费力,和选择排序相同,不论任何情况都要进行
O
(
n
2
)
O(n^2)
O(n2)次比较,交换次数又和需要多次赋值的插入排序相同。
void bubblesort(vector<int>& data) {
int sz = data.size();
for (int i = 0; i < sz; ++i) {
for (int j = sz - 1; j > i; --j) {
if (data[j] < data[j - 1]) {
swap(data, j, j - 1);
}
}
}
}
三、高效的排序算法实现
1. 希尔排序
基本思路就是分成子序列进行预排序,间隔从大到小,每种间隔h分别有h个子序列。
复杂度比较难于分析,大致是 O ( n m ) , 1 < m < 2 O(n^m),1<m<2 O(nm),1<m<2
void shellsort(vector<int>&data){
int sz = data.size();
//构建最优的间隔序列
stack<int> H;
int h = 1;
while(h < sz){
H.push(h);
h = 3 * h + 1;
}
//从大到小遍历间隔序列
while(!H.empty()){
h = H.back();
H.pop();
//每种间隔有h个子序列
for(int j = h; j < 2*h && j < sz; ++j){
//对每个子序列进行排序,考虑到序列元素较少且基本有序,这里使用插入排序
for(int k = j; k < sz; k += h){
int label = data[k], t = k;
for(; t-h >= 0 && data[t-h] > label; t -= h){
data[t] = data[t-h];
}
data[t] = label;
}
}
}
}
2. 堆排序
要排序的数列是一个层序遍历的树;
维护一个大顶堆,每次交换队首元素和队尾元素,即,将原本位于队首的最大的元素置于最后,然后将大顶堆size-1,进行更新维持为大顶堆。
简要理解其复杂度,大致需要进行 n n n 次交换队首和队尾元素,每次交换后更新树,需要进行树的深度(即 l o g i logi logi )次的最大元素上传,总复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
C++中有priority_queue容器,本文不使用容器手撕大顶堆。
void heapify(vector<int>& data, int i, int size) {
if (i > (size - 1) / 2) {
return;
}
//对当前子堆进行堆排序,找到最大值
int max = i;
if (2*i + 1 < size && data[2*i + 1] > data[max]) {
max = 2*i + 1;
}
if (2*i + 2 < size && data[2*i + 2] > data[max]) {
max = 2*i + 2;
}
if (max != i) {
swap(data, i, max);
//进行交换后,可能破坏了已经完成的下层堆排序,需要对交换过的节点进行,进行一个推排序递归
heapify(data, max, size);
}
}
void buildheap(vector<int>& data, int size) {
//从底而上遍历根节点们
for (int i = (size-1) / 2 ; i >= 0; --i) {
heapify(data, i, size);
}
}
//排序
void heapsort(vector<int>& data) {
int sz = data.size();
while (sz > 0) {
buildheap(data, sz);
swap(data, 0, sz-1);
sz--;
}
}
3. 快速排序
选择一个bound,所有比它小的元素都在左边,比它大的元素都在右边,然后对左右序列递归。
通过划分左右序列,减少比较次数。
最优情况下每次划分都分在中间,划分了
l
o
g
n
logn
logn 次;最劣情况下每次有一个子序列只有1个元素,划分了
n
n
n 次。
每次划分的子序列加起来进行了
n
n
n 次比较,因此,最优时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),最差为
O
(
n
2
)
O(n^2)
O(n2) 。
一般情况的复杂度接近最优情况,为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
双指针写的快排算法,额外空间复杂度为 O ( 1 ) O(1) O(1) 。
void quicksort(vector<int>&data,int first, int last){
swap(nums, first, (first+last)>>1);
int bound = data[first], lower = first + 1, upper = last;
while(lower <= upper){
while(lower <= last && data[lower] <= bound) ++lower;
while(upper > first && data[upper] >= bound) --upper;
if(lower < upper) swap(data, lower, upper);
}
//upper右边的元素都>=bound
//upper此时指向的值小于bound, 也就是first位置的值
swap(data, first, upper);
//此时upper位置就是bound,判断它的左右两边
//如果不止一个元素,就递归进行快排
if(upper-1 > first) quicksort(data, first, upper-1);
if(upper+1 < last) quicksort(data, upper+1, last);
}
//调用
//quicksort(d, 0, d.size()-1);
4. 归并排序
5. 基数排序
6. 计数排序
以上是关于十大排序算法对比和C++实现的主要内容,如果未能解决你的问题,请参考以下文章