快速排序变种实现:一次宏定义引发的熬夜事件

一、背景  

  睡前忽然想写两行代码练练手,想起快速排序的一种变种实现,于是写了快速排序,在理论上完全没问题的情况下测试结果却很诡异,debug半天发现是宏定义导致,作为经验教训记录下来。

二、快速排序变种实现

  原理:

    |_| |_| |_________________________________________|

    L    R                     unSorted

              ---------------------------------------------------------------------------------------------------------------------

   1、取哨兵下标为low,即首元素;初始状态L和R集合都为空,L集合用来存放小于pivot的值,R集合用于存放大于pivot的值。

              pivot = vector<T>& vec[low] ;   mi = low;

                 ↓mi

              |——|

              |_| |_| |_________________________________________|

    L    R                     unSorted

   2、遍历初始集合,如果发现当前下标元素值大于pivot则迭代器加1,不做任何操作,效果相当于移动元素到R中。

                          idx

              |_| |__| |________________________________________|

    L    R                     unSorted

       3、如果发现当前下标元素值小于pivot则需要将当前值放于集合L中,为了提升效率,使当前元素和R集合的首元素交换,然后迭代器加1;作用效果相当于L元素新增,R元素整体后移一位。

                    mi

                     ↓      idx

              |__| |__| |_______________________________________|

    L    R                     unSorted

  4、当遍历完当前序列后unSorted的将为空,L和R将包含所有小于/大于pivot元素的集合。

                                                      mi

                 ↓

              |___________________| |_________________________|

                    L                                           R

  5、最终将首元素,即pivot和mi所指的元素交换,左右效果是mi这个选出来的哨兵节点已经就位,完成了一趟排序,之后将调用递归的方式对前半段和后半段进行排序(和大家所熟知的快排一致)。

三、具体实现,以下代码中swap函数用宏定义实现,为了逻辑紧凑,在swap的参数中用了“++”操作符。 

  1、排序逻辑                   

 1 #ifndef __ALGO_SORT_QUICK_SORT_IMPROVED_H__
 2 #define __ALGO_SORT_QUICK_SORT_IMPROVED_H__
 3 
 4 #include <vector>
 5 #include <iostream>
 6 using namespace std;
 7 
 8 #define swap(a, b)    ({
 9     typeof(a) tmp = (a) ; 
10     (a) = (b); 
11     (b) = tmp; })
12 
13 template<typename Type>
14 static int partion(std::vector<Type>& elems, int low, int high)
15 {
16     Type pivot = elems[low];
17     int mi = low;
18     for (int i = low + 1; i <= high; i++)
19     {   
20         if (elems[i] < pivot)
21         {
22             //++mi;
23             swap(elems[++mi], elems[i]);
24         }
25     }   
26     swap(elems[mi], elems[low]);
27     return mi; 
28 }
29 
30 template<typename Type>
31 void quick_sort_improved(std::vector<Type>& elems, int low, int high)
32 {
33     if (low < high) 
34     {   
35         int mi = partion(elems, low, high);
36         quick_sort_improved(elems, low, mi - 1); 
37         quick_sort_improved(elems, mi + 1, high);
38     }   
39 }
40 
41 #endif

  2、测试程序

1     vector<double> nums{18.1,16.12,32.21,12.22,13.1,53.21,221.5,354,123,42,22.11,33};
2     quick_sort_improved<double>(nums, 0, nums.size() - 1); 
3     for (int i = 0; i < nums.size(); i++ )
4     {   
5         cout << nums[i] << " ";
6     }   
7     cout << endl;

  3、测试输出

yg@yg-PC:~/workspace/Algo/src/sort/insertSort$ ./a.out 
6.52013e-319 6.52013e-319 6.52013e-319 12.22 12.22 16.12 33 0 0 0 42 42 

   4、分析:由上结果可知,测试结果并未按照预期输出,乱七八糟的。

四、修复版

  加了诸多debug信息,得知问题出在使用宏定义的过程中传入了++操作符,熟悉宏定义的人肯定遇到过类似问题;当前问题是:宏定义中表达式出现几次,++将会被调用几次,这当然不是期望的结果;知道原因后稍加修改。

template<typename Type>
static int partion(std::vector<Type>& elems, int low, int high)
{
    Type pivot = elems[low];
    int mi = low;
    for (int i = low + 1; i <= high; i++)
    {   
        if (elems[i] < pivot)
        {
            ++mi;
            swap(elems[mi], elems[i]);
        }
    }   
    swap(elems[mi], elems[low]);
    return mi; 
}

   测试输出:正确,符合预期

yg@yg-PC:~/workspace/Algo/src/sort/insertSort$ ./a.out 
12.22 13.1 16.12 18.1 22.11 32.21 33 42 53.21 123 221.5 354

五、总结

  日常代码中宏定义有时候无法避免,就像上面用到的swap,即便实现上已经避免了很多低级错误;但宏定义依然有很多不尽人意之处,就像上面的使用,编译器甚至都不会给个警告,在运行期也可以正常运行,但结果通却是莫名其妙的错误;通过一番折腾找出原因所在,以后在使用宏定义的地方一定要避免这种操作。

原文地址:https://www.cnblogs.com/zhangyi-studio/p/12117269.html