1

我正在实现 Cormen's Algorithm book (CLRS) 中的快速排序算法。

 vector<int> numbers = {5, 6, 3, 4, 1, 2, 7, 13, -6, 0, 3, 1, -2};

 My_Quick_Sort(numbers.begin(), numbers.end());

它没有错误,但无法对数字进行排序。

书中的伪代码如下。

  1. 快速排序(A, p, r)
  2. 如果 p < r
  3. q = 分区(A, p, r)
  4. 快速排序(A, p, q-1)
  5. 快速排序(A, q+1, r)

  6. 分区(A, p, r)

  7. x = A[r]
  8. 我 = p - 1
  9. 对于 j = p 到 r - 1
  10. _ _ _i= i+1;
  11. _ _ _用 A[j] 交换 A[i]
  12. 将 A[i+1] 与 A[r] 交换
  13. 返回 i + 1;

我的实现如下。

 template<typename T>
 void Swap(T a, T b)
 {
     typedef typename std::iterator_traits<T>::value_type value_type;
     value_type temp;
     temp = *a;
     *a = *b;
     *b = temp;
 }

 template<typename T>
 int Partition(T begin, T end)
 {
     typedef typename std::iterator_traits<T>::value_type value_type;
     value_type x;
         x = *end;
     T i = begin - 1;
     T j;
     for(j = begin; j != end+1; ++j)
     {
         if ( x >= *j )
         {
              i++;
              Swap(i, j);
         }
         Swap(i+1, end);
     }
     return static_cast<int>(distance(begin, i) + 1);
 }

 template<typename T>
 void Q_Sort(T begin, T end)
 {
     auto length = end - begin;
     if (length < 2) return;

     if ( begin != end )
     {
         auto pivot = Partition(begin, end);
         Q_Sort(begin, begin + pivot - 1);
         Q_Sort(begin + pivot + 1, end);
     }
 }

有人对我的代码有任何想法吗?它有效,但不进行排序。例如,如果我输入 shuffle: 13, 0, -6, 6, -2, 5, 4, 3, 1, 1, 3, 2, 7,

输出类似于以下 My_Quick_Sort: -6, -2, 0, 6, 13, 0, 5, 1, 1, 4, 2, 3, 7,

4

2 回答 2

1

关于您的实施的一些注意事项:

首先,为了简化您的 Q_Sort 方法和逻辑,我将从分区方法返回一个迭代器,而不是一个 int。这将简化 Q_Sort 如下:

template<typename T>
void Q_Sort(T begin, T end)
{
    if ( begin < end )
    {
        T pivot = Partition(begin, end);
        Q_Sort(begin, pivot - 1);
        Q_Sort( pivot + 1, end);
    }
}

请注意,您不需要检查“if (length < 2) return;”

其次,在 for 循环中的分区方法中,您的终止条件“j!= end+1”与伪代码不匹配。它应该是 end - 1。这是 Partition 方法的新代码。请注意,我假设第二个参数 (end) 指向实际的最后一个值,而不是指向最后一个以外的值。

template<typename T>
T Partition(T begin, T end)
{
    typedef typename std::iterator_traits<T>::value_type value_type;
    value_type x;

    x = *end;
    T i = begin - 1;

    for(T j = begin; j < end; ++j)
    {
        if ( x >= *j )
        {
            i++;
            Swap(i, j);
        }
    }

    Swap(i+1, end);
    //return static_cast<int>(distance(begin, i) + 1);
    return i+1;
}

最后,我相信伪代码假定第二个参数是最后一个元素,但迭代器 numbers.end() 指向最后一个元素之外的位置。因此,您需要将调用更改为快速排序,如下所示:

vector<int>::iterator iterEnd = numbers.end();
--iterEnd;
Q_Sort(numbers.begin(), iterEnd);

在考虑了以上几点之后,您应该能够正确排序。

于 2013-08-10T12:18:37.780 回答
1

在几乎所有失败的 quicksort() 算法中,压倒性的罪魁祸首是分区算法未能正确排除枢轴槽或其中低端和高端的不正确数学。这没有什么不同。查看进行就地分区的实时示例。

在您的情况下,我将确保您的分区算法假定被分区的区域从之前begin元素开始和结束,换句话说:end

for(j = begin; j != end+1; ++j)

应该是这样的:

for(j = begin; j != end; ++j)

quicksort() 失败的第二个压倒性原因是未能仅跳过前一次分区运行的枢轴。如果你做了之前代码中提到的正确的事情,那么:

 auto pivot = Partition(begin, end);
 Q_Sort(begin, begin + pivot - 1);  // <<=== -1 should not be here.
 Q_Sort(begin + pivot + 1, end);

其实应该是这样的:

 auto pivot = Partition(begin, end);
 Q_Sort(begin, begin + pivot);
 Q_Sort(begin + pivot + 1, end);

请记住,C++ 迭代器运行到end(),这是您想要的最后一个元素之后的第一个元素,因此不需要 -1。给定一个序列,例如。

int ar[] = { 5,6,2,7,9,8 }

并假设枢轴槽位于第四个槽(枢轴 = 3),然后

 Q_Sort(begin, begin + pivot);    // includes 5,6,2, NOT 7
 Q_Sort(begin + pivot + 1, end);  // includes 9,8, again, NOT 7

我知道这可能看起来很奇怪,但如果您不小心不想跳过枢轴插槽,那么调用将如下所示:

 Q_Sort(begin, begin + pivot);   // beginning through (pivot-1)
 Q_Sort(begin + pivot, end);     // pivot through end

这是 quicksort() 实现中的另一个常见错误。

在这些基础上工作,你应该会很好。

于 2013-08-10T12:50:29.960 回答