1、冒泡排序
平均时间复杂度:O(n^2)
最差时间复杂度:O(n^2)
空间复杂度:O(1)
是否稳定:是
原理:它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
// 当数列本身有序,只需要扫描一遍即可
void BubbleSortFlag(vector<int>& vec) {
for (int i = 0; i < vec.size() - 1; i++) {
bool swapFlag = true;
for (int j = 0; j < vec.size() - 1 - i; j++) {
if (vec[j] > vec[j + 1]) {
swap(vec[j], vec[j + 1]);
swapFlag = false;
}
}
if (swapFlag) break;
}
}
2、插入排序
平均时间复杂度:O(n^2)
最差时间复杂度:O(n^2)
空间复杂度:O(1)
是否稳定:是
原理:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
//从(右侧)无序序列中顺序选取数据插入到(左侧)有序序列中
void InsertSort(vector<int>& vec) {
// 默认第一个元素是有序的,只需要对剩下的 n-1 个数进行插入操作,即扫描n-1次,所以,只需要从 i = 1 开始
for (int i = 1; i < vec.size(); i++) {
int temp = vec[i];
int j=i-1;
// 从有序序列尾部开始,方便挪动,i 之前都是有序的
for (; j >= 0; j--) {//查找插入的位置
if(vec[j]>temp) vec[j + 1] = vec[j];//数据移动
else break;
}
vec[j + 1] = temp; // 插入
}
}
3、选择排序
平均时间复杂度:O(n^2)
最差时间复杂度:O(n^2)
空间复杂度:O(1)
是否稳定:否
原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
void SelectSort(vector<int>& vec) {
// 遍历n-1次
for (int i = 0; i < vec.size() - 1; i++) {
int minn = i;
for (int j = i + 1; j < vec.size(); j++) {//查找最小值所在的位置
if (vec[j] < vec[minn]) minn = j;
}
if(minn != i)
swap(vec[minn], vec[i]); // 把最值放在起始位置
}
}
4、归并排序
平均时间复杂度:O(nlog2n)
最差时间复杂度:O(nlog2n)
空间复杂度:O(n)
是否稳定:是
原理:采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。分割:递归地把当前序列平均分割成两半。集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。
//合并两个有序数组,下标分别为 l~mid 以及 mid+1~r
void merge(vector<int>& vec, int l, int mid, int r) {
vector<int> temp(r - l + 1);
int i=l,j=mid+1,k=0;
while (i <= mid && j<=r) {
if (vec[i] < vec[j]) {
temp[k++] = vec[i++];
}
else {
temp[k++] = vec[j++];
}
}
while (i <= mid) {
temp[k++] = vec[i++];
}
while (j <= r) {
temp[k++] = vec[j++];
}
//将temp中的内容copy回vec[l...r]中
for(i = 0; i < r-l+1; i++){
vec[l+i] = temp[i];
}
}
//传统递归分治
void MergeSort(vector<int>& vec, int l, int r) {
if (l >= r) return;
int mid = l + (r - l) / 2;
MergeSort(vec, l, mid);
MergeSort(vec, mid + 1, r);
//将vec[1...mid]和vec[mid+1...r]合并为vec[l...r]
merge(vec, l, mid, r);
}
5、快速排序
平均时间复杂度:O(nlog2n)
最差时间复杂度:O(n^2)
空间复杂度:O(log2n)
是否稳定:否
tip:数组小的时候采用插入排序效果更高 。快排每次递归都有一个数放在最终的位置。
// 先从右开始向左寻找比 pivot 小的数,然后从左向右寻找比 pivot 大的数,然后交换,重复以上步骤,直到i >= j
int partition(vector<int>& vec, int l, int r) {
int pivot = vec[l];
int i = l, j = r; // i = l+1 不可以,当 l+1=r 的时候, 1、2两个数会发生交换,实际上不应该交换,而i=l,因为计算j的位置,i的位置就不会发生变换,即使交换也是原地交换
while (i < j) {
while (i < j && vec[j] >= pivot) j--;
while (i < j && vec[i] <= pivot) i++; // 为什么等于号,因为 i 从l开始,和自身比较,如果不考虑相等,i 少前进一位
if (i < j) swap(vec[i], vec[j]);
}
if (l != i) {
vec[l] = vec[i];
vec[i] = pivot;
}
return i;
}
void QuickSort(vector<int>& vec, int l, int r) {
if (l >= r) return;
int q = partition(vec, l, r);//获取分区点下标
QuickSort(vec, l, q-1);
QuickSort(vec, q + 1, r);
}
void sort(vector<int>& vec) {
QuickSort(vec, 0, vec.size()-1);
}