排序算法
一、排序
对n个数据执行某种操作,使其按照某种规则有序的排列,这样的操作就是排序。
二、排序算法分类
内排序与外排序
按照排序过程中数据是否全放在内存中,可以分为内排序和外排序
交插选并
其中内排序又分为插入排序、交换排序、选择排序、归并排序,总结为交插选并。
三、算法性能:
一、衡量标准
时间性能:比较和移动的操作次数
辅助空间:算法执行开辟的额外空间。
算法复杂性:算法本身的复杂度
二、七种排序算法
1、冒泡排序
算法思想:假设有n个数据,依次比较相邻两个元素,如果反序则交换位置,直到没有反序为止。
最好时间复杂度O(n)
最坏时间复杂度O(n2)
for(int i = 0; i < length - 1; l++){
for(int j = length - 2; j >= i; j--){
if(a[j] > a[j + 1]){
int temp = a[j];
a[j] = a[j - 1];
a[j + 1] = temp;
}
}
}
2、选择排序
算法思想:假设有n个元素,每次在n-i+1个元素中选出最小的元素,并和第i个元素交换。
优点:交换次数少
时间复杂度O(n2),性能优于冒泡排序法。
3、直接插入排序
将一个元素插入到有序表中。
时间复杂度O(n2),性能优于选择排序算法
基本有序和数据量较少时,效率很高。
4、希尔排序
5、堆排序(heap sort)
堆:堆是一棵完全二叉树。如果每个结点不小于孩子结点的值,称为大顶堆。如果每个结点不大于孩子结点的值,称为小顶堆
{
}
\{\}
{}
- 调整序列称为一个堆
- 输出根结点元素,并调整剩余元素成为一个堆。
// <font face = "微软雅黑" color = red size = 5>堆调整函数</font>
void HeapAdjust(std::vector<int>& vec, int begin, int end) {
vec[0] = vec[begin];
for (int i = 2 * begin; i <= end; i = 2 * i) {
if ((i < end) && (vec[i] < vec[i+1])) {
i++;
}
if (vec[0] >= vec[i]) {
break;
}
vec[begin] = vec[i];
vec[i] = vec[0];
begin = i;
}
}
// <font face = "微软雅黑" color = red size = 5>堆排序函数</font>
void HeapSort(std::vector<int>& vec, int size) {
//创建堆结构
for (int i = size / 2; i > 0; i --) {
HeapAdjust(vec, i, size);
}
//调整堆结构
for (int j = size; j > 0; j--) {
// <font face = "微软雅黑" color = red size = 5>交换首尾元素</font>
vec[0] = vec[1];
vec[1] = vec[j];
vec[j] = vec[0];
// <font face = "微软雅黑" color = red size = 5>调整剩余元素为堆</font>
HeapAdjust(vec, 1, j-1);
}
}
int main(int argc, char* argv[]) {
std::vector<int> vec(11);
for (int i = 0; i < 11; i++) {
vec[i] = 10 - i;
std::cout << vec[i] << std::endl;
}
HeapSort(vec, vec.size() - 1);
for (int i = 0; i < 11; i++) {
std::cout << vec[i] << std::endl;
}
return 0;
}
时间复杂度O(nlogn),性能优于前面的算法。
初始化堆的时间复杂度为O(n),重建堆的时间复杂度为O(nlogn),总的时间复杂度为O(nlogn)。
6、归并排序
时间复杂度:O(nlogn)
每一趟需要对n个元素遍历,一共需要logn次遍历,所以时间复杂度为O(nlogn)
空间复杂度:O(n + logn)
归并排序需要同样数量的内存空间存储排序结果,以及logn大小的占空间存储临时结果。
总的来说,归并排序是一种占用内存较多,但是效率高且稳定的算法。