【问题标题】:Quicksort algorithm with Hoare's Partitioning Scheme returns original unsorted list使用 Hoare 分区方案的快速排序算法返回原始未排序列表
【发布时间】:2019-07-19 18:32:19
【问题描述】:

我尝试使用 Hoare 的分区方案来实现快速排序算法,但是当我在测试列表 {412, 123, 57, 12, 1, 5} 上运行它然后打印出来时,我得到了原始顺序的数字。有人可以帮我找出我做错了什么吗?下面是我的实现。

void Quicksort::sort(std::vector<int> &list)
{
   sort(list, 0, list.size() - 1);
}

void Quicksort::sort(std::vector<int> &list, int left, int right)
{
   if (left - right <= 1) return; // no need to partition a single element
   int pivot = left + (right - left) / 2; // <=> (left+right)/2, but avoids overflow
   int endIndex = partition(list, pivot, left, right); 
   sort(list, 0, endIndex - 1);
   sort(list, endIndex + 1, list.size() - 1);
}

int Quicksort::partition(std::vector<int> &list, int pivot, int left, int right)
{
   while (true)
   {
      while (list[left] < list[pivot])
         left++;
      while (list[right] > list[pivot])
         right--;
      if (left != right)
         std::swap(list[left], list[right]);
      else
         return left;
   }
}

要调用列表{412, 123, 57, 12, 1, 5} 上的快速排序算法,我使用以下代码:

std::vector<int> numbers = {412, 123, 57, 12, 1, 5};
Quicksort::sort(numbers);
for (int i = 0; i < numbers.size(); i++)
   std::cout << numbers[i] << "\n";

控制台输出是

412
123
57
12
1
5

编辑

修复了应该是if (right - left &lt;= 1) 的错误if (left - right &lt;= 1) 后,程序遇到错误Segmentation fault: 11。这让我相信我正在尝试访问超出范围的内容。

【问题讨论】:

  • 请将其设为minimal reproducible example。看起来你只需要添加几行就可以了,这对你来说很容易,但对我们来说还有很多缺失的部分
  • 您确定while (list[left] &lt; list[pivot]) left++; 不会越界吗?
  • @user463035818 我现在添加了有关如何调用我的快速排序实现以及我得到的确切输出以使问题最小化、完整和可验证的信息。
  • 我不认为while (list[left] &lt; list[pivot]) left++; 会越界。如果是这样,我应该得到一个错误,不是吗?
  • if (left - right &lt;= 1) right 不是大于left 吗?

标签: c++ algorithm sorting quicksort


【解决方案1】:

算法的分区部分没有以正确的方式实现。特别是,left 可能会变得大于right 而这

if (left != right)
     std::swap(list[left], list[right]);
//             ^^^^^^^^^^

可以越界访问向量。

看下面的sn-p:

int partition(std::vector<int> &list, int left, int right)
{
   // I'm calculating the pivot here, instead of passing it to the function
   int pivot = list[left + (right - left) / 2];
   while (true)
   {
      while (list[left] < pivot)
         left++;
      while (list[right] > pivot)
         right--;

      // Stop when the pivot is reached 
      if (left >= right)
         return right;

      // Otherwise move the elements to the correct side 
      std::swap(list[left], list[right]);
   }
}

void sort(std::vector<int> &list, int left, int right)
{
    // Execute only if there are enough elements
   if (left < right) 
   {
       int pivot = partition(list, left, right);

       // As NiVer noticed, you have to limit the range to [left, right]
       sort(list, left, pivot - 1); 
       sort(list, pivot + 1, right);
   }
}

可测试HERE

考虑使用迭代器以更通用的方式实现这些功能。

【讨论】:

  • @Dulvin 左移时分区函数不返回
  • @Dulvin 你在考虑哪个功能?
  • @Bob__ 分区函数。
  • @TedLyngmo 当它不返回任何值时,pivot 的值是多少?
  • @Dulvin 它总是会返回一个值。当left&gt;=right 时,它将返回right
【解决方案2】:

我认为代码的问题(或至少一个问题)是以下几行:

sort(list, 0, endIndex - 1);
sort(list, endIndex + 1, list.size() - 1);

这总是考虑整个列表,而不仅仅是剩余的未排序部分。您应该使用限制索引leftright

sort(list, left, endIndex - 1);
sort(list, endIndex + 1, right);

【讨论】:

  • 但是endIndex - 1endIndex + 1 不是分别作为限制性索引吗?因为在我看来,左子数组将始终从0 开始,而右子数组将始终以list.size() - 1 结束,但我想这仅适用于对分区的第一次调用,而不适用于连续的递归调用。跨度>
猜你喜欢
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 2015-05-15
  • 2022-07-01
  • 2017-09-27
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
相关资源
最近更新 更多