下面为每类算法赋予贴合原理的趣味名称,同时从核心思想、执行步骤、复杂度、C++ 实现代码、算法特点五个维度做详细讲解,全部代码可直接编译运行。
一、四大经典排序算法
1. 快速排序 → 标杆劈叉法
核心思想
选一个元素作为「标杆」,遍历数组后把比标杆小的元素全放左边,比标杆大的全放右边,标杆直接落到自己的最终正确位置,像 "劈叉" 一样把数组切成两半;再对左右两个子数组重复这个操作,直到所有元素都归位。是分治思想的典型代表。
执行步骤
- 选标杆:通常选数组首元素、尾元素或中间元素作为基准值
- 分区操作:用双指针从两端向中间遍历,把小于标杆的元素换到左侧,大于标杆的换到右侧,最后把标杆放到分界点
- 递归分治:对标杆左侧、右侧的子数组分别重复前两步,直到子数组长度为 1
复杂度
- 平均时间:
O(nlogn) - 最坏时间:
O(n²)(数组已有序且选端点当标杆时,可通过随机选标杆优化) - 空间复杂度:
O(logn)(递归调用栈) - 稳定性:不稳定
C++ 实现
cpp
运行
cpp
#include <vector>
using namespace std;
// 分区函数:返回标杆最终位置
int partition(vector<int>& nums, int left, int right) {
int pivot = nums[left]; // 选左端点当标杆
while (left < right) {
// 从右往左找第一个小于标杆的数
while (left < right && nums[right] >= pivot) right--;
nums[left] = nums[right];
// 从左往右找第一个大于标杆的数
while (left < right && nums[left] <= pivot) left++;
nums[right] = nums[left];
}
nums[left] = pivot; // 标杆归位
return left;
}
void quickSort(vector<int>& nums, int left, int right) {
if (left >= right) return;
int pivotPos = partition(nums, left, right);
quickSort(nums, left, pivotPos - 1); // 递归左半区
quickSort(nums, pivotPos + 1, right); // 递归右半区
}
2. 堆排序 → 金字塔淘汰赛
核心思想
把数组构造成「大顶堆」(金字塔结构,塔顶永远是最大元素),每次把塔顶的 "冠军" 元素和堆尾元素交换(相当于冠军晋级最终排名),再把剩下的元素重新调整成堆,选出下一个冠军;重复这个过程,直到所有元素都排好序。
执行步骤
- 建堆:从最后一个非叶子节点开始,从下往上把数组调整为大顶堆
- 交换堆顶:将堆顶最大元素与堆尾元素交换,堆的有效长度减 1
- 下沉调整:对新的堆顶元素做下沉操作,维持大顶堆性质
- 重复步骤 2、3,直到堆的有效长度为 1
复杂度
- 时间复杂度:
O(nlogn)(建堆 O (n) + n 次调整 O (logn),时间稳定) - 空间复杂度:
O(1)(原地排序,无额外空间) - 稳定性:不稳定
C++ 实现
cpp
运行
cpp
#include <vector>
using namespace std;
// 下沉调整:维持大顶堆性质
void heapify(vector<int>& nums, int n, int i) {
int largest = i; // 父节点
int left = 2 * i + 1; // 左孩子
int right = 2 * i + 2; // 右孩子
if (left < n && nums[left] > nums[largest]) largest = left;
if (right < n && nums[right] > nums[largest]) largest = right;
if (largest != i) {
swap(nums[i], nums[largest]);
heapify(nums, n, largest); // 递归调整被交换的子节点
}
}
void heapSort(vector<int>& nums) {
int n = nums.size();
// 1. 构建大顶堆
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(nums, n, i);
}
// 2. 逐个取出堆顶元素
for (int i = n - 1; i > 0; i--) {
swap(nums[0], nums[i]); // 堆顶与堆尾交换
heapify(nums, i, 0); // 调整剩余堆
}
}
3. 归并排序 → 百川归海法
核心思想
典型的「先分后合」思路:先把数组层层拆分,直到拆成一个个单独的元素(像无数条小溪,单个元素天然有序);再把有序的小数组两两合并,逐步汇成更大的有序数组,最终合并成完整的有序数组,如同百川汇入大海。
执行步骤
- 拆分:把数组从中间切成左右两部分,递归拆分,直到每个子数组只有 1 个元素
- 合并:创建临时数组,把两个有序子数组按大小顺序合并成一个有序数组
- 逐层向上合并,直到得到完整有序数组
复杂度
- 时间复杂度:
O(nlogn)(拆分 logn 层,每层合并 O (n),时间稳定) - 空间复杂度:
O(n)(需要临时数组存合并结果) - 稳定性:稳定排序
C++ 实现
cpp
运行
cpp
#include <vector>
using namespace std;
// 合并两个有序区间 [left,mid] 和 [mid+1,right]
void merge(vector<int>& nums, int left, int mid, int right, vector<int>& temp) {
int i = left, j = mid + 1, k = 0;
// 按大小顺序合并到临时数组
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) temp[k++] = nums[i++];
else temp[k++] = nums[j++];
}
// 处理剩余元素
while (i <= mid) temp[k++] = nums[i++];
while (j <= right) temp[k++] = nums[j++];
// 拷贝回原数组
for (i = 0; i < k; i++) nums[left + i] = temp[i];
}
void mergeSort(vector<int>& nums, int left, int right, vector<int>& temp) {
if (left >= right) return;
int mid = left + (right - left) / 2;
mergeSort(nums, left, mid, temp); // 递归拆分左半
mergeSort(nums, mid + 1, right, temp); // 递归拆分右半
merge(nums, left, mid, right, temp); // 合并两个有序区间
}
// 对外调用接口
void mergeSort(vector<int>& nums) {
vector<int> temp(nums.size());
mergeSort(nums, 0, nums.size() - 1, temp);
}
4. 希尔排序 → 缩步插缝法
核心思想
插入排序的优化版。插入排序在数组基本有序时效率极高,希尔排序就利用这一点:先按大步长把数组分成多组,每组内做插入排序,让数组先 "大概有序";再逐步缩小步长,精细调整;直到步长缩为 1,此时数组已经接近有序,最后做一遍普通插入排序即可完成。
执行步骤
- 设定初始步长
gap(通常取数组长度的 1/2) - 按 gap 分组:所有下标相差 gap 的元素为一组,每组内执行插入排序
- 缩小 gap(通常减半),重复分组排序
- 直到 gap = 1,完成最后一次插入排序,数组有序
复杂度
- 平均时间:
O(n^1.3)(步长选择不同,复杂度有差异) - 最坏时间:
O(n²) - 空间复杂度:
O(1)(原地排序) - 稳定性:不稳定
C++ 实现
cpp
运行
cpp
#include <vector>
using namespace std;
void shellSort(vector<int>& nums) {
int n = nums.size();
// 逐步缩小步长 gap
for (int gap = n / 2; gap > 0; gap /= 2) {
// 对每组进行插入排序
for (int i = gap; i < n; i++) {
int temp = nums[i];
int j;
// 组内向前找插入位置
for (j = i; j >= gap && nums[j - gap] > temp; j -= gap) {
nums[j] = nums[j - gap];
}
nums[j] = temp;
}
}
}
二、基础查找算法:二分查找 → 折半寻宝术
核心思想
在有序数组中查找目标值,每次都取区间中间的元素和目标对比:
- 相等 → 直接找到
- 目标更小 → 目标只可能在左半区,直接砍掉右半区
- 目标更大 → 目标只可能在右半区,直接砍掉左半区
像折木棍一样每次砍掉一半范围,效率极高。
前提条件
数组必须是有序的(升序 / 降序均可,这里以升序为例)。
复杂度
- 时间复杂度:
O(logn)(n 个元素最多折 log₂n 次) - 空间复杂度:
O(1)(迭代版)
C++ 实现(迭代版 + 递归版)
cpp
运行
cpp
#include <vector>
using namespace std;
// 迭代版(推荐,无递归栈开销)
int binarySearch(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] == target) return mid; // 找到目标
else if (nums[mid] < target) left = mid + 1; // 去右半区找
else right = mid - 1; // 去左半区找
}
return -1; // 未找到
}
// 递归版
int binarySearchRecur(vector<int>& nums, int left, int right, int target) {
if (left > right) return -1;
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target)
return binarySearchRecur(nums, mid + 1, right, target);
else
return binarySearchRecur(nums, left, mid - 1, target);
}
特点
- 查找效率极高,适合海量静态有序数据
- 缺点:依赖有序数组,不适合频繁插入、删除的动态数据
谢谢