首页 / 知识

关于delphi:对数组进行排序的最佳方法

2023-04-14 01:53:00

关于delphi:对数组进行排序的最佳方法

Best way to sort an array

说我有一个记录数组,我想根据记录中的一个字段对它们进行排序。实现此目标的最佳方法是什么?

1
2
3
4
5
6
TExample = record
  SortOrder : integer;
  SomethingElse : string;
end;

var SomeVar : array of TExample;

您可以将指向数组元素的指针添加到TList,然后使用比较函数调用TList.Sort,最后创建一个新数组,并按所需顺序将值复制到TList中。 >

但是,如果您使用的是下一版本D2009,则有一个新的collections库,可以对数组进行排序。对于自定义排序顺序,它需要一个可选的IComparer<TExample>实现。这是针对您的特定情况的操作:

1
2
3
4
5
TArray.Sort<TExample>(SomeVar , TDelegatedComparer<TExample>.Construct(
  function(const Left, Right: TExample): Integer
  begin
    Result := TComparer<Integer>.Default.Compare(Left.SortOrder, Right.SortOrder);
  end));

(我知道这是一年后的事,但仍然有用。)

Skamradt关于填充整数值的建议假设您要使用字符串比较进行排序。这会很慢。每次插入都会调用format(),但速度较慢。相反,您想进行整数比较。

您从记录类型开始:

1
2
3
4
TExample = record
  SortOrder : integer;
  SomethingElse : string;
end;

您没有说明记录的存储方式,或排序后想要访问的方式。因此,假设您将它们放入动态数组中:

1
2
3
4
5
6
7
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作为指针添加。然后按指针排序:

1
2
3
4
5
6
7
8
9
10
11
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指针:

1
2
3
4
5
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进行排序(它将对字符串值进行排序。)

1
tsExample.CustomSort(@CompareObjects);     //Sort the list

现在已对TStringList进行排序,因此您可以将其从0迭代到.Count-1并按排序顺序读取字符串。

但是假设您不想要TStringList,而只想要按排序顺序的数组。或者,在此示例中,记录包含的数据不仅仅是一个字符串,而且您的排序顺序更加复杂。您可以跳过添加每个字符串的步骤,只需将数组索引添加为TList中的Items。除了使用TList代替TStringList以外,以相同的方式执行上述所有操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
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已排序,将其用作查找表以按排序顺序访问数组:

1
2
3
  for i:=0 to Mlist.Count-1 do begin
    Something := MyDA[integer(Mlist[i])].SomeField;
  end;

当我遍历TList时,我们以排序的顺序取回数组索引。我们只需要将它们强制转换回整数,因为TList认为它们是指针。

我喜欢这样,但是您也可以通过添加数组元素的地址而不是其索引,在TList中将实际指针放置到TList中。然后,要使用它们,可以将它们转换为TExample记录的指针。这是Barry Kelly和CoolMagic在回答中说的。


如果您需要按字符串排序,请使用排序后的TStringList
通过TString.AddObject(string, Pointer(int_val))添加记录。

但是如果需要按整数字段和字符串排序-使用TObjectList,并在添加所有记录后调用TObjectList.Sort,并将必要的排序函数作为参数。


这全部取决于您要排序的记录数。如果您只排序不到几百种,那么其他排序方法也可以正常工作;如果您要进行更多排序,那么请仔细看一下旧的可信赖的Turbo Power SysTools项目。源代码中包含一个非常好的排序算法。一个很好的工具,可以高效地对数百万条记录进行排序。

如果要使用tStringList方法对记录列表进行排序,请在将整数插入列表之前确保将其整数填充到右边。例如,您可以使用格式(\\'%。10d \\',[rec.sortorder])右对齐至10位数字。


当需要快速排序时,通常使用快速排序算法。例如,Delphi正在(或曾经)将其用于List.Sort。
Delphi List可以用来对任何东西进行排序,但是它是一个重量级的容器,应该看起来像结构上的指针数组。即使我们在该线程中使用诸如Guy Gordon之类的技巧(将索引或其他内容代替指针,或者如果它们小于32位直接放置值)也很重要:我们需要构造一个列表,依此类推... <铅>

因此,轻松快速地对结构数组排序的一种替代方法可能是使用msvcrt.dll中的qsort C运行时函数。

这是一个可能不错的声明(警告:仅在Windows上可移植的代码)。

1
2
type TComparatorFunction = function(lpItem1: Pointer; lpItem2: Pointer): Integer; cdecl;
procedure qsort(base: Pointer; num: Cardinal; size: Cardinal; lpComparatorFunction: TComparatorFunction) cdecl; external 'msvcrt.dll';

此处是完整示例。

请注意,如果记录很大,则直接对记录数组进行排序可能会很慢。在这种情况下,对指向记录的指针数组进行排序会更快(类似于List方法)。


对于数组,我将使用quicksort或可能使用heapsort,并且只需将比较更改为使用TExample.SortOrder,交换部分仍将仅作用于数组和交换指针。如果数组很大,那么如果有很多插入和删除操作,则可能需要一个链表结构。

基于C的例程,这里有几个
http://www.yendor.com/programming/sort/

另一个站点,但是有pascal来源
http://www.dcc.uchile.cl/~rbaeza/handbook/sort_a.html


如果您使用的是Delphi XE2或更高版本,则可以尝试:

1
2
3
4
5
6
7
8
9
10
11
12
13
var
  someVar: array of TExample;
  list: TList<TExample>;
  sortedVar: array of TExample;
begin
  list := TList<TExample>.Create(someVar);
  try
    list.Sort;
    sortedVar := list.ToArray;
  finally
    list.Free;
  end;
end;

TStringList具有高效的排序方法。
如果要进行排序,请使用TStringList对象,将Sorted属性设置为True。

注意:为了提高速度,请在未排序的TStringList中添加对象,最后将属性更改为True。
注意:对于按整数字段排序,请转换为字符串。
注意:如果存在重复的值,则此方法无效。

致谢。


使用Wikipedia提议的一种排序规则。交换函数应使用与数组元素相同类型的临时变量交换数组元素。如果希望具有相同SortOrder整数值的条目保持在第一位的顺序,请使用稳定的排序。


我创建了一个非常简单的示例,如果排序字段是字符串,该示例将正常工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Type
  THuman = Class
  Public
    Name: String;
    Age: Byte;
    Constructor Create(Name: String; Age: Integer);
  End;

Constructor THuman.Create(Name: String; Age: Integer);
Begin
  Self.Name:= Name;
  Self.Age:= Age;
End;

Procedure Test();
Var
 Human: THuman;
 Humans: Array Of THuman;
 List: TStringList;
Begin

 SetLength(Humans, 3);
 Humans[0]:= THuman.Create('David', 41);
 Humans[1]:= THuman.Create('Brian', 50);
 Humans[2]:= THuman.Create('Alex', 20);

 List:= TStringList.Create;
 List.AddObject(Humans[0].Name, TObject(Humans[0]));
 List.AddObject(Humans[1].Name, TObject(Humans[1]));
 List.AddObject(Humans[2].Name, TObject(Humans[2]));
 List.Sort;

 Human:= THuman(List.Objects[0]);
 Showmessage('The first person on the list is the human ' + Human.name + '!');

 List.Free;
End;

数组方法排序目标

最新内容

相关内容

热门文章

推荐文章

标签云

猜你喜欢