使用插入排序优化的自底向上的归并排序的性能问题

使用插入排序优化的自底向上的归并排序的性能问题

使用了插入排序优化后,感觉性能差了不少,大约用时增加了50%,老师看看我是不是写的有问题呀,下面是完整的C++测试代码。

1
#include <iostream><br>#include <ctime><br>#include <iomanip><br>#include <cassert><br><br>using namespace std;<br><br>template <typename T><br>class MergeSort {<br> private:<br>  static void merge(T arr[], int l, int mid, int r, int temp[]) {<br>    // 创建临时数组 temp 存放 arr[l, r] 的内容<br>    // 复制当前的arr[l, r] 到 temp 中<br>    for (int i = l; i <= r; i++) temp[i] = arr[i];<br>    int i = l;<br>    int j = mid + 1;<br>    // 每轮循环为 arr[k] 赋值<br>    // i 的取值范围为[l, mid]<br>    // j 的取值范围为[mid + 1, r]<br>    for (int k = l; k <= r; k++) {<br>      // 取出 arr[i] 和 arr[j] 的最小值<br>      // arr[j] 对应 temp[j - l], arr[i] 对应 temp[i - l]<br>      if (i > mid) {<br>        // i 已经超过范围<br>        arr[k] = temp[j];<br>        j++;<br>      } else if (j > r) {<br>        // j 已经超过范围<br>        arr[k] = temp[i];<br>        i++;<br>      } else if (temp[i] <= temp[j]) {<br>        // 注意这里如果 T 不是基本类型,需要重载 <= 运算符<br>        arr[k] = temp[i];<br>        i++;<br>      } else {<br>        arr[k] = temp[j];<br>        j++;<br>      }<br>    }<br>  }<br><br> public:<br>  // 自底向上的归并排序(非递归)<br>  static void sortBU(T arr[], int length) {<br>    // 复制 arr 数组到 temp 数组<br>    T *temp = new T[length];<br>    for (int i = 0; i < length; i++) temp[i] = arr[i];<br><br>    // 遍历合并的区间长度<br>    for (int sz = 1; sz < length; sz += sz) {<br>      // 遍历合并的两个区间的起始位置 i<br>      // 合并 [i, i + sz - 1] 和 [i + sz, min(i + sz + sz - 1, length - 1)]<br>      for (int i = 0; i + sz < length; i += sz + sz) {<br>        int l = i;<br>        int mid = i + sz - 1;<br>        int r = min(i + sz + sz - 1, length - 1);<br>        if (arr[mid] > arr[mid + 1]) merge(arr, l, mid, r, temp);<br>      }<br>    }<br>  }<br><br>  // 使用插入排序优化的自底向上的归并排序(非递归)<br>  static void sortBUI(T arr[], int length) {<br>    // 复制 arr 数组到 temp 数组<br>    T *temp = new T[length];<br>    for (int i = 0; i < length; i++) temp[i] = arr[i];<br><br>    // 对于长度小于等于16的区间使用插入排序<br>    for (int k = 0; k < length; k += 16) {<br>      for (int i = k + 1; i < min(length - 1, k + 15); i++) {<br>        T t = arr[i];<br>        int j;<br>        for (j = i; j - 1 >= 0 && t < arr[j - 1]; j--) arr[j] = arr[j - 1];<br>        arr[j] = t;<br>      }<br>    }<br><br>    // 遍历合并的区间长度<br>    for (int sz = 1; sz < length; sz += sz) {<br>      // 遍历合并的两个区间的起始位置 i<br>      // 合并 [i, i + sz - 1] 和 [i + sz, min(i + sz + sz - 1, length - 1)]<br>      for (int i = 0; i + sz < length; i += sz + sz) {<br>        int l = i;<br>        int mid = i + sz - 1;<br>        int r = min(i + sz + sz - 1, length - 1);<br>        if (arr[mid] > arr[mid + 1]) merge(arr, l, mid, r, temp);<br>      }<br>    }<br>  }<br>};<br><br>template <typename T><br>class SortHelper {<br> public:<br>  // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]<br>  static int *generateRandomArray(int n, int rangeL, int rangeR) {<br>    // 设置断言,保证正确的输入<br>    assert(rangeL <= rangeR);<br>    int *arr = new int[n];<br>    // 设置随机数种子<br>    srand((unsigned int)time(NULL));<br>    for (int i = 0; i < n; i++) {<br>      // 标准的随机数生成方式<br>      arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;<br>    }<br>    return arr;<br>  }<br><br>  // 生成有n个元素的近乎有序的数组<br>  static int *generateNearlyOrderedArray(int n, int swapTimes) {<br>    int *arr = new int[n];<br>    for (int i = 0; i < n; i++) arr[i] = i;<br><br>    srand((unsigned int)time(NULL));<br>    for (int i = 0; i < swapTimes; i++) {<br>      int posx = rand() % n;<br>      int posy = rand() % n;<br>      swap(arr[posx], arr[posy]);<br>    }<br>    return arr;<br>  }<br><br>  // 生成有n个元素的完全有序的数组<br>  static int *generateOrderedArray(int n) {<br>    int *arr = new int[n];<br>    for (int i = 0; i < n; i++) arr[i] = i;<br>    return arr;<br>  }<br><br>  static void printArray(T arr[], int n) {<br>    for (int i = 0; i < n; i++) cout << arr[i] << " ";<br>    cout << endl;<br>  }<br><br>  static bool isSorted(T arr[], int n) {<br>    for (int i = 0; i < n - 1; i++)<br>      if (arr[i] > arr[i + 1]) return false;<br>    return true;<br>  }<br><br>  static void testSort(string sortName, void (*sort)(T[], int), T arr[],<br>                       int n) {<br>    clock_t startTime = clock();<br>    sort(arr, n);<br>    clock_t endTime = clock();<br><br>    assert(isSorted(arr, n));<br>    cout << setw(17) << sortName << ", n = " << n << " : "<br>         << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;<br>  }<br><br>  // 拷贝整形数组<br>  static int *copyIntArray(int a[], int n) {<br>    int *arr = new int[n];<br>    copy(a, a + n, arr);<br>    return arr;<br>  }<br>};<br><br>int main() {<br>  int dataSize[] = {10000, 100000, 1000000, 10000000};<br>  for (int n : dataSize) {<br>    int *arr1 = SortHelper<int>::generateRandomArray(n, 0, n);<br>    int *arr2 = SortHelper<int>::copyIntArray(arr1, n);<br><br>    cout << "\nFor Random Array:\n";<br>    SortHelper<int>::testSort("Merge Sort BU", MergeSort<int>::sortBU, arr1, n);<br>    SortHelper<int>::testSort("Merge Sort BUI", MergeSort<int>::sortBUI, arr2, n);<br><br>    arr1 = SortHelper<int>::generateNearlyOrderedArray(n, 10);<br>    arr2 = SortHelper<int>::copyIntArray(arr1, n);<br><br>    cout << "\nFor Nearly Ordered Array:\n";<br>    SortHelper<int>::testSort("Merge Sort BU", MergeSort<int>::sortBU, arr1, n);<br>    SortHelper<int>::testSort("Merge Sort BUI", MergeSort<int>::sortBUI, arr2, n);<br><br>    arr1 = SortHelper<int>::generateOrderedArray(n);<br>    arr2 = SortHelper<int>::copyIntArray(arr1, n);<br>    cout << "\nFor Fully Ordered Array:\n";<br>    SortHelper<int>::testSort("Merge Sort BU", MergeSort<int>::sortBU, arr1, n);<br>    SortHelper<int>::testSort("Merge Sort BUI", MergeSort<int>::sortBUI, arr2, n);<br>  }<br>}​<br>

