您的位置:首页 > 编程语言

各类排序算法生成与测试样例代码

2017-04-01 10:30 323 查看
来源自我的博客

http://www.yingzinanfei.com/2017/04/01/geleipaixusuanfashengchengyuceshiyanglidaima/

#include <iostream>
#include <vector>
#include <random>
#include <algorithm>
#include <functional>
#include <string>
#include <map>
#include <unordered_map>
#include <cassert>
using namespace std;
const int maxn = 1000;      // 生成多少个数
const int max_range = 100000;       // 生成的数的范围0~max_range

void initNums(vector<int>& nums) {
// 初始化随机数数组
uniform_int_distribution<int> u(0, max_range);
default_random_engine e;
nums.assign(maxn, 0);
for (auto& num : nums) {
num = u(e);
}
}

/***********************************************************
* 直接插入排序
***********************************************************/
void directInsertSort(vector<int>& nums) {
for (unsigned i = 1; i < nums.size(); i++) {
// 依次将nums[1]~nums[n-1]插入到前面已排序序列
if (nums[i] < nums[i - 1]) {
// 当前元素需要往前插时
int num = nums[i];      // 暂存当前元素
int j;      // 向前遍历的索引
for (j = i - 1; j >= 0 && num < nums[j]; j--) {
nums[j + 1] = nums[j];  // 后移比当前元素大的元素
}
// 填充当前元素到合适位置,因为退出循环时j指向的元素是比当前元素小的,所以要赋值给下一个元素
nums[j + 1] = num;
}
}
}

/***********************************************************
* 二分插入排序
***********************************************************/
void binaryInsertSort(vector<int>& nums) {
for (unsigned i = 1; i < nums.size(); i++) {
// 依次将nums[1]~nums[n-1]插入到前面已排序序列
if (nums[i] < nums[i - 1]) {
// 当前元素需要往前插时
int num = nums[i];      // 暂存当前元素
int low = 0, high = i - 1;      // 折半查找的范围
while (low <= high) {
int mid = low + (high - low) / 2;   // mid倾向左半边
// 先判断大于会导致范围倾向左边,最后应使用high
if (nums[mid] > num) high = mid - 1;        // 查询左半范围
else low = mid + 1;     // 查询右半范围
}
// 跳出循环后,high位置元素是比小于等于当前元素的最后一个元素,所以要把当前元素插入到high的后面
for (int j = i - 1; j > high; j--) {
nums[j + 1] = nums[j];      // 统一后移元素,空出插入位置
}
nums[high + 1] = num;       // 插入当前元素
}
}
}

/***********************************************************
* 冒泡排序
***********************************************************/
void bubbleSort(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n - 1; i++) {
// 冒泡n-1次
bool flag = false;      // 表示本趟是否发生交换的标志
for (int j = n - 1; j > i; j--) {
// 一趟冒泡过程nums[i]~nums[n-1]
if (nums[j - 1] > nums[j]) {
// 若为逆序则交换
swap(nums[j - 1], nums[j]);
flag = true;
}
}
// 若本趟遍历后没有发生交换,说明表已经有序
if (!flag) return;
}
}

/***********************************************************
* 快速排序
***********************************************************/
void rangeQuickSort(vector<int>&, int, int);        // 对范围快速排序的函数声明
int onePartition(vector<int>&, int, int);       // 对范围进行一次划分的函数声明
void quickSort(vector<int>& nums) {
rangeQuickSort(nums, 0, nums.size() - 1);       // 转换成调用递归函数
}

void rangeQuickSort(vector<int>& nums, int low, int high) {
// 对范围[low, high]内数据进行排序
if (low < high) {
// low >= high时是递归终点,一个数即有序
int pivot_pos = onePartition(nums, low, high);      // 一次划分
rangeQuickSort(nums, low, pivot_pos - 1);
rangeQuickSort(nums, pivot_pos + 1, high);
}
}

int onePartition(vector<int>& nums, int low, int high) {
// 对范围[low, high]内数据进行一次划分,此处选择以nums[low]作为枢轴值
int pivot = nums[low];      // 暂存枢轴值,挖出一个坑
while (low < high) {
// 先从后往前找到第一个比枢轴值小的位置
while (low < high && nums[high] > pivot) high--;
// 将此位置的元素丢到前面挖的坑,使此位置成为一个坑
nums[low] = nums[high];
// 再从前往后找到第一个比枢轴值大的位置
while (low < high && nums[low] <= pivot) low++;
// 将此位置的元素丢到后面挖的坑,使此位置成为一个坑
nums[high] = nums[low];
}
// 循环结束时low==high,且仅剩此位置的一个坑
assert(low == high);
// 将暂存的枢轴值存到此坑中
nums[low] = pivot;
// 返回枢轴值的位置
return low;
}

/***********************************************************
* 简单选择排序
***********************************************************/
void selectSort(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n - 1; i++) {
// 进行n-1趟排序,对范围[i,n-1]进行排序
int min_index = i;      // 记录最小元素位置
for (int j = i + 1; j < n; j++) {
// 在nums[i...n-1]中选择最小元素
if (nums[j] < nums[min_index]) min_index = j;       // 更新最小元素位置
}
if (min_index != i) swap(nums[i], nums[min_index]);     // 将最小元素交换到前面来
}
}

