原理:通过相邻元素之间的比较和交换,将最大(小)的元素逐步 “冒泡” 到序列的末尾。每一趟比较都能确定一个最大(小)元素的最终位置。
时间复杂度:平均时间复杂度,最好时间复杂度
,最坏时间复杂度
。
空间复杂度:
稳定性:稳定
原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
时间复杂度:平均时间复杂度,最好和最坏时间复杂度都为
。
空间复杂度:
稳定性:不稳定
原理:将前面的数据看作已经排序完成的有序数据,将后面的数据插入前面数据中首个小于等于它自身的位置之后,重复上述过程直到数据有序。(注:若数据本身趋于有序,插入排序是所有排序算法中,效率最高的排序算法)
时间复杂度:平均时间复杂度,最好时间复杂度
,最坏时间复杂度
。
空间复杂度:。
稳定性:稳定
原理:让数据先趋于有序,再对数据进行插入排序。
时间复杂度:平均时间复杂度
空间复杂度:。
稳定性;不稳定
上述四种排序算法比较;
基本算法 | 空间复杂度 | 时间复杂度 | 稳定性 |
冒泡排序 | ![]() |
![]() |
稳定 |
选择排序 | ![]() |
![]() |
不稳定 |
插入排序 | ![]() |
![]() |
稳定 |
希尔排序 | ![]() |
![]() |
不稳定 |
基本算法 | 100 | 1000 | 10000 | 100000 |
冒泡排序 | 2.4e-05s | 0.001799s | 0.152341s | 24.7368s |
选择排序 | 1.3e-05s | 0.00049s | 0.041018s | 4.57467s |
插入排序 | 6e-06s | 0.000367s | 0.03789s | 3.95476s |
希尔排序 | 6e-06s | 0.000112s | 0.001235s | 0.023815s |
代码如下;
#include
#include
#include
#include
using namespace std;
//冒泡排序
void BubbleSort(int arr[], int size)
{
for (int i = 0; i < size; i++)
{
bool flag = false;
for (int j = i; j < size - 1; j++)
{
if (arr[j] >= arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = true;
}
}
if (!flag)
{
break;
}
}
}
//选择排序
void ChoiceSort(int arr[], int size)
{
for (int i = 0; i < size; i++)
{
int val = arr[i];
int k = i;
for (int j = i + 1; j < size; j++)
{
if (arr[j] < val)
{
val = arr[j];
k = j;
}
}
if (k != i)
{
int tmp = arr[i];
arr[i] = arr[k];
arr[k] = tmp;
}
}
}
//插入排序
void InsertSort(int arr[], int size)
{
for (int i = 1; i < size; i++)
{
int val = arr[i];
int j = i - 1;
for (; j >= 0; j--)
{
if (arr[j] <= val)
{
break;
}
arr[j + 1] = arr[j];
}
arr[j + 1] = val;
}
}
//希尔排序
void ShellSort(int arr[], int size)
{
int gap = size / 2;
for (; gap > 0; gap /= 2)
{
for (int i = gap; i < size; i++)
{
int val = arr[i];
int j = i - gap;
for (; j >= 0; j -= gap)
{
if (arr[j] <= val)
{
break;
}
arr[j + gap] = arr[j];
}
arr[j + gap] = val;
}
}
}
// 复制数组
void copyArray(int source[], int destination[], int size) {
for (int i = 0; i < size; i++) {
destination[i] = source[i];
}
}
// 比较四种排序算法的运行时间
void compareSortingTimes(int arr[], int size) {
int* arrCopy = new int[size];
// 冒泡排序计时
copyArray(arr, arrCopy, size);
auto start = std::chrono::high_resolution_clock::now();
BubbleSort(arrCopy, size);
auto end = std::chrono::high_resolution_clock::now();
auto durationBubble = std::chrono::duration_cast(end - start).count();
std::cout << "冒泡排序运行时间: " << static_cast(durationBubble) / 1000000 << " 秒" << std::endl;
// 选择排序计时
copyArray(arr, arrCopy, size);
start = std::chrono::high_resolution_clock::now();
ChoiceSort(arrCopy, size);
end = std::chrono::high_resolution_clock::now();
auto durationChoice = std::chrono::duration_cast(end - start).count();
std::cout << "选择排序运行时间: " << static_cast(durationChoice) / 1000000 << " 秒" << std::endl;
// 插入排序计时
copyArray(arr, arrCopy, size);
start = std::chrono::high_resolution_clock::now();
InsertSort(arrCopy, size);
end = std::chrono::high_resolution_clock::now();
auto durationInsert = std::chrono::duration_cast(end - start).count();
std::cout << "插入排序运行时间: " << static_cast(durationInsert) / 1000000 << " 秒" << std::endl;
// 希尔排序计时
copyArray(arr, arrCopy, size);
start = std::chrono::high_resolution_clock::now();
ShellSort(arrCopy, size);
end = std::chrono::high_resolution_clock::now();
auto durationShell = std::chrono::duration_cast(end - start).count();
std::cout << "希尔排序运行时间: " << static_cast(durationShell) / 1000000 << " 秒" << std::endl;
delete[] arrCopy;
}
int main()
{
const int size = 1000000;
int arr[size];
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 1000000);
// 生成随机数组
for (int i = 0; i < size; i++) {
arr[i] = dis(gen);
}
// 比较四种排序算法的运行时间
compareSortingTimes(arr, size);
return 0;
}
原理:选择一个合适的基准数,把小于基准数的元素调整到基准数的左边,把大于基准数的数字调整到基准数的右边。然后对基准数的两边重复上述操作,直到数据有序。
时间复杂度:平均时间复杂度,最好时间复杂度
,最坏时间复杂度
空间复杂度:最好空间复杂度,最坏空间复杂度
稳定性:不稳定
原理:采用分治思想,把序列划分对每个子数组进行排序再进行元素的有序合并。
时间复杂度:平均时间复杂度,最好和最坏时间复杂度
。
空间复杂度:
稳定性:稳定
原理:将待排序序列构造成一个大顶堆或小顶堆,此时,整个序列的最大值或最小值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值或最小值。然后将剩余 个元素重新构造成一个堆,这样会得到
个元素的次大值或次小值。如此反复执行,便能得到一个有序序列。
时间复杂度:平均时间复杂度,最好和最坏时间复杂度
。
空间复杂度:
稳定性:不稳定
上述算法比较:
基本算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
快速排序 | ![]() |
![]() |
不稳定 |
归并排序 | ![]() |
![]() |
稳定 |
堆排序 | ![]() |
![]() |
不稳定 |
基本算法 | 10000 | 100000 | 1000000 | 10000000 | 100000000 |
快速排序 | 0s | 0.007s | 0.0612s | 0.6634s | 9.591s |
归并排序 | 0.0038s | 0.03s | 0.2642s | 2.7254s | 40.065s |
堆排序 | 0.001s | 0.012s | 0.122s | 1.7928s | 29.8044s |
代码如下:
#include
#include
#include
#include
using namespace std;
//快速排序
int Pos(int arr[], int begin, int end)
{
int val = arr[begin];
while (begin < end)
{
while (begin val)
{
end--;
}
if (begin < end)
{
arr[begin] = arr[end];
begin++;
}
while (begin < end && arr[begin] < val)
{
begin++;
}
if (begin < end)
{
arr[end] = arr[begin];
end--;
}
}
arr[begin] = val;
return begin;
}
void InsertSort(int arr[], int begin, int end)
{
int size = sizeof arr / sizeof arr[0];
for (int i = 0; i < size; i++)
{
int val = arr[i];
int j = i - 1;
for (; j >= 0; j--)
{
if (arr[j] > val)
{
return;
}
arr[j + 1] = arr[j];
}
arr[j + 1] = val;
}
}
void QuickSort(int arr[], int i, int j)
{
if (i >= j)//快排递归结束的条件
{
return;
}
//在[i,j]之间做一次快排分割
//插入排序
if (j - i < 20)
{
InsertSort(arr, i, j);
return;
}
int pos = Pos(arr, i, j);
//对基准数的左右分别进行快排
QuickSort(arr, i, pos - 1);
QuickSort(arr, pos + 1, j);
}
void QuickSort(int arr[], int size)
{
return QuickSort(arr, 0, size - 1);
}
//归并排序
void Merge(int arr[], int l, int m, int r)
{
int* p = new int[r - l + 1];
int i = l;
int j = m + 1;
int index = 0;
while (i <= m && j <= r)
{
if (arr[i] <= arr[j])
{
p[index++] = arr[i++];
}
else
{
p[index++] = arr[j++];
}
}
while (i <= m)
{
p[index++] = arr[i++];
}
while (j <= r)
{
p[index++] = arr[j++];
}
for (int i = l, j = 0; i <= r; i++, j++)
{
arr[i] = p[j];
}
delete[]p;
}
void MergeSort(int arr[], int begin, int end)
{
if (begin >= end)
{
return;
}
int mid = (begin + end) / 2;
MergeSort(arr, begin, mid);
MergeSort(arr, mid + 1, end);
Merge(arr, begin, mid, end);
}
void MergeSort(int arr[], int size)
{
MergeSort(arr, 0, size - 1);
}
//堆排序
// 向下调整堆
void SiftDown(int arr[], int k, int size) {
int val = arr[k];
while (k < size / 2)
{
int child = 2 * k + 1;
if (child + 1 < size && arr[child + 1] > arr[child])
{
child = child + 1;
}
if (arr[child] > val)
{
arr[k] = arr[child];
k = child;
}
else
{
break;
}
}
arr[k] = val;
}
// 堆排序
void HeapSort(int arr[], int size)
{
int n = size - 1;
// 构建最大堆
for (int i = (n - 1) / 2; i >= 0; i--)
{
SiftDown(arr, i, size);
}
// 排序过程
for (int i = n; i > 0; i--)
{
// 交换堆顶元素和当前堆的最后一个元素
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
// 调整堆
SiftDown(arr, 0, i);
}
}
// 比较排序算法运行时间的函数
void compareSortingTimes(int size) {
std::cout << "数据规模: " << size << std::endl;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 1000);
double totalQuick = 0;
double totalMerge = 0;
double totalHeap = 0;
for (int trial = 0; trial < 5; ++trial) {
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = dis(gen);
}
int* arrQuick = new int[size];
int* arrMerge = new int[size];
int* arrHeap = new int[size];
for (int i = 0; i < size; i++) {
arrQuick[i] = arr[i];
arrMerge[i] = arr[i];
arrHeap[i] = arr[i];
}
// 快速排序计时
auto startQuick = std::chrono::high_resolution_clock::now();
QuickSort(arrQuick, size);
auto endQuick = std::chrono::high_resolution_clock::now();
auto durationQuick = std::chrono::duration_cast(endQuick - startQuick).count();
totalQuick += durationQuick;
// 归并排序计时
auto startMerge = std::chrono::high_resolution_clock::now();
MergeSort(arrMerge, size);
auto endMerge = std::chrono::high_resolution_clock::now();
auto durationMerge = std::chrono::duration_cast(endMerge - startMerge).count();
totalMerge += durationMerge;
// 堆排序计时
auto startHeap = std::chrono::high_resolution_clock::now();
HeapSort(arrHeap, size);
auto endHeap = std::chrono::high_resolution_clock::now();
auto durationHeap = std::chrono::duration_cast(endHeap - startHeap).count();
totalHeap += durationHeap;
delete[] arr;
delete[] arrQuick;
delete[] arrMerge;
delete[] arrHeap;
}
std::cout << "快速排序平均运行时间: " << totalQuick / 5 / 1000 << " 秒" << std::endl;
std::cout << "归并排序平均运行时间: " << totalMerge / 5 / 1000 << " 秒" << std::endl;
std::cout << "堆排序平均运行时间: " << totalHeap / 5 / 1000 << " 秒" << std::endl;
std::cout << std::endl;
}
int main() {
int size = 100000000;
compareSortingTimes(size);
return 0;
}
原理:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
时间复杂度:平均时间复杂度为,其中
是位数,
是数据个数,
是基数。
空间复杂度:
稳定性:稳定
STL中sort用了什么排序算法?
sort用了内省排序(一种混合排序算法)主要有快速排序,堆排序和插入排序。
快速排序:内省排序在开始阶段会优先使用快速排序,因为快速排序在平均情况下具有较好的性能,其平均时间复杂度为 。但是,快速排序在最坏情况下(例如数组已经有序或接近有序)的时间复杂度会退化为
。
插入排序:在数据趋于有序的情况下,插入排序是效率最好的排序,因此在子数组规模小到一定程度时会使用插入排序,其常数因子较小,性能可能比快速排序和堆排序更好
堆排序:为了避免快速排序在最坏情况下的性能问题,当递归深度到达一定阈值时会使用堆排序,以保证在最坏情况下也能达到 的时间复杂度。