正在回答

登陆购买课程后可参与讨论,去登陆

1回答

对长度为 16 的数组使用插入排序以后,下面的归并排序 sz 就不需要从 1 开始了。


P.S. 貌似你的区间插入排序的代码也是有问题的,可以在调试一下。


我之前的一个课程有 C++ 的 merge sort bu + insertion 优化的示例代码,可以参考:https://github.com/liuyubobobo/Play-with-Algorithms/blob/master/03-Sorting-Advance/Course%20Code%20(C%2B%2B)/04-Merge-Sort-Bottom-Up/main.cpp


继续加油!:)

  • 我是没有昵称 提问者 #1

    确实上课还不够仔细,做完插入排序后,sz应该从16开始,要不然插入排序部分就白做了,另外之前的插入排序算法也有一些问题。下面是修改后的代码:感觉应该ok啦,谢谢老师!

    1
    ​#include <cassert><br>#include <ctime><br>#include <iomanip><br>#include <iostream><br><br>using namespace std;<br><br>template <typename T><br>class MergeSort {<br> private:<br>  static void merge(T arr[], int l, int mid, int r, int temp[]) {<br>    // 创建临时数组 temp 存放 arr[l, r] 的内容<br>    // 复制当前的arr[l, r] 到 temp 中<br>    for (int i = l; i <= r; i++) temp[i] = arr[i];<br>    int i = l;<br>    int j = mid + 1;<br>    // 每轮循环为 arr[k] 赋值<br>    // i 的取值范围为[l, mid]<br>    // j 的取值范围为[mid + 1, r]<br>    for (int k = l; k <= r; k++) {<br>      // 取出 arr[i] 和 arr[j] 的最小值<br>      // arr[j] 对应 temp[j - l], arr[i] 对应 temp[i - l]<br>      if (i > mid) {<br>        // i 已经超过范围<br>        arr[k] = temp[j];<br>        j++;<br>      } else if (j > r) {<br>        // j 已经超过范围<br>        arr[k] = temp[i];<br>        i++;<br>      } else if (temp[i] <= temp[j]) {<br>        // 注意这里如果 T 不是基本类型,需要重载 <= 运算符<br>        arr[k] = temp[i];<br>        i++;<br>      } else {<br>        arr[k] = temp[j];<br>        j++;<br>      }<br>    }<br>  }<br><br> public:<br>  // 自底向上的归并排序(非递归)<br>  static void sortBU(T arr[], int length) {<br>    // 复制 arr 数组到 temp 数组<br>    T *temp = new T[length];<br>    for (int i = 0; i < length; i++) temp[i] = arr[i];<br><br>    // 遍历合并的区间长度<br>    for (int sz = 1; sz < length; sz += sz) {<br>      // 遍历合并的两个区间的起始位置 i<br>      // 合并 [i, i + sz - 1] 和 [i + sz, min(i + sz + sz - 1, length - 1)]<br>      for (int i = 0; i + sz < length; i += sz + sz) {<br>        int l = i;<br>        int mid = i + sz - 1;<br>        int r = min(i + sz + sz - 1, length - 1);<br>        if (arr[mid] > arr[mid + 1]) merge(arr, l, mid, r, temp);<br>      }<br>    }<br>  }<br><br>  // 使用插入排序优化的自底向上的归并排序(非递归)<br>  static void sortBUI(T arr[], int length) {<br>    // 复制 arr 数组到 temp 数组<br>    T *temp = new T[length];<br>    for (int i = 0; i < length; i++) temp[i] = arr[i];<br><br>    // 对于所有arr[i, i + 15]的区间使用插入排序<br>    for (int i = 0; i < length; i += 16) {<br>      int l = i;<br>      int r = min(length - 1, i + 15);<br>      for (int i = l + 1; i <= r; i++) {<br>        T e = arr[i];<br>        int j;<br>        for (j = i; j > l && e < arr[j - 1]; j--) arr[j] = arr[j - 1];<br>        arr[j] = e;<br>      }<br>    }<br><br>    // 遍历合并的区间长度,注意这里 sz 从 16 开始<br>    for (int sz = 16; sz < length; sz += sz) {<br>      // 遍历合并的两个区间的起始位置 i<br>      // 合并 [i, i + sz - 1] 和 [i + sz, min(i + sz + sz - 1, length - 1)]<br>      for (int i = 0; i + sz < length; i += sz + sz) {<br>        int l = i;<br>        int mid = i + sz - 1;<br>        int r = min(i + sz + sz - 1, length - 1);<br>        if (arr[mid] > arr[mid + 1]) merge(arr, l, mid, r, temp);<br>      }<br>    }<br>  }<br>};<br><br>template <typename T><br>class SortHelper {<br> public:<br>  // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]<br>  static int *generateRandomArray(int n, int rangeL, int rangeR) {<br>    // 设置断言,保证正确的输入<br>    assert(rangeL <= rangeR);<br>    int *arr = new int[n];<br>    // 设置随机数种子<br>    srand((unsigned int)time(NULL));<br>    for (int i = 0; i < n; i++) {<br>      // 标准的随机数生成方式<br>      arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;<br>    }<br>    return arr;<br>  }<br><br>  // 生成有n个元素的近乎有序的数组<br>  static int *generateNearlyOrderedArray(int n, int swapTimes) {<br>    int *arr = new int[n];<br>    for (int i = 0; i < n; i++) arr[i] = i;<br><br>    srand((unsigned int)time(NULL));<br>    for (int i = 0; i < swapTimes; i++) {<br>      int posx = rand() % n;<br>      int posy = rand() % n;<br>      swap(arr[posx], arr[posy]);<br>    }<br>    return arr;<br>  }<br><br>  // 生成有n个元素的完全有序的数组<br>  static int *generateOrderedArray(int n) {<br>    int *arr = new int[n];<br>    for (int i = 0; i < n; i++) arr[i] = i;<br>    return arr;<br>  }<br><br>  static void printArray(T arr[], int n) {<br>    for (int i = 0; i < n; i++) cout << arr[i] << " ";<br>    cout << endl;<br>  }<br><br>  static bool isSorted(T arr[], int n) {<br>    for (int i = 0; i < n - 1; i++)<br>      if (arr[i] > arr[i + 1]) return false;<br>    return true;<br>  }<br><br>  static void testSort(string sortName, void (*sort)(T[], int), T arr[],<br>                       int n) {<br>    clock_t startTime = clock();<br>    sort(arr, n);<br>    clock_t endTime = clock();<br><br>    assert(isSorted(arr, n));<br>    cout << setw(17) << sortName << ", n = " << n << " : "<br>         << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;<br>  }<br><br>  // 拷贝整形数组<br>  static int *copyIntArray(int a[], int n) {<br>    int *arr = new int[n];<br>    copy(a, a + n, arr);<br>    return arr;<br>  }<br>};<br><br>int main() {<br>  int dataSize[] = {10000, 100000, 1000000, 10000000, 100000000};<br>  for (int n : dataSize) {<br>    int *arr1 = SortHelper<int>::generateRandomArray(n, 0, n);<br>    int *arr2 = SortHelper<int>::copyIntArray(arr1, n);<br><br>    cout << "\nFor Random Array:\n";<br>    SortHelper<int>::testSort("Merge Sort BU", MergeSort<int>::sortBU, arr1, n);<br>    SortHelper<int>::testSort("Merge Sort BUI", MergeSort<int>::sortBUI, arr2, n);<br><br>    arr1 = SortHelper<int>::generateNearlyOrderedArray(n, 10);<br>    arr2 = SortHelper<int>::copyIntArray(arr1, n);<br><br>    cout << "\nFor Nearly Ordered Array:\n";<br>    SortHelper<int>::testSort("Merge Sort BU", MergeSort<int>::sortBU, arr1, n);<br>    SortHelper<int>::testSort("Merge Sort BUI", MergeSort<int>::sortBUI, arr2, n);<br><br>    arr1 = SortHelper<int>::generateOrderedArray(n);<br>    arr2 = SortHelper<int>::copyIntArray(arr1, n);<br>    cout << "\nFor Fully Ordered Array:\n";<br>    SortHelper<int>::testSort("Merge Sort BU", MergeSort<int>::sortBU, arr1, n);<br>    SortHelper<int>::testSort("Merge Sort BUI", MergeSort<int>::sortBUI, arr2, n);<br>  }<br>}<br><br>


    2021-04-13 22:10:56
  • liuyubobobo 回复 提问者 我是没有昵称 #2

    大赞!继续加油!:)

    2021-04-13 22:41:38
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
算法与数据结构
  • 参与学习       2603    人
  • 解答问题       1096    个

慕课网算法名师Liuyubobobo,5年集大成之作 从0到工作5年,算法与数据结构系统解决方案

了解课程
请稍等 ...
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师
插入代码