快排的一种新写法
pascal吧
全部回复
仅看楼主
level 1
黄金止手 楼主
这里用到了传递开放数组一部分的功能。
要求 FP 版本在 2.2 以上,NOIP/NOI 的至少是 2.4.0 所以不用担心,但其他地方可能不同。
type
ValSInt=LongInt;// ValSInt for data, LongInt for index
var
SortSwapSInt32:ValSInt;
SortKeySInt32:ValSInt;
procedure QsortSInt32(var a:array of ValSInt);
var
i,j:LongInt;// Using SizeInt is better
begin
i:=0;
j:=High(A);
SortKeySInt32:=A[(i+j) shr 1];
while i<j do
begin
if (i<High(A)) and (A[i]<SortKeySInt32) then
inc(i);
if (0<j) and (SortKeySInt32<A[j]) then
dec(j);
if i<=j then
begin
SortSwapSInt32:=A[i];
A[i]:=A[j];
A[j]:=SortSwapSInt32;
inc(i);
dec(j);
end;
end;
if i<High(A) then
QSortSInt32(A[i..High(A)]);
if 0<j then
QSortSInt32(A[0..j]);
end;
调用的时候写
QSortSInt32(Arr[left..right]);
就行了。传入时的数组下标可以不从 0 开始。
如果是朴素实现的话,比起传统的 QSort(left, right) 的写法更加体现实现的思路,不用在内部把数组名称写上了。
传递开放数组(形参列表的类型写成 array of TypeName)时,实际上会传两个值,一个是数组首地址,一个是数组长度-1 (即最低下标为0时的最高下标)。开放数组在过程内部的下标统一转成0。
(假如传入开放数组时没有用 var 或 const 的话,传值和传址都会发生,时间空间消耗都会很大)
(使用 const 时假如数组是已定义数组或其一部分,那么就不会发生传值)
但这种写法的代价是(3.0.0 版本)每层递归的栈空间占用比传统实现多出 2 个指针长度(32位下是 2*4B)。
而且把尾递归手动去除就不太方便了。传入开放数组时虽然也能进行手动去除尾递归,但需要传更多参数,写法上没有朴素实现那么优雅,复杂度的常数也会大一些。
2016年07月05日 05点07分 1
level 1
黄金止手 楼主
假如要保留通用性和手动去除尾递归的话,可以写成
type
PLongInt=^LongInt;
QSortByPtr(Base:PLongInt; BaseHigh:LongInt);
begin {...} end;
begin
QSortByPtr(@A[left], right-left); // or ord(right)-ord(left)
end.
这种形式。尾递归优化可以用指针算术来进行。
但这种写法是有点不太符合 Pascal 的习惯的,带上了 C 的风格。
这里 Base[x] 表示把 Base 视作 0 起始下标的 LongInt 数组的首地址,该数组下标为 x 的元素。
指针“数组”和真实一维数组的区别是指针不带数组长度信息(所以上面加了个 BaseHigh 来手动管理),程序无法进行范围检查,一旦越界可能会写到别的变量上去。
(发生分段错误(Segmentation fault)还是幸运的)
Pascal 是“不给做范围检查好难受”,不过也支持 C 的指针数组形式。
C 则是“要我这语言做范围检查,不如叫我去死。范围检查全都由你来做”。
2016年07月05日 05点07分 2
level 1
黄金止手 楼主
另外分块值(即小于它放左,大于它放右的值)变量和交换用变量放在递归过程的内外也是有取舍的。
在递归过程里面的话,CPU处理它们会快一点,但栈空间占用会多一些。
在递归过程外面的话,CPU处理会慢一些,栈空间占用会少一些。
栈空间的占用差异,准确来说就是局部变量占用空间字节对齐(32位程序是以 4B 为单位,64位以 8B)后的差异。
处理速度区别的原因是 CPU 有预读取的功能,需要用到的变量尽可能靠近存放会使得处理更快。
2016年07月05日 05点07分 3
1