排序数组的最佳方法
说我有一个记录数组,我想根据记录中的一个字段对它们进行排序。实现此目标的最佳方法是什么?
TExample = record SortOrder : integer; SomethingElse : string; end; var SomeVar : array of TExample;
解决方案
回答
使用Wikipedia提出的一种排序规则。交换函数应使用与数组元素相同类型的临时变量交换数组元素。如果希望具有相同SortOrder整数值的条目保持在第一位的顺序,请使用稳定排序。
回答
我们可以将指向数组元素的指针添加到" TList"中,然后使用比较函数调用" TList.Sort",最后创建一个新数组,并按所需顺序将值复制到TList中。
但是,如果我们使用的是下一个版本D2009,则有一个新的collections库可以对数组进行排序。它需要一个可选的IComparer <TExample>
实现来实现自定义排序顺序。这是针对特定情况的操作:
TArray.Sort<TExample>(SomeVar , TDelegatedComparer<TExample>.Construct( function(const Left, Right: TExample): Integer begin Result := TComparer<Integer>.Default.Compare(Left.SortOrder, Right.SortOrder); end));
回答
对于数组,我将使用quicksort
或者可能的heapsort
,并仅将比较更改为使用TExample.SortOrder
,交换部分仍将仅作用于数组和交换指针。如果数组很大,那么如果有很多插入和删除操作,则可能需要一个链表结构。
基于C的例程,这里有几个
http://www.yendor.com/programming/sort/
另一个站点,但是有Pascal来源
http://www.dcc.uchile.cl/~rbaeza/handbook/sort_a.html
回答
如果我们需要按字符串排序,请使用排序后的TStringList和
通过TString.AddObject(string,Pointer(int_val))
添加记录。
但是,如果需要按整数字段和字符串排序,请使用" TObjectList",并在添加所有记录后调用" TObjectList.Sort",并将必要的排序函数作为参数。
回答
TStringList具有有效的排序方法。
如果要排序,请使用TStringList对象,将Sorted属性设置为True。
注意:为了提高速度,请在未排序的" TStringList"中添加对象,最后将属性更改为True。
注意:对于按整数字段排序,请转换为字符串。
注意:如果存在重复的值,则此方法无效。
问候。
回答
这完全取决于我们要排序的记录数。如果我们只排序不到几百种,那么其他排序方法也可以正常工作;如果我们要进行更多排序,那么请仔细看一下旧的可信赖的Turbo Power SysTools项目。源代码中包含一个非常好的排序算法。一个很好的工作,可以高效地对数百万条记录进行排序。
如果要使用tStringList方法对记录列表进行排序,请确保在将整数插入列表之前将其填充到右边。例如,我们可以使用格式('%.10d',[rec.sortorder])右对齐10位数字。
回答
(我知道这是一年后的事,但仍然有用。)
Skamradt关于填充整数值的建议假设我们要使用字符串比较进行排序。这会很慢。每次插入都会调用format(),但速度较慢。相反,我们想进行整数比较。
我们从记录类型开始:
TExample = record SortOrder : integer; SomethingElse : string; end;
我们没有说明记录的存储方式,或者排序后想要访问的方式。因此,假设我们将它们放入动态数组中:
var MyDA Array of TExample; ... SetLength(MyDA,NewSize); //allocate memory for the dynamic array for i:=0 to NewSize-1 do begin //fill the array with records MyDA[i].SortOrder := SomeInteger; MyDA[i].SomethingElse := SomeString; end;
现在,我们要按整数值SortOrder对该数组进行排序。如果要使用的是TStringList(因此可以使用ts.Find方法),则应将每个字符串添加到列表中,并将SortOrder作为指针添加。然后对指针进行排序:
var tsExamples: TStringList; //declare it somewhere (global or local) ... tsExamples := tStringList.create; //allocate it somewhere (and free it later!) ... tsExamples.Clear; //now let's use it tsExamples.sorted := False; //don't want to sort after every add tsExamples.Capacity := High(MyDA)+1 //don't want to increase size with every add //an empty dynamic array has High() = -1 for i:=0 to High(MyDA) do begin tsExamples.AddObject(MyDA[i].SomethingElse,TObject(MyDA[i].SortOrder)); end;
请注意将Integer SortOrder转换为TObject指针的技巧,该指针存储在TStringList.Object属性中。 (这取决于整数和指针大小相同的事实。)我们必须在某个地方定义一个函数来比较TObject指针:
function CompareObjects(ts:tStringList; Item1,Item2: integer): Integer; var i,j: integer; begin Result := integer(ts.Objects[i]) - integer(ts.Objects[j]; end;
现在,我们可以通过调用.CustomSort而不是.Sort来对.Object上的tsList进行排序(它将对字符串值进行排序)。
tsExample.CustomSort(@CompareObjects); //Sort the list
现在已对TStringList进行排序,因此我们可以将其从0迭代到.Count-1并按排序顺序读取字符串。
但是,假设我们不想要TStringList,而只想要按排序顺序的数组。或者,在此示例中,记录包含的数据不仅仅是一个字符串,而且排序顺序更加复杂。我们可以跳过添加每个字符串的步骤,而只需将数组索引添加为TList中的Items。除了使用TList而不是TStringList之外,其他操作均以相同的方式进行:
var Mlist: TList; //a list of Pointers ... for i:=0 to High(MyDA) do Mlist.add(Pointer(i)); //cast the array index as a Pointer Mlist.Sort(@CompareRecords); //using the compare function below function CompareRecords(Item1, Item2: Integer): Integer; var i,j: integer; begin i := integer(item1); //recover the index into MyDA j := integer(item2); // and use it to access any field Result := SomeFunctionOf(MyDA[i].SomeField) - SomeFunctionOf(MyDA[j].SomeField); end;
现在,Mlist已排序,将其用作查找表以按排序顺序访问数组:
for i:=0 to Mlist.Count-1 do begin Something := MyDA[integer(Mlist[i])].SomeField; end;
当我遍历TList时,我们以排序的顺序取回数组索引。我们只需要将它们强制转换为整数,因为TList认为它们是指针。
我喜欢这样,但是我们也可以通过添加数组元素的地址而不是索引来在TList中放置指向数组元素的实际指针。然后,要使用它们,可以将它们转换为TExample记录的指针。这就是Barry Kelly和CoolMagic在回答中所说的。