十大排序算法的C++代码,一个源文件全部搞定,并给出了详细的注释便于理解。
#include <iostream>
#include <bits/stdc++.h>
#include <vector>
using namespace std;
class Sort {
public:
// *****************三个基础排序O(n^2)*********************************************
// ---------------------------------------------------------------
// 1.冒泡排序
// (1)从数组的第一个元素开始,相邻元素两两比较。
// (2)如果前一个元素大于后一个元素,就交换它们的位置。
// (3)对每一轮的未排序部分重复上述比较和交换过程。
// (4)当某一轮比较中没有发生任何交换时,说明数组已完全排序,结束算法。
// ---------------------------------------------------------------
void bubbleSort(vector<int>& nums) {
for (int i = 0; i < nums.size(); i++) {
bool sw = false; // 记录某一轮是否发生了交换
for (int j = nums.size() - 1; j > i; j--) {
if (nums[j] < nums[j - 1]) {
swap(nums[j], nums[j - 1]);
sw = true;
}
}
if (sw == false) break; // 没有发生任何交换时,说明数组已完全排序,提前结束
}
}
// ---------------------------------------------------------------
// 2.选择排序
// (1)在未排序部分中找到最小(或最大)元素及其索引。
// (2)将找到的最小(或最大)元素与未排序部分的第一个元素交换位置。
// (3)缩小未排序部分,继续在新的未排序部分中重复上述步骤,直到整个数组排序完毕。
// ---------------------------------------------------------------
void selectionSort(vector<int>& nums) {
for (int i = 0; i < nums.size(); i++) {
int minNum = i;
for (int j = nums.size() - 1; j > i; j--) {
if (nums[j] < nums[minNum]) {
minNum = j;
}
}
if (minNum != i) swap(nums[i], nums[minNum]);
}
}
// ---------------------------------------------------------------
// 3.插入排序
// (1)将待排序数组分为已排序序列(初始时只有一个元素)和未排序序列。
// (2)取未排序序列的第一个元素,与已排序序列中的元素逐个比较,找到插入位置。
// (3)将当前待插入元素插入到已排序序列的相应位置。
// (4)更新已排序序列范围,继续处理未排序序列中的下一个元素,直到未排序序列为空。
// ---------------------------------------------------------------
void insertSort(vector<int>& nums) {
for (int i = 1; i < nums.size(); i++) {
int tmp = nums[i];
int j = i;
while (j > 0 && tmp < nums[j - 1]) {
nums[j] = nums[j - 1];
j--;
}
nums[j] = tmp;
}
}
// ***********************四个O(nlogn)排序********************************************
// ---------------------------------------------------------------
// 4.希尔排序:插入排序的改进版,结合插入排序记忆
// (1)设定初始增量step,并按当前增量将数组划分为多个子序列。
// (2)对每个子序列独立执行插入排序。
// (3)减小增量,进入下一组子序列的排序。
// (4)重复上述过程,直到增量减至1。
// (5)使用增量为1进行最后一次插入排序,完成整个排序过程。
// ---------------------------------------------------------------
void shellSort(vector<int>& nums) {
for (int step = nums.size() / 2; step > 0; step /= 2) { // 增量划定操作
for (int i = step; i < nums.size(); i++) { // 剩下的就是插入排序的思路
int tmp = nums[i];
int j = i;
while (j - step >= 0 && tmp < nums[j - step]) {
nums[j] = nums[j - step];
j -= step;
}
nums[j] = tmp;
}
}
}
// ---------------------------------------------------------------
// 5.快速排序
// (1)选取一个基准元素。
// (2)对数组进行分区操作,使得基准元素左侧所有元素都小于它,右侧所有元素都大于它。
// (3)递归地对基准元素左右两侧的子数组进行快速排序。
// (4)当待排序数组长度小于等于1时,递归结束。
// ---------------------------------------------------------------
void quickSort(vector<int>& nums) {
int n = nums.size() - 1;
partition(nums, 0, n);
}
// 递归部分
void partition(vector<int>& nums, int low, int high) {
// 递归终结条件,左右指针指向同一个元素(排序数组的长度小于等于1)
if (low >= high) return;
// 快速排序内容-----------------
int i = low, j = high;
int pivot = nums[low];
while (i < j) {
while (i < j && nums[j] > pivot) j--;
nums[i] = nums[j];
while (i < j && nums[i] < pivot) i++;
nums[j] = nums[i];
}
nums[i] = pivot;
// ----------------------------
// 继续分解再执行快排
partition(nums, low, i - 1);
partition(nums, i + 1, high);
}
// ---------------------------------------------------------------
// 6.并归排序,分为分解和合并两步,在递归的写法中,先分解再合并
// (1)递归地将待排序数组分解成长度为1的子数组。
// (2)从最底层开始,两两合并已排序的子数组,生成新的已排序子数组。
// (3)递归地合并上一层子数组,直到合并成一个完整的有序数组。
// ---------------------------------------------------------------
void mergeSort(vector<int>& nums) {
int n = nums.size() - 1;
divide(nums, 0, n);
}
// 分解
void divide(vector<int>& nums, int low, int high) {
// 当分解到只有一个单位,则结束
if (low >= high) return;
// 继续递归分解
int mid = low + (high - low) / 2;
divide(nums, low, mid);
divide(nums, mid + 1, high);
// 合并
merge(nums, low, mid, high);
}
// 合并
void merge(vector<int>& nums, int low, int mid, int high) {
int i = low, j = mid + 1, k = 0;
vector<int> tmp(high - low + 1);
// 依次选择小的进入结果数组
while (i <= mid && j <= high) {
if (nums[i] < nums[j]) {
tmp[k++] = nums[i++];
} else {
tmp[k++] = nums[j++];
}
}
// 对不满的一半填满
while (i <= mid) tmp[k++] = nums[i++];
while (j <= high) tmp[k++] = nums[j++];
// 将结果填回原数组
for (i = low, k = 0; i <= high; i++, k++) {
nums[i] = tmp[k];
}
}
// ---------------------------------------------------------------
// 7.堆排序
// (1)构建初始堆:从数组中间位置开始自底向上进行下沉操作,确保整个数组满足堆序性质。
// (2)交换堆顶元素与末尾元素:将最大元素放到正确的位置(数组末尾)。
// (3)调整堆:将新堆重新调整为大顶堆。
// (4)重复交换与调整过程,直到数组完全有序。
// ---------------------------------------------------------------
// API与初始化
void heapSort(vector<int>& nums) {
// 首先整体范围建堆
for (int i = nums.size() / 2 - 1; i >= 0; i--) {
adjustHeap(nums, i, nums.size());
}
// 交换根结点(最大值)和最后的结点,放在数组末尾,对剩下的序列从上到下调整为大顶堆
for (int i = nums.size() - 1; i >= 0; i--) {
swap(nums[0], nums[i]);
adjustHeap(nums, 0, i);
}
}
// 建堆过程
void adjustHeap(vector<int>& nums, int father, int len) {
int leftChild = father * 2 + 1; // 使用数组存树,按层序遍历的方式放置元素,则某节点的左孩子位于该节点位置的2倍加1
int rightChild = father * 2 + 2;
// 堆的调整过程:如果某父节点存在比它大的孩子,则交换值
int maxVal = father;
if (leftChild < len && nums[leftChild] > nums[maxVal]) {
maxVal = leftChild;
}
if (rightChild < len && nums[rightChild] > nums[maxVal]) {
maxVal = rightChild;
}
if (maxVal != father) {
swap(nums[maxVal], nums[father]);
// 对改变过的子树调整为大顶堆,此时maxVal不再指向最大值,而是指向了与父节点交换了值的孩子
adjustHeap(nums, maxVal, len);
}
}
// *************时间复杂度为O(n)的三个排序******************************************
// ---------------------------------------------------------------
// 8.计数排序,本质上是哈希的思路
// (1)确定待排序数组的值域范围。
// (2)初始化计数数组,用于统计每个值的出现次数。
// (3)遍历待排序数组,统计每个值在计数数组中的频率。
// (4)累计计数,使计数数组中的每个值表示小于或等于该值的元素在排序后的位置。
// (5)根据计数数组输出排序结果。
// ---------------------------------------------------------------
void countSort(vector<int>& nums) {
// 获取数组最大值以确定计数范围,建立计数数组
int maxVal = *max_element(nums.begin(), nums.end());
vector<int> tmp(maxVal + 1);
// 统计每个值的出现频率
for (int i : nums) tmp[i]++;
int index = 0;
for (int i = 0; i < tmp.size(); i++) {
// 累计计数
while (tmp[i] > 0) {
nums[index++] = i;
tmp[i]--;
}
}
}
// ---------------------------------------------------------------
// 9.桶排序,本质上是对待排数据划分区间,再在小区间里使用其它排序算法
// (1)确定桶的数量和范围。
// (2)分配元素到对应的桶。
// (3)对每个桶内部进行排序。
// (4)合并桶中的元素,得到最终的有序序列。
// ---------------------------------------------------------------
void bucketSort(vector<int>& nums) {
// 设定桶大小
int bucketSize = 10; // 可自定
// 获取数据范围与计算桶数量
int minVal = nums[0], maxVal = nums[0]; // 获取最大最小值也可以直接用min_element和max_element,见下一个算法
for (int i = 0; i < nums.size(); i++) {
minVal = min(nums[i], minVal);
maxVal = max(nums[i], maxVal);
}
int bucketNum = int((maxVal - minVal) / bucketSize) + 1;
// 桶生成,数据入桶:划区间的过程
vector<vector<int>> bucket(bucketNum, vector<int>());
for (int i = 0; i < nums.size(); i++) {
bucket[int((nums[i] - minVal) / bucketSize)].push_back(nums[i]);
}
// 桶内数据排序与返回原数组
int index = 0;
for (int i = 0; i < bucket.size(); i++) {
// 这里使用了计数排序,使用其它排序算法也可以
countSort(bucket[i]);
// 填充回原数组
for (int j = 0; j < bucket[i].size(); j++) {
nums[index++] = bucket[i][j];
}
}
}
// ---------------------------------------------------------------
// 10.基数排序,与桶排序的思路有相同之处
// (1)确定最大位数,以确定循环次数。
// (2)创建辅助数组。
// (3)从最低位开始,进行多轮分配和收集操作。
// (4)每轮收集后,将辅助数组的内容复制回原数组。
// (5)重复上述过程,直到最高位也完成排序。
// ---------------------------------------------------------------
void radixSort(vector<int>& nums) {
// 获取最大最小值,最大值用于确定循环次数,最小值用于确定是否对负数进行补偿
int minVal = *min_element(nums.begin(), nums.end());
int maxVal = *max_element(nums.begin(), nums.end());
// 负数补偿(负数直接进行基数排序不得行,得保证整个序列是非负的)
if (minVal < 0) {
for (int i = 0; i < nums.size(); i++) {
nums[i] -= minVal;
}
maxVal -= minVal;
}
// 确定循环次数,即最大值的位数
int digit = 0;
while (maxVal > 0) {
maxVal /= 10;
digit++;
}
// 与桶排序思路类似,基数排序的实现过程
int radix = 10; // 每个位有 0~9 十种情况,故有10个桶
vector<vector<int>> bucket(radix, vector<int>());
for (int i = 0, mod = 10, dev = 1; i < digit; i++, mod *= 10, dev *= 10) {
// 按位入桶
for (int j = 0; j < nums.size(); j++) {
bucket[(nums[j] % mod) / dev].push_back(nums[j]); // 位数有低到高,个十百千...
}
// 填充回原数组
int index = 0;
for (int j = 0; j < bucket.size(); j++) {
for (int k = 0; k < bucket[j].size(); k++) {
nums[index++] = bucket[j][k];
}
bucket[j].clear(); // 记得清空,下次循环还要用
}
}
// 负数补偿的还原
if (minVal < 0) {
for (int i = 0; i < nums.size(); i++) {
nums[i] += minVal;
}
}
}
};
// ********************************************************************************
int main(){
// 测试用例
// vector<int> nums = {6, 8, 3, 4, 1, 7, 9, 2, 3, 5, 0};
vector<int> nums = {15, 19, 46, 26, 3, 5, 27, 2, 36, 4, 38, 47, 50, 44, 48};
Sort sort;
// ---------------------------------
// 10种排序算法调用如下:
// sort.bubbleSort(nums);
// sort.selectionSort(nums);
// sort.insertSort(nums);
// ------
// sort.shellSort(nums);
// sort.quickSort(nums);
// sort.mergeSort(nums);
// sort.heapSort(nums);
// ------
// sort.countSort(nums);
// sort.bucketSort(nums);
// sort.radixSort(nums);
// ---------------------------------
for (int i : nums) cout << i << ' ';
return 0;
}