前置说明
分类
- 简单排序:冒泡、选择、插入
- 进阶插入:希尔排序
- 分治排序:快速、归并
- 堆排序:基于堆
核心概念
- 稳定排序:相等元素相对顺序不变(冒泡、插入、归并)
- 不稳定:选择、希尔、快排、堆排
1. 冒泡排序 BubbleSort
原理
相邻元素两两比较,逆序则交换;每一轮把最大值 "浮" 到末尾。优化:标记本轮是否交换,无交换直接退出(有序提前终止)。
#include <iostream>
#include <vector>
using namespace std;
void BubbleSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; ++i) {
bool swapped = false;
// 每轮末尾i个已排好,不用比较
for (int j = 0; j < n - 1 - i; ++j) {
if (arr[j] > arr[j+1]) {
swap(arr[j], arr[j+1]);
swapped = true;
}
}
if (!swapped) break; // 全程无交换,已有序
}
}
// 测试
int main() {
vector<int> a = {3,1,4,1,5,9,2,6};
BubbleSort(a);
for (int x : a) cout << x << " ";
return 0;
}
时间复杂度:
- 最好(完全有序):O(n),一次遍历无交换直接退出
- 平均 / 最坏(完全逆序):O(n2)
2. 选择排序 SelectSort
原理
分有序区 、无序区;每轮遍历无序区找到最小值,和无序区首元素交换。
void selectSort(vector<int>& arr)
{
int n = arr.size();
for (int i = 0; i < n - 1; i++)
{
int min=i;
for (int j = i; j < n; j++)
{
if (arr[j] < arr[min])
{
min = j;
}
}
swap(arr[min], arr[i]);
}
时间复杂度
无论有序 / 无序,最好 / 平均 / 最坏全是 O(n2);全程必须完整遍历找最小值。
3. 插入排序 InsertSort
原理
左侧是有序数组,依次取右侧元素向前插入到合适位置
void insertSort(vector<int> &arr)
{
int n = arr.size();
for (int i = 1; i < n; i++)
{
int tmp = arr[i];
int j;
for (j = i - 1; j >= 0; j--)
{
if (arr[j] > tmp)
{
arr[j + 1] = arr[j];
}
else
break;
}
arr[j+1] = tmp;
}
}
时间复杂度
- 最好(完全有序):O(n),仅比较不移动;
- 平均 / 最坏(逆序):O(n2)。
4. 希尔排序 ShellSort(插入排序优化)
希尔排序是插入排序的优化版本。
原理
分组插入排序,设置间隔 gap,每组单独插入排序;不断缩小 gap 至 1,退化为普通插入。
void shellSort(vector<int>& arr) {
int n = arr.size();
for (int gap = n / 2; gap > 0; gap /= 2)
{
for (int i = gap; i < n; i++)
{
int tmp = arr[i];
int j;
for (j = i - gap; j >= 0; j-=gap)
{
if (arr[j] > tmp)
arr[j + gap] = arr[j];
else
break;
}
arr[j+gap] = tmp;
}
}
}
时间复杂度
- 最好:O(n);
- 平均:O(n1.3)(二分增量序列);
- 最坏:O(n2)。
5. 快速排序 QuickSort
快排详解(这里面讲解了快排的几种实现方法):
原理:分治
- 选基准 pivot
- 分区:小于 pivot 放左,大于放右
- 递归排序左右子区间
挖坑法:
void QS(vector<int>& arr, int left, int right)
{
if (left > right)
return;
int key = arr[left];
int begin = left, end = right;
while (left < right)
{
while (left < right && arr[right]>=key)
{
right--;
}
arr[left] = arr[right];
while (left < right &&arr[left]<=key)
{
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
QS(arr,begin, left - 1);
QS(arr, left + 1, end);
}
void QuickSort(vector<int> &arr)
{
QS(arr, 0, arr.size() - 1);
}
时间复杂度
- 最好 / 平均(基准平分区间):O(nlogn);
- 最坏(有序 / 逆序,基准取端点):O(n2)。
随机基准优化
int partition_rand(vector<int>& arr, int l, int r) {
int randIdx = l + rand() % (r - l + 1);
swap(arr[randIdx], arr[r]);
return partition(arr, l, r);
}
6. 归并排序 MergeSort(稳定 O (nlogn))
原理:分治
- 二分拆分数组直到单个元素
- 有序合并两个子数组,需要辅助数组
C++ 完整代码
void _MergeSort(vector<int>& arr,vector<int> &vec,int left,int right)
{
if (right == left)
return;
int mid = (left + right) / 2;
_MergeSort(arr, vec, left, mid);
_MergeSort(arr, vec, mid + 1, right);
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int i=left;
//将两段有序序列归并成一段
while (begin1 <= end1 && begin2 <= end2)
{
if (arr[begin1] <= arr[begin2])
vec[i++] = arr[begin1++];
else
vec[i++] = arr[begin2++];
}
//将剩下的数组添加到新数组尾部
while (begin1 <= end1)
{
vec[i++] = arr[begin1++];
}
while (begin2 <= end2)
{
vec[i++] = arr[begin2++];
}
//拷贝
for (int i = left; i <=right; i++)
{
arr[i] = vec[i];
}
}
void MergeSort(vector<int> &arr)
{
int n = arr.size();
vector<int> vec(n);
_MergeSort(arr,vec,0,n-1);
}
时间复杂度
最好 / 平均 / 最坏稳定 O(nlogn),无退化风险。
7. 堆排序 HeapSort
原理
数组模拟完全二叉树构建大顶堆;堆顶为最大值,交换到数组末尾缩小堆,反复下沉调整堆。
- 父 i → 左孩子
2*i+1,右2*i+2 - 孩子 i → 父
(i-1)/2
C++ 代码
// 下沉调整大顶堆
void heapify(vector<int>& arr, int n, int i) {
int largest = i;
int left = 2*i + 1;
int right = 2*i + 2;
if (left < n && arr[left] > arr[largest])
largest = left;
if (right < n && arr[right] > arr[largest])
largest = right;
if (largest != i) {
swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void HeapSort(vector<int>& arr) {
int n = arr.size();
// 1. 建大顶堆:从最后一个父节点向前遍历
for (int i = n/2 - 1; i >= 0; --i)
heapify(arr, n, i);
// 2. 堆顶放末尾,调整堆
for (int i = n - 1; i > 0; --i) {
swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
时间复杂度
最好 / 平均 / 最坏全部稳定 O(nlogn)。