各类排序算法生成与测试样例代码
2017-04-01 10:30
323 查看
来源自我的博客
http://www.yingzinanfei.com/2017/04/01/geleipaixusuanfashengchengyuceshiyanglidaima/
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; }
相关文章推荐
- 微型项目实践(2):用测试驱动代码生成
- 动态生成代码测试090325
- 在谷歌地图上随机生成指定数量的点,测试负载能力的代码。
- java 发送邮件 email相关操作代码测试,生成复杂格式邮件,发送邮件相关操作(键人岐)
- java 发送邮件 email相关操作代码测试,生成复杂格式邮件,发送邮件相关操作
- C#的图片处理类,生成高质量缩略图,代码已测试过,效果很好
- asp生成和导出excel和word数据源码和代码,简单好用(已经测试可以用)
- junit + ant + emma生成代码测试覆盖率报告
- QQ在线客服代码生成(测试有效)
- 排序算法大数据量测试代码
- 随机生成代码——已测试
- 生成垃圾文件和测试代码执行时间
- MySQL与MSSQl使用While语句循环生成测试数据的代码
- Axis2生成客户端代码和测试代码
- 【算法】各种排序算法测试代码
- 简单P -码生成过程的实现及测试代码
- 测试语法高亮的 C# 代码的 html fragment 生成
- Silverlight 创建WCF生成客户端代码及测试
- [memo]intelij idea 自动生成测试代码junit设置到maven标准测试目录
- 排序算法大数据量测试代码