/***********************************************************
* 堆排序
***********************************************************/
void buildMaxHeap(vector<int>&);        // 建大根堆的函数声明
void adjustDown(vector<int>&, int, int);        // 向下调整大根堆的函数声明
void heapSort(vector<int>& nums) {
// 从小到大,可以建大根堆,将最大元素依次取出放到后面
buildMaxHeap(nums);     // 初始建堆
for (int i = nums.size() - 1; i > 0; i--) {
// n-1趟排序,将堆顶最大元素与第i个位置元素交换,大元素就依次置后了
swap(nums[0], nums[i]);
// 重新调整堆,限制范围缩小以保护置后的大元素
adjustDown(nums, 0, i);
}
}

void buildMaxHeap(vector<int>& nums) {
// 建立大根堆
int n = nums.size();
for (int i = n / 2 - 1; i >= 0; i--) {
// 从一半元素下标开始往前,不断向下调整完整堆
adjustDown(nums, i, n);
}
}

void adjustDown(vector<int>& nums, int k, int max_len) {
// 从下标为k的元素开始循环向下调整大根堆,此过程限制在范围[0, max_len)中
int num = nums[k];      // 先暂存当前元素
for (int i = k * 2 + 1; i < max_len; i = i * 2 + 1) {
// 先使i指向左子元素,然后指向左右子元素中较大值
if (i + 1 < max_len && nums[i] < nums[i + 1]) i++;
if (nums[i] < num) {
// 如果子元素都比暂存元素小,说明不用向下调整了,跳出循环
break;
}
else {
// 如果子元素较大值比当前元素大,把此子元素提升到当前位置上
nums[k] = nums[i];
// 更新k的值,使其指向刚被提升的子元素处,继续迭代循环处理
k = i;
}
}
// 循环结束时,需要将暂存的元素放到坑中
nums[k] = num;
}

/***********************************************************
* 2-路归并排序
***********************************************************/
void rangeTwoWayMergeSort(vector<int>&, int, int);      // 对序列进行2-路归并排序的函数声明
void rangeTwoWayMerge(vector<int>&, int, int, int);     // 对序列的左右子序列进行2-路归并的函数声明
void twoWayMergeSort(vector<int>& nums) {
// 转换成调用范围内2-路归并排序
rangeTwoWayMergeSort(nums, 0, nums.size() - 1);
}

void rangeTwoWayMergeSort(vector<int>& nums, int low, int high) {
// 对序列nums[low]~nums[high]进行2-路归并排序
if (low < high) {
// low == high时表示为一个数,不用排序
int mid = low + (high - low) / 2;       // 从中间划分两个子序列
rangeTwoWayMergeSort(nums, low, mid);       // 对左侧子序列排序
rangeTwoWayMergeSort(nums, mid + 1, high);      // 对右侧子序列排序
rangeTwoWayMerge(nums, low, mid, high);     // 对两个有序子序列进行归并
}
}

void rangeTwoWayMerge(vector<int>& nums, int low, int mid, int high) {
// 对子序列nums[low]~nums[mid]和子序列nums[mid+1]~nums[high]进行归并,归并到nums[low]~nums[high]中
// 将前半段复制到临时数组
vector<int> first_half(mid - low + 1);
copy(nums.begin() + low, nums.begin() + mid + 1, first_half.begin());
// half1指向临时数组开始,表示序列左半段。half2指向序列右半段开始。dest指向序列左半段开始,表示最终合并位置
unsigned half1, half2, dest;
for (half1 = 0, half2 = mid + 1, dest = low; half1 < first_half.size() && half2 <= high; dest++) {
if (first_half[half1] <= nums[half2]) {
// 前半段对应值小于后半段对应值,选择前半段对应值
nums[dest] = first_half[half1++];
}
else {
// 选择后半段对应值
nums[dest] = nums[half2++];
}
}
// 出循环后如果临时数组还有剩余,全部复制到前半段
while (half1 < first_half.size()) nums[dest++] = first_half[half1++];
// 如果后半段有剩余,不需要复制,直接留在原数组中就有序了
}

int main() {
vector<int> nums;
initNums(nums);
vector<int> nums_order = nums;
sort(nums_order.begin(), nums_order.end());

vector < pair< function<void(vector<int>&)>, string > > functions{
{directInsertSort, "直接插入排序"},
{binaryInsertSort, "折半插入排序"},
{bubbleSort, "冒泡排序"},
{quickSort, "快速排序"},
{selectSort, "简单选择排序"},
{heapSort, "堆排序"},
{twoWayMergeSort, "2-路归并排序"}
};
for (auto& func : functions) {
vector<int> nums_out_of_order = nums;
func.first(nums_out_of_order);
if (nums_order.size() != nums_out_of_order.size() ||
!equal(nums_order.cbegin(), nums_order.cend(), nums_out_of_order.cbegin())) {
cout << func.second << "结果不对!" << endl;
for (auto& num : nums_out_of_order) cout << num << " "; cout << endl;
for (auto& num : nums_order) cout << num << " "; cout << endl;
}
else {
cout << func.second << "测试通过!" << endl;
//for (auto& num : nums_out_of_order) cout << num << " "; cout << endl;
//for (auto& num : nums_order) cout << num << " "; cout << endl;
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  排序算法 测试