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

来源自我的博客

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;
}
点赞