如何在我的递归快速排序算法中防止堆栈溢出
Posted
技术标签:
【中文标题】如何在我的递归快速排序算法中防止堆栈溢出【英文标题】:How to prevent stack overflow in my recursive quicksort algorithm 【发布时间】:2019-09-25 11:49:20 【问题描述】:在处理大小超过 5000 的双向链表时,出现堆栈溢出错误。我的算法在每次递归后创建双向链表的 2 个新对象,这些对象在每次递归后附加到排序列表中。是否有另一种方法来创建对象,这样我就不必在快速排序的每次递归中都这样做?
static void quick_sort(List sortList, Node first, Node last)
//Quick sort algorithm
Node left, pivNode;
List bigList = new List();
List endList = new List();
int pivot;
pivNode = first; //Sets node to become pivot (1st node in given range)
left = first.next; //Sets comparison node
pivot = first.data; //Sets integer for pivot
if (last.next != null)
//Creates a new list if there are nodes after last node
endList.firstNode = last.next;
last.next = null;
endList.firstNode.prev = null;
if (left != null)
do
if (left.data > pivot)
Node popNode;
popNode = left;
removeNode(popNode, sortList); //Removes node larger than pivot from sortList
left = pivNode; //Resets left node to pivNode
if (bigList.firstNode == null) //Inserts larger node into bigList
insertBeginning(popNode, bigList);
else
popNode.next = popNode.prev = null;
insertEnd(popNode, bigList);
if (left.data <= pivot)
swapNode(left, pivNode); //Swaps smaller node with pivNode(moves smaller nodes to left of list)
pivNode = left; //Resets pivNode to correct node
left = left.next;
while (left != last.next); //Iterates until last given node
if (endList.firstNode != null)
//If endList exists
if (bigList.firstNode != null)
//If bigList exists
appendLists(bigList, endList); //Appends endList at end of bigList
else
//If bigList doesn't exist, changes it to endList
bigList = endList;
appendLists(sortList, bigList); //Appends bigList to end of sortList
if (endList.firstNode != null)
last = endList.firstNode.prev; //Set's correct last node
//Recursion until last has been fully sorted
if (pivNode.prev != first && pivNode != first)
quick_sort(sortList, first, pivNode.prev);
if (pivNode != last && first != last)
quick_sort(sortList, pivNode.next, last);
【问题讨论】:
"" 您需要一个停止/退出条件,您的方法决定不再递归调用自身... 由于您的代码中已经有一些停止条件(if
语句),显然某些事情并没有按照您的预期进行,从而使停止条件无效。当 ***Exception 抛出时,使用debugger 并分析程序的状态(尤其是递归方法中所有变量的值)...
我已经用较小的列表(最大为 5000)测试了代码,它们都可以正常工作,因为 if 语句最终会导致递归结束。这让我相信问题不在于停止条件。
关于您的最后一条评论,可能是如果您有大量元素要排序,那么您的递归可能会变得太深,即调用堆栈耗尽并因此触发 SO 异常.这个问题的解决方案通常是避免这种非常深的递归,这意味着将你的算法转换为迭代算法。 (如果您的教授禁止您使用迭代方法,您将不得不将自己限制为“不太大的列表”,或者增加调用堆栈大小:***.com/questions/2556938/…)
在多次递归调用的情况下,递归总是存在导致堆栈溢出的风险(实际限制取决于平台/技术堆栈限制)。这就是为什么许多现代编译器能够将尾递归方法优化为迭代方法并且这种技术被视为足够安全的原因。不幸的是,这不是 C# 编译器的情况,因此您必须处理这样一个事实,即您有发生堆栈溢出的风险,或者您需要重新制作您的方法以使用最接近递归技术并保证堆栈安全的蹦床。跨度>
【参考方案1】:
为防止堆栈溢出,创建两个计数器,名称分别为 left_counter 和 right_counter。在分区步骤期间,更新计数器,以便 left_counter = 从 first 到 pivot 或 pivot.prev 的节点数,而 right_counter 是从 pivot 或 pivot.next 到 last 的节点数。比较两个计数器并在对应于较小计数的子列表上使用递归。然后根据排序的部分(通过递归)更新 first = pivot.next 或 last = pivot.prev 并循环回到代码的顶部以继续。
在排序过程中,创建循环双向链表可能更简单,这样 first.prev 指向最后一个节点,last.next 指向第一个节点。这将消除在第一个或最后一个节点处或旁边交换节点时必须检查空值。
代码显示了使用名为 List 的类类型,但这通常是 C# 的本机类,具有索引(随机访问)“列表”中的元素的能力,类似于数组。可能应该为列表类使用不同的名称,也许,“DLList”表示双向链表。
【讨论】:
以上是关于如何在我的递归快速排序算法中防止堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章