本篇博客,主要是使用 C++ 实现考研数据结构的排序代码,实现的均为从小到大排序,本文的排序代码中的无穷大值均采用的是 INT_MAX = 2147483647,如有需要可以自行更改。
目录
直接插入排序
思想
直接插入排序的思想就是维护前部分的有序数组,然后将后面的未排列的元素依次插入到前部分。
代码实现:
vector<int> directInsertSort(vector<int> arr) {
// 实现直接插入排序
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
for (int i = 1; i < n; ++i) {
if (arr[i - 1] > arr[i]) {
tmp = arr[i];
// [0, ..., i - 1] 都是有序的, 将 arr[i] 插入进去
int j = i - 1;
while (j >= 0 && ++cnt && arr[j] > tmp) {
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = tmp;
}
++cnt;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
折半插入排序
所谓折半插入排序就是二分查找当前插入的元素在前面有序部分的位置,相对于简单插入排序可能速度相对而言快一些。
代码实现
vector<int> binaryInsertSort(vector<int> arr) {
// 在简单排序的基础上加入折半(二分查找位置)
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
for (int i = 1; i < n; ++i) {
if (arr[i - 1] > arr[i]) {
// [0, ..., i - 1] 都是有序的, 将 arr[i] 插入进去
// 为了实现稳定排序,应当查找的是 >= (arr[i] + 1) 元素的第一个位置
int left = 0, right = i - 1;
tmp = arr[i] + 1;
while (left <= right) { // [left, right]
int mid = left + ((right - left) >> 1);
if (arr[mid] < tmp) {
left = mid + 1;
} else {
right = mid - 1;
}
++cnt;
}
int j = i - 1;
while (j >= left) {
arr[j + 1] = arr[j];
--j;
}
arr[left] = tmp - 1;
}
++cnt;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
希尔排序
写入排序由简单插入排序所优化,目的是减少元素的移动位置;
对比来看,简单插入要排序的序列相邻元素的下标间隔是 1,而希尔排序的间隔可以是大于 1 小于数组的长度的整数值,然后不断的减少至间隔为 1(相当于最终退化成简单插入排序)。
代码实现
vector<int> shellInsertSort(vector<int> arr) {
// 实现简单插入排序的基础上,优化移动次数成希尔排序
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
// 初始化窗口大小
int dx = 1;
while (dx <= n / 2) dx <<= 1;
while (dx) {
for (int i = 0; i < n - dx; ++i) {
for (int k = i + dx; k < n; k += dx) {
if (arr[k - dx] > arr[k]) { // 内部使用简单插入排序
tmp = arr[k];
// [i, i + dx, ..., k - dx] 都是有序的, 将 arr[k] 插入进去
int j = k - dx;
while (j >= 0 && ++cnt && arr[j] > tmp) {
arr[j + dx] = arr[j];
j -= dx;
}
arr[j + dx] = tmp;
}
++cnt;
}
}
dx >>= 1;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
冒泡排序
思想
每次都扫描数组,然后将更小的元素尽量往左边移动,显然,最小的元素肯定会被移动到最左边,也就是说一趟排序后至少能够确定一个正确的位置。
代码实现
vector<int> bubbleExchangeSort(vector<int> arr) {
// 实现冒泡排序
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
for (int i = 0; i < n; ++i) {
bool flag = false; // 记录本回合是否发生交换
for (int j = n - 1; j > i; --j) {
if (++cnt && arr[j] < arr[j - 1]) {
swap(arr[j], arr[j - 1]);
flag = true;
}
}
if (!flag) {
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr; // 当前已经排序完成了
}
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
快速排序
思想
每次在需要排序的部分选中一个基准元素,然后将小于基准元素的元素移动到其左边,大于基准元素的值移动到其右边。这保证了,每一趟这个排序能够实现1个元素能够被置到合适的位置。
代码实现
int partion(vector<int>& arr, int left, int right, int& cnt) {
int x = arr[left];
while (left < right) {
while (right > left && ++cnt && arr[right] >= x) --right;
arr[left] = arr[right];
while (left < right && ++cnt && arr[left] <= x) ++left;
arr[right] = arr[left];
}
arr[left] = x;
return left;
}
int quickSort(vector<int>& arr, int left, int right) {
int cnt = 0;
if (left >= right) return cnt;
int mid = partion(arr, left, right, cnt);
cnt += quickSort(arr, left, mid - 1);
cnt += quickSort(arr, mid + 1, right);
return cnt;
}
vector<int> quickSort(vector<int> arr) {
// 实现快速排序, 使用第一个元素作为基准元素
int n = arr.size();
int cnt = quickSort(arr, 0, n - 1);
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
简单选择排序
思想
实现很简单,每次选中未排序的部分的最小值,与左边交换。
代码实现
vector<int> simpleSelectSort(vector<int> arr) {
// 实现简单选择排序
int cnt = 0; // 对比较次数进行计数
int n = arr.size();
for (int i = 0; i < n; ++i) {
int min_value = INT_MAX, min_loc = -1;
for (int j = i; j < n; ++j) {
if (++cnt && min_value > arr[j]) {
min_loc = j;
min_value = arr[j];
}
}
swap(arr[i], arr[min_loc]);
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
堆排序
思想
思想其实很简单,首先建立一个小根堆,然后依次将小根堆的堆顶输出,输出的时候注意维护好小根堆。
代码实现
void adjustHeap(vector<int>& arr, int j, int back, int& cnt) {
while (j < back) {
int left = INT_MAX, right = INT_MAX;
if (2 * j < back) left = arr[2 * j];
if (2 * j + 1 < back) right = arr[2 * j + 1];
cnt += 2;
if (left < arr[j] && right < arr[j]) {
if (left < right) {
swap(arr[j], arr[2 * j]);
j = 2 * j;
} else {
swap(arr[j], arr[2 * j + 1]);
j = 2 * j + 1;
}
++cnt;
} else if (left < arr[j]) {
swap(arr[j], arr[2 * j]);
j = 2 * j;
} else if (right < arr[j]) {
swap(arr[j], arr[2 * j + 1]);
j = 2 * j + 1;
} else break; // 无法调整树型
}
}
vector<int> heapSort(vector<int> arr) {
// 实现堆排序
int cnt = 0; // 对比较次数进行计数
// 构建小根堆
int n = arr.size();
for (int i = n / 2; i >= 0; --i) {
adjustHeap(arr, i, n, cnt);
}
// 输出排序结果
vector<int> tmp(n);
int back = n - 1;
for (int i = 0; i < n; ++i) {
// 弹出堆顶元素
tmp[i] = arr[0];
// 调整堆
arr[0] = arr[back];
adjustHeap(arr, 0, back, cnt);
back--;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return tmp;
}
基数排序
思想
我觉得这个叫做桶排序更加形象一些,思想就是根据数位将数组元素装桶,然后出桶,直至所有元素都在一个桶结束,还是非常巧妙的。
代码实现
vector<int> baseNumberSort(vector<int> arr) {
// 实现基数排序, 最低位优先
vector<vector<int>> nums(10);
int cur = 1; // 当前位数
int tmp = 10; // 非空桶个数
while (tmp > 1) {
// 入桶
for (int num : arr)
nums[num / cur % 10].push_back(num);
// 出桶
tmp = 0;
arr.clear();
for (auto& arr1 : nums) {
tmp += (arr1.size() != 0);
for (auto num : arr1) {
arr.push_back(num);
}
arr1.clear();
}
cur *= 10;
}
return arr;
}
归并排序
思想
将其两两分组,然后两两合并即可。
代码实现
vector<int> MergeSort(vector<int> arr) {
// 实现归并排序, 使用二路归并
int cnt = 0; // 对比较次数进行计数
function<void(vector<int>&, int, int, int)> merge = [&](vector<int>& arr, int low, int mid, int high) {
// [low, mid], [mid + 1, high] 有序,合并两个区间
vector<int> tmp(high - low + 1);
int left = low, right = mid + 1, ix = 0; // 双指针
while (left <= mid || right <= high) {
tmp[ix++] = arr[left] < arr[right] ? arr[left++] : arr[right++], ++cnt;
while (left > mid && right <= high) tmp[ix++] = arr[right++];
while (right > high && left <= mid) tmp[ix++] = arr[left++];
}
for (int i = low; i <= high; ++i)
arr[i] = tmp[i - low];
tmp.clear();
};
int k = 2, n = arr.size();
while (k <= n) {
for (int low = 0; low < n; low += k) {
if (low + k / 2 - 1 >= n - 1) continue;
int high = (low + k - 1) < n ? low + k - 1 : n - 1;
merge(arr, low, low + k / 2 - 1, high);
}
k <<= 1;
}
if (k / 2 <= n) {
merge(arr, 0, k / 2 - 1, n - 1);
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
代码测试
为了调用和输出更加方便,撰写了如下代码进行输出:
vector<int> copyArray(vector<int> arr) {
return vector<int>(arr.begin(), arr.end());
}
void printArray(vector<int> arr) {
cout << "array = [";
for (int i = 0; i < arr.size() - 1; ++i) {
cout << arr[i] << ", ";
}
cout << *arr.rbegin() << "]" << endl;
}
以下是对应的测试代码:
int main() {
// 实现八大排序 (排序结果都是从小到大排序), 加入输出比较次数。
vector<int> arr{12, 9, 19, 229, 210, 39, 67, 346, 234, 2, 2, 3, 3, 4, 5, 2, 7, 6, 8, 0};
cout << "排序前原数组: " << endl;
printArray(arr);
cout << "直接插入排序: ";
printArray(directInsertSort(arr));
cout << "折半插入排序: ";
printArray(binaryInsertSort(arr));
cout << "希尔插入排序: ";
printArray(shellInsertSort(arr));
cout << "冒泡交换排序: ";
printArray(bubbleExchangeSort(arr));
cout << "快速排序: ";
printArray(quickSort(arr));
cout << "简单选择排序: ";
printArray(simpleSelectSort(arr));
cout << "堆排序: ";
printArray(heapSort(arr));
cout << "基数排序: " << endl;
printArray(baseNumberSort(arr));
cout << "归并排序: ";
printArray(MergeSort(arr));
return 0;
}
代码测试结果:
排序前原数组:
array = [12, 9, 19, 229, 210, 39, 67, 346, 234, 2, 2, 3, 3, 4, 5, 2, 7, 6, 8, 0]
直接插入排序: 算法比较次数为:153 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
折半插入排序: 算法比较次数为:74 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
希尔插入排序: 算法比较次数为:399 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
冒泡交换排序: 算法比较次数为:180 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
快速排序: 算法比较次数为:109 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
简单选择排序: 算法比较次数为:210 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
堆排序: 算法比较次数为:257 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
基数排序:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
归并排序: 算法比较次数为:64 排序结果为:
array = [0, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 19, 39, 67, 210, 229, 234, 346]
从代码测试结果来看,代码实现应该没有很大的问题,放心食用~
写作不易,欢迎点赞~
以下是完整代码,可供复制:
#include<bits/stdc++.h>
using namespace std;
vector<int> copyArray(vector<int> arr) {
return vector<int>(arr.begin(), arr.end());
}
void printArray(vector<int> arr) {
cout << "array = [";
for (int i = 0; i < arr.size() - 1; ++i) {
cout << arr[i] << ", ";
}
cout << *arr.rbegin() << "]" << endl;
}
vector<int> directInsertSort(vector<int> arr) {
// 实现直接插入排序
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
for (int i = 1; i < n; ++i) {
if (arr[i - 1] > arr[i]) {
tmp = arr[i];
// [0, ..., i - 1] 都是有序的, 将 arr[i] 插入进去
int j = i - 1;
while (j >= 0 && ++cnt && arr[j] > tmp) {
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = tmp;
}
++cnt;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
vector<int> binaryInsertSort(vector<int> arr) {
// 在简单排序的基础上加入折半(二分查找位置)
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
for (int i = 1; i < n; ++i) {
if (arr[i - 1] > arr[i]) {
// [0, ..., i - 1] 都是有序的, 将 arr[i] 插入进去
// 为了实现稳定排序,应当查找的是 >= (arr[i] + 1) 元素的第一个位置
int left = 0, right = i - 1;
tmp = arr[i] + 1;
while (left <= right) { // [left, right]
int mid = left + ((right - left) >> 1);
if (arr[mid] < tmp) {
left = mid + 1;
} else {
right = mid - 1;
}
++cnt;
}
int j = i - 1;
while (j >= left) {
arr[j + 1] = arr[j];
--j;
}
arr[left] = tmp - 1;
}
++cnt;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
vector<int> shellInsertSort(vector<int> arr) {
// 实现简单插入排序的基础上,优化移动次数成希尔排序
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
// 初始化窗口大小
int dx = 1;
while (dx <= n / 2) dx <<= 1;
while (dx) {
for (int i = 0; i < n - dx; ++i) {
for (int k = i + dx; k < n; k += dx) {
if (arr[k - dx] > arr[k]) { // 内部使用简单插入排序
tmp = arr[k];
// [i, i + dx, ..., k - dx] 都是有序的, 将 arr[k] 插入进去
int j = k - dx;
while (j >= 0 && ++cnt && arr[j] > tmp) {
arr[j + dx] = arr[j];
j -= dx;
}
arr[j + dx] = tmp;
}
++cnt;
}
}
dx >>= 1;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
vector<int> bubbleExchangeSort(vector<int> arr) {
// 实现冒泡排序
int cnt = 0; // 对比较次数进行计数
int tmp = -INT_MAX, n = arr.size();
for (int i = 0; i < n; ++i) {
bool flag = false; // 记录本回合是否发生交换
for (int j = n - 1; j > i; --j) {
if (++cnt && arr[j] < arr[j - 1]) {
swap(arr[j], arr[j - 1]);
flag = true;
}
}
if (!flag) {
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr; // 当前已经排序完成了
}
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
int partion(vector<int>& arr, int left, int right, int& cnt) {
int x = arr[left];
while (left < right) {
while (right > left && ++cnt && arr[right] >= x) --right;
arr[left] = arr[right];
while (left < right && ++cnt && arr[left] <= x) ++left;
arr[right] = arr[left];
}
arr[left] = x;
return left;
}
int quickSort(vector<int>& arr, int left, int right) {
int cnt = 0;
if (left >= right) return cnt;
int mid = partion(arr, left, right, cnt);
cnt += quickSort(arr, left, mid - 1);
cnt += quickSort(arr, mid + 1, right);
return cnt;
}
vector<int> quickSort(vector<int> arr) {
// 实现快速排序, 使用第一个元素作为基准元素
int n = arr.size();
int cnt = quickSort(arr, 0, n - 1);
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
vector<int> simpleSelectSort(vector<int> arr) {
// 实现简单选择排序
int cnt = 0; // 对比较次数进行计数
int n = arr.size();
for (int i = 0; i < n; ++i) {
int min_value = INT_MAX, min_loc = -1;
for (int j = i; j < n; ++j) {
if (++cnt && min_value > arr[j]) {
min_loc = j;
min_value = arr[j];
}
}
swap(arr[i], arr[min_loc]);
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
void adjustHeap(vector<int>& arr, int j, int back, int& cnt) {
while (j < back) {
int left = INT_MAX, right = INT_MAX;
if (2 * j < back) left = arr[2 * j];
if (2 * j + 1 < back) right = arr[2 * j + 1];
cnt += 2;
if (left < arr[j] && right < arr[j]) {
if (left < right) {
swap(arr[j], arr[2 * j]);
j = 2 * j;
} else {
swap(arr[j], arr[2 * j + 1]);
j = 2 * j + 1;
}
++cnt;
} else if (left < arr[j]) {
swap(arr[j], arr[2 * j]);
j = 2 * j;
} else if (right < arr[j]) {
swap(arr[j], arr[2 * j + 1]);
j = 2 * j + 1;
} else break; // 无法调整树型
}
}
vector<int> heapSort(vector<int> arr) {
// 实现堆排序
int cnt = 0; // 对比较次数进行计数
// 构建小根堆
int n = arr.size();
for (int i = n / 2; i >= 0; --i) {
adjustHeap(arr, i, n, cnt);
}
// 输出排序结果
vector<int> tmp(n);
int back = n - 1;
for (int i = 0; i < n; ++i) {
// 弹出堆顶元素
tmp[i] = arr[0];
// 调整堆
arr[0] = arr[back];
adjustHeap(arr, 0, back, cnt);
back--;
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return tmp;
}
vector<int> baseNumberSort(vector<int> arr) {
// 实现基数排序, 最低位优先
vector<vector<int>> nums(10);
int cur = 1; // 当前位数
int tmp = 10; // 非空桶个数
while (tmp > 1) {
// 入桶
for (int num : arr)
nums[num / cur % 10].push_back(num);
// 出桶
tmp = 0;
arr.clear();
for (auto& arr1 : nums) {
tmp += (arr1.size() != 0);
for (auto num : arr1) {
arr.push_back(num);
}
arr1.clear();
}
cur *= 10;
}
return arr;
}
vector<int> MergeSort(vector<int> arr) {
// 实现归并排序, 使用二路归并
int cnt = 0; // 对比较次数进行计数
function<void(vector<int>&, int, int, int)> merge = [&](vector<int>& arr, int low, int mid, int high) {
// [low, mid], [mid + 1, high] 有序,合并两个区间
vector<int> tmp(high - low + 1);
int left = low, right = mid + 1, ix = 0; // 双指针
while (left <= mid || right <= high) {
tmp[ix++] = arr[left] < arr[right] ? arr[left++] : arr[right++], ++cnt;
while (left > mid && right <= high) tmp[ix++] = arr[right++];
while (right > high && left <= mid) tmp[ix++] = arr[left++];
}
for (int i = low; i <= high; ++i)
arr[i] = tmp[i - low];
tmp.clear();
};
int k = 2, n = arr.size();
while (k <= n) {
for (int low = 0; low < n; low += k) {
if (low + k / 2 - 1 >= n - 1) continue;
int high = (low + k - 1) < n ? low + k - 1 : n - 1;
merge(arr, low, low + k / 2 - 1, high);
}
k <<= 1;
}
if (k / 2 <= n) {
merge(arr, 0, k / 2 - 1, n - 1);
}
cout << "算法比较次数为:" << cnt << " 排序结果为: " << endl;
return arr;
}
int main() {
// 实现八大排序 (排序结果都是从小到大排序), 加入输出比较次数。
vector<int> arr{12, 9, 19, 229, 210, 39, 67, 346, 234, 2, 2, 3, 3, 4, 5, 2, 7, 6, 8, 0};
cout << "排序前原数组: " << endl;
printArray(arr);
cout << "直接插入排序: ";
printArray(directInsertSort(arr));
cout << "折半插入排序: ";
printArray(binaryInsertSort(arr));
cout << "希尔插入排序: ";
printArray(shellInsertSort(arr));
cout << "冒泡交换排序: ";
printArray(bubbleExchangeSort(arr));
cout << "快速排序: ";
printArray(quickSort(arr));
cout << "简单选择排序: ";
printArray(simpleSelectSort(arr));
cout << "堆排序: ";
printArray(heapSort(arr));
cout << "基数排序: " << endl;
printArray(baseNumberSort(arr));
cout << "归并排序: ";
printArray(MergeSort(arr));
return 0;
}