思路如下:
背景:
Delphi的TStringList可以对内部字符串实现二分法高速排序。
字符串排序的原理为:对待比较大小的两个字符串从左到右进行扫描,对相同位置的字
符进行大小比较——ASCII码大的即大,若遇到相等的,则继续比较下一个字符,直到其中
一个字符串结束,最终排序的结果为小者在上,大者在上的字符串列表。(ps:TStringList
的排序对英文大小写字母有特殊处理,所以,我们使用十六进制编码来避免这个陷阱——十
六进制字符能被自然排序 0<1<2<...<9<A<B<...<F)。
例子: '1' > '0' , '01' < '02' , '1A' > '19' , '3F0021' > '3C9876'
若待排序的数据只有一列,并且可以很容易的转化为等长的字符串(同时要满足:数据
大的转换出来的字符串的值也大,不会造成排序错乱)。那么我们大可以直接将该列数据依
次编码为字符串并存入TStringList(利用AddObject将数据的初始序号强制转化为TObject
一并绑定存入),然后利用Sort方法就自动完成排序了。例子如下:
with TStringList.Createdo
begin
AddObject('018',TObject(1));
AddObject('002',TObject(2));
AddObject('019',TObject(3));
AddObject('310',TObject(4));
ShowMessage(Text);
Sort;
ShowMessage(Text);
Free;
end;
若待排序的数据列超过一列,那么不同数据列之间必然存在排序优先级的差别,我们可
以利用字符串可无限拼接的特点来排序:
AddObject('30'+'018',TObject(1));
AddObject('22'+'002',TObject(2));
AddObject('22'+'019',TObject(3));
AddObject('04'+'310',TObject(4));
——只要保证特定的列的值无论大小都能在拼接过程中保持相同的长度,就能够在对自
身进行正确排序的同时不影响其它列的排序行为。
至于倒序排列的问题,可以被转化为将该列所有值×-1然后再正向排序的问题从而得到
解决。但考虑到负号并不在0..F的ASCII码范围内,直接乘以-1是行不通的(即便将负数强
行转化为不带符号的HEX字符串,也会以FFFF...开头而破坏与以0000...开头的正数之间的
正常大小关系,不能采用)。所以,我在此通过给出的已知上下限范围(即参数MinNum以及
MaxNum),将所有数据都转化为正数:
正向排序: NewX := X - MinNum => NewX in [ 0 .. MaxNum-MinNum ]
反向排序: NewX := MaxNum - X => NewX in [ 0 .. MaxNum-MinNum ]
(注:原来给出的反向排序计算方法经过论证在MinNum<0时存在Bug,现在修正
)
对于上面的例子,如果我们需要对第二列进行从大到小的排列,实际过程就应该这样:
(MinNum=0, MaxNum=1000)
AddObject('30'+'982',TObject(1));
AddObject('22'+'998',TObject(2));
AddObject('22'+'981',TObject(3));
AddObject('04'+'690',TObject(4));
——对这组字符串排序后,3就会排在2的前面,达到了目的。
最后,我使用了一个小技巧——用一个整数数组来描述索引:
数值的绝对值表示排序时使用的列序号;
数值的符号决定了使用正向(小者在上),还是反向(大者在上)排序方式;
为零的索引值表示这个位置不参加排序;