Delphi 快速排序和堆栈溢出异常
我认为某些特定条件下的快速排序可能会导致堆栈溢出异常Delphi 快速排序和堆栈溢出异常,delphi,stack-overflow,quicksort,Delphi,Stack Overflow,Quicksort,我认为某些特定条件下的快速排序可能会导致堆栈溢出异常 在排序过程中有两种基本的选择枢轴元素的方法-枢轴值可以是排序范围中间的元素或者随机选择的元素(在排序范围内)。第二种方法(随机)是否比第一种方法更容易发生堆栈溢出?你能给我提个建议吗 以下是我的快速排序(Delphi)版本: 过程快速排序(LLOWBOND,lHighBound:integer;lCompare:TListSortCompare; lSwap:tlistortswap); 过程排序(lLowIndex,lHighIndex:i
在排序过程中有两种基本的选择枢轴元素的方法-枢轴值可以是排序范围中间的元素或者随机选择的元素(在排序范围内)。第二种方法(随机)是否比第一种方法更容易发生堆栈溢出?你能给我提个建议吗
以下是我的快速排序(Delphi)版本:过程快速排序(LLOWBOND,lHighBound:integer;lCompare:TListSortCompare;
lSwap:tlistortswap);
过程排序(lLowIndex,lHighIndex:integer);
变量
lLeft:整数;
lRight:整数;
lPivot:整数;
lLeftCompare:整数;
lRightCompare:整数;
开始
重复
lLeft:=lLowIndex;
lRight:=lHighIndex;
lPivot:=(lLowIndex+lHighIndex)第2分部//作为中间元素的枢轴
//lPivot:=lLowIndex+随机(lHighIndex-lLowIndex+1)//轴心是随机选择的
重复
lLeftCompare:=lCompare(lLeft,lPivot);
而lLeftCompare<0 do
开始
公司(lLeft);
lLeftCompare:=lCompare(lLeft,lPivot);
结束;
lRightCompare:=lCompare(lRight,lPivot);
当lRightCompare>0时,请执行以下操作
开始
十二月(右);
lRightCompare:=lCompare(lRight,lPivot);
结束;
如果不正确;
如果(lLowIndex=lHighIndex;
结束;
开始
如果lHighBound>LLOWBOND,则
排序(LLOWBOND、lHighBound);
结束;
提前谢谢你的建议
Mariusz.提高效率的概率方法是选择3个随机元素并使用中间值(不是最大值也不是最小值) 您还可以使用一堆记录来推送和弹出边界,并编写循环,而不是进行递归调用(由于不需要为所有调用复制指向数组的指针,因此它将使用更少的内存)
编辑:我注意到内部过程没有将指针作为参数,所以忘记部分^ ^无论如何,堆栈帧的信息比函数的参数更多,因此它的内存效率更高(主要的一点是,分配数据堆栈的堆通常比进程堆栈大)。在特定索引处使用任何元素(第一个、最后一个或中间)因为pivot元素总是会带来特定数据集退化的风险。第一个和最后一个元素尤其糟糕,因为它们会退化预排序(或几乎预排序)的数据,这是很常见的。中间元素在实践中问题较少,但仍然容易受到恶意构造的数据集的攻击
使用随机元素意味着退化只能通过纯粹的厄运发生(假设RNG不可由假设的攻击者预测),因此这是一个好策略。显著降低被厄运击中可能性的进一步改进是使用中位数3(或5,或更多)随机选择的元素,但它必须根据额外的复杂性和运行时间进行加权。感谢您的回答 Fortran,谢谢你关于创建非递归方法的建议。基于这些建议,我成功地进行了迭代快速排序,它似乎工作正常:) 代码如下:
procedure QuickSortI(lLowBound, lHighBound: integer; lCompare: TListSortCompare;
lSwap: TListSortSwap);
var
lLeft: Integer;
lRight: Integer;
lPivot: Integer;
lLeftCompare: Integer;
lRightCompare: Integer;
lStack: array of integer;
lStackLen: integer;
begin
if lHighBound > lLowBound then
begin
lStackLen := 2;
SetLength(lStack, lStackLen);
lStack[lStackLen - 1] := lLowBound;
lStack[lStackLen - 2] := lHighBound;
repeat
lLowBound := lStack[lStackLen - 1];
lHighBound := lStack[lStackLen - 2];
SetLength(lStack, lStackLen - 2);
Dec(lStackLen, 2);
lLeft := lLowBound;
lRight := lHighBound;
lPivot := (lLowBound + lHighBound) div 2;
repeat
lLeftCompare := lCompare(lLeft, lPivot);
while lLeftCompare < 0 do
begin
Inc(lLeft);
lLeftCompare := lCompare(lLeft, lPivot);
end;
lRightCompare := lCompare(lRight, lPivot);
while lRightCompare > 0 do
begin
Dec(lRight);
lRightCompare := lCompare(lRight, lPivot);
end;
if lLeft <= lRight then
begin
if not ((lLeftCompare = 0) and (lRightCompare = 0)) then
begin
lSwap(lRight, lLeft);
if lPivot = lLeft then
lPivot := lRight
else if lPivot = lRight then
lPivot := lLeft;
end;
Inc(lLeft);
Dec(lRight);
end;
until lLeft > lRight;
if (lHighBound > lLeft) then
begin
Inc(lStackLen, 2);
SetLength(lStack, lStackLen);
lStack[lStackLen - 1] := lLeft;
lStack[lStackLen - 2] := lHighBound;
end;
if (lLowBound < lRight) then
begin
Inc(lStackLen, 2);
SetLength(lStack, lStackLen);
lStack[lStackLen - 1] := lLowBound;
lStack[lStackLen - 2] := lRight;
end;
until lStackLen = 0;
end;
end;
过程快速排序(LLOWBOND,lHighBound:integer;lCompare:TLISTORTCOMPARE;
lSwap:tlistortswap);
变量
lLeft:整数;
lRight:整数;
lPivot:整数;
lLeftCompare:整数;
lRightCompare:整数;
lStack:整数数组;
lStackLen:整数;
开始
如果lHighBound>LLOWBOND,则
开始
lStackLen:=2;
设置长度(lStack,lStackLen);
lStack[lStackLen-1]:=lLowBound;
lStack[lStackLen-2]:=lHighBound;
重复
lLowBound:=lStack[lStackLen-1];
lHighBound:=lStack[lStackLen-2];
设置长度(lStack,lStackLen-2);
12月(伊斯塔克伦,2);
lLeft:=lLowBound;
lRight:=lHighBond;
lPivot:=(低低音+低低音)第2部分;
重复
lLeftCompare:=lCompare(lLeft,lPivot);
而lLeftCompare<0 do
开始
公司(lLeft);
lLeftCompare:=lCompare(lLeft,lPivot);
结束;
lRightCompare:=lCompare(lRight,lPivot);
当lRightCompare>0时,请执行以下操作
开始
十二月(右);
lRightCompare:=lCompare(lRight,lPivot);
结束;
如果不正确;
如果(lHighBond>lLeft),则
开始
公司(lStackLen,2),;
设置长度(lStack,lStackLen);
lStack[lStackLen-1]:=lLeft;
lStack[lStackLen-2]:=lHighBound;
结束;
如果(lLowBound
我希望我以最佳方式实现它。我使用了一个动态数组来存储排序边界(每对项都是下限和上限)
这种迭代方法似乎比递归方法稍微慢一点,但我认为这并不重要
如果您注意到错误或知道优化方法的方法,如果您让我知道,我将不胜感激
谢谢
Mariusz.一个合适的快速排序实现使用O(logn)堆栈空间。它通过首先对最小的子数组进行排序来实现这一点。如果不这样做,最糟糕的情况是枢轴是最大的元素,并且您尝试对一个子数组进行排序,每次只对一个子数组进行排序。当您使用已排序的数据作为输入,并将正确的元素作为轴心时,就会发生这种情况 显式堆栈实现速度较慢,并且会受到
procedure QuickSortI(lLowBound, lHighBound: integer; lCompare: TListSortCompare;
lSwap: TListSortSwap);
var
lLeft: Integer;
lRight: Integer;
lPivot: Integer;
lLeftCompare: Integer;
lRightCompare: Integer;
lStack: array of integer;
lStackLen: integer;
begin
if lHighBound > lLowBound then
begin
lStackLen := 2;
SetLength(lStack, lStackLen);
lStack[lStackLen - 1] := lLowBound;
lStack[lStackLen - 2] := lHighBound;
repeat
lLowBound := lStack[lStackLen - 1];
lHighBound := lStack[lStackLen - 2];
SetLength(lStack, lStackLen - 2);
Dec(lStackLen, 2);
lLeft := lLowBound;
lRight := lHighBound;
lPivot := (lLowBound + lHighBound) div 2;
repeat
lLeftCompare := lCompare(lLeft, lPivot);
while lLeftCompare < 0 do
begin
Inc(lLeft);
lLeftCompare := lCompare(lLeft, lPivot);
end;
lRightCompare := lCompare(lRight, lPivot);
while lRightCompare > 0 do
begin
Dec(lRight);
lRightCompare := lCompare(lRight, lPivot);
end;
if lLeft <= lRight then
begin
if not ((lLeftCompare = 0) and (lRightCompare = 0)) then
begin
lSwap(lRight, lLeft);
if lPivot = lLeft then
lPivot := lRight
else if lPivot = lRight then
lPivot := lLeft;
end;
Inc(lLeft);
Dec(lRight);
end;
until lLeft > lRight;
if (lHighBound > lLeft) then
begin
Inc(lStackLen, 2);
SetLength(lStack, lStackLen);
lStack[lStackLen - 1] := lLeft;
lStack[lStackLen - 2] := lHighBound;
end;
if (lLowBound < lRight) then
begin
Inc(lStackLen, 2);
SetLength(lStack, lStackLen);
lStack[lStackLen - 1] := lLowBound;
lStack[lStackLen - 2] := lRight;
end;
until lStackLen = 0;
end;
end;