冒泡排序
算法描述
- 从第一个元素开始,依次比较相邻的两个元素。
- 如果顺序不正确(即前一个元素大于后一个元素),则交换这两个元素。
- 继续进行这样的比较和交换,直到遍历到数组的倒数第二个元素。
- 重复以上步骤,每次遍历都将当前未排序部分的最大元素移动到正确的位置。
- 重复上述步骤,直到整个数组排序完成。
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
// 如果当前元素大于下一个元素,则交换它们
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
冒泡排序的时间复杂度为O(n^2),其中n是待排序数组的长度。
插入排序
算法描述
分为已排序和未排序两个区间,初始时,已排序区间就只有第一个元素,然后遍历未排序区间的每一个元素,直到已排序区间每个元素都有序
void insertionSort(int arr[], int n)
{
for(int i = 1;i < n;i++)
{
int key = arr[i];
int j = i - 1;
while(j >= 0 && arr[j] > key)
{
//比key大的往后移动
arr[j + 1] = arr[i];
--j;
}
arr[j + 1] = key;
}
}
插入排序的时间复杂度为O(n^2),其中n是待排序数组的长度。具体来说,插入排序的算法包含一个外层循环和一个内层循环:
选择排序
算法描述
跟插入排序差不多,还是分为已排区间和未排区间,在未排序区间中选择最小的元素,然后放到已排序区间的末尾。
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
// 假设当前元素为最小值
int minIndex = i;
// 在未排序的部分中找到最小的元素的索引
for (int j = i + 1; j < n; ++j) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 将找到的最小元素与当前元素交换位置
if (minIndex != i) {
std::swap(arr[i], arr[minIndex]);
}
}
}
选择排序的时间复杂度也是O(n^2),其中n是待排序数组的长度。
快速排序
算法描述
一种基于分治思维的高效描述算法。通过一个基准的元素,将数组分为两部分,其一部分的元素都小于基准,另一部分元素都大于其基准元素,然后一直递归下去
void quickSort(std::vector<int>& arr, int low, int high)
{
if(low < high)
{
int pivotIndex = partition(arr, low, high);
// 对划分的两部分递归进行排序
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
// 函数用于选择基准元素并对数组进行划分
int partition(std::vector<int>& arr, int low, int high) {
int pivot = arr[high]; // 选择数组最后一个元素作为基准
int i = low - 1; // 初始化较小元素的索引
for (int j = low; j <= high - 1; ++j) {
// 如果当前元素小于等于基准,则交换它与较小元素的位置
if (arr[j] <= pivot) {
++i;
swap(arr[i], arr[j]);
}
}
// 将基准元素放置到正确的位置
swap(arr[i + 1], arr[high]);
return i + 1;
}
快速排序的平均时间复杂度为O(n log n)。
归并排序
算法描述
- 将数组分为两个子数组,进行递归排序
- 将排序好的子数组合并成一个有序的数组
// 函数用于递归地进行归并排序
void mergeSort(std::vector<int>& arr, int left, int right) {
if (left < right) {
// 计算中间位置
int middle = left + (right - left) / 2;
// 对左右两部分进行递归排序
mergeSort(arr, left, middle);
mergeSort(arr, middle + 1, right);
// 合并两个有序数组
merge(arr, left, middle, right);
}
}
void merege(vector<int>& arr,int left,int middle,int right)
{
int n = middle - left + 1;
int m = right - middle;
// 创建临时数组用于存储两个子数组
std::vector<int> leftArray(n);
std::vector<int> rightArray(m);
// 将数据复制到临时数组
for (int i = 0; i < n1; ++i) {
leftArray[i] = arr[left + i];
}
for (int j = 0; j < n2; ++j) {
rightArray[j] = arr[middle + 1 + j];
}
//合并两个数组
int i = 0,int j = 0,k = left;
while(i < n && j < m)
{
if (leftArray[i] <= rightArray[j])
{
arr[k] = leftArray[i];
++i;
} else {
arr[k] = rightArray[j];
++j;
}
++k;
}
//将剩余元素复制到数组中
while(i < n)
{
arr[k] = leftArray[i];
++i;
++k;
}
while(j < m)
{
arr[k] = rightArray[j];
++j;
++k;
}
}
归并排序的时间复杂度为O(n log n)
堆排序详解过程
堆类似于完全二叉树的结构,并同时满足堆的性质:即子节点的键值或者索引总是小于或大于父节点。
算法描述
- 利用给定数组创建一个堆,输出堆顶元素
- 把堆的尺寸缩小到 1
- 重复步骤 2,直到堆的尺⼨为 1
// 函数用于调整堆,使得以节点i为根的子树成为最大堆
void heapify(std::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) {
std::swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
// 函数用于进行堆排序
void heapSort(std::vector<int>& arr) {
int n = arr.size();
// 构建最大堆
for (int i = n / 2 - 1; i >= 0; --i) {
heapify(arr, n, i);
}
// 从堆顶依次取出最大值,与数组末尾交换并调整堆
for (int i = n - 1; i > 0; --i) {
std::swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
堆排序的时间复杂度为O(n log n)
桶排序
算法描述
将数组分别到有限数量的桶里,每个桶在别排序
//桶排序
void bucketSort(std::vector<int>& arr)
{
int n = arr.size();
int maxValue = *max_element(arr.begin(), arr.end());
int bucketSize = maxValue / n + 1;
//创建桶
vector<vector<int>> bucket(n);
//将元素分配到桶里面
for (int i = 0; i < n; ++i)
{
int bucketIndex = arr[i] / bucketSize;
bucket[bucketIndex].push_back(arr[i]);
}
//对桶内每个元素排序
for (int i = 0; i < n; i++)
{
std::sort(bucket[i].begin(), bucket[i].end());
}
//合并桶内的结果
int index = 0;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < bucket[i].size(); ++j)
{
arr[index++] = bucket[i][j];
}
}
}
计数排序
计数排序(Counting Sort)是一种非比较排序算法,它通过统计每个元素的出现次数来实现排序。这个算法假设输入数据的范围是已知的且较小的整数,并利用这个信息进行排序。计数排序的基本思想是创建一个计数数组来记录每个元素出现的次数,然后根据计数数组的信息重构有序序列。
注意:计数排序只能⽤在数据范围不大的场景中,如果数据范围 k 比要排序的数据 n 大很多,就不适合用计数排序了。而且,计数排序只能给非负整数排序,如果要排序的数据是其他类型的,要将其在不改变相对大小的情况下,转化为非负整数。
void countingSort(std::vector<int>& arr) {
// 找到数组中的最大值,确定计数数组的大小
int max = *std::max_element(arr.begin(), arr.end());
// 创建计数数组,并初始化为0
std::vector<int> count(max + 1, 0);
// 统计每个元素的出现次数
for (int i : arr) {
++count[i];
}
// 根据计数数组的信息重构有序序列
int index = 0;
for (int i = 0; i <= max; ++i) {
while (count[i] > 0) {
arr[index++] = i;
--count[i];
}
}
}
计数排序的时间复杂度为O(n + k),其中n是待排序数组的长度,k是数据范围(最大值与最小值之差)
基数排序基数排序详解
基数排序是一种按照每个位上的数字进行排序的算法,它可以用于整数或字符串的排序
// 获取数字的某一位上的数值
int getDigit(int num, int digit) {
int divisor = 1;
for (int i = 0; i < digit - 1; ++i) {
divisor *= 10;
}
return (num / divisor) % 10;
}
void radixSort(std::vector<int>& arr) {
const int n = arr.size();
const int maxDigits = 3; // 假设最大的位数为3位
// 对每一位进行计数排序
for (int digit = 1; digit <= maxDigits; ++digit) {
std::vector<int> count(10, 0); // 0-9 十个数字
// 统计每个数字出现的次数
for (int i = 0; i < n; ++i) {
count[getDigit(arr[i], digit)]++;
}
// 计算累计次数
for (int i = 1; i < 10; ++i) {
count[i] += count[i - 1];
}
// 临时数组用于存储排序后的结果
std::vector<int> tempArr(n);
// 根据当前位排序
for (int i = n - 1; i >= 0; --i) {
int digitValue = getDigit(arr[i], digit);
tempArr[count[digitValue] - 1] = arr[i];
count[digitValue]--;
}
// 将排序结果复制回原数组
for (int i = 0; i < n; ++i) {
arr[i] = tempArr[i];
}
}
}
希尔排序
算法详解
希尔排序(Shell Sort)是插入排序的一种改进版本,也被称为缩小增量排序。它通过将相距一定间隔的元素组成一个子序列,对每个子序列进行插入排序,逐渐缩小间隔,最终使整个数组成为有序序列。希尔排序的主要思想是通过预处理使数组部分有序,最后再进行一次插入排序,能够在一定程度上提高插入排序的性能。
void shellSort(std::vector<int>& arr) {
int n = arr.size();
// 初始间隔
for (int gap = n / 2; gap > 0; gap /= 2) {
// 对每个子序列进行插入排序
for (int i = gap; i < n; ++i) {
int temp = arr[i];
int j;
// 插入排序
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap];
}
// 将元素插入正确的位置
arr[j] = temp;
}
}
}
平均复杂度O(n log n)