我有一个包含一些数字的字符串列表。我使用我写的冒泡排序对它们进行了排序。输出是:
18
20
3
44
53
我无法理解为什么以上输出而不是我的预期:
3
18
20
44
53
我错过了什么?
答案 0 :(得分:9)
首先,您不需要编写自己的排序代码来对字符串列表中的字符串进行排序。字符串列表类附带一个sort函数。您可以使用它而不是编写自己的排序代码。
现在转到问题的关键点,在程序的输出中,这些值被正确排序,当被视为文本时。它们按字典或字典顺序正确排序。例如
'20' < '3'
因为文字是逐字符比较的,'2' < '3'
。
但您希望将值排序为数字。在这种情况下,您应首先将它们转换为数字,然后对数字而不是文本进行排序。这样做:
TArray<Integer>
,其大小可以保存字符串列表的每个值。 StrToInt
填充数组,将列表中的每个字符串转换为整数。Generics.Collections
单元中的TArray.Sort<T>
对数组进行排序。 或许更好的方法是不要将值存储为文本。将数据保存为数组或整数列表,然后对其进行排序。毕竟,如果您的数据是整数值,那么将它们存储起来是有意义的。
如果您将数据保存在数组中,则可以按上述TArray.Sort<T>
进行排序。或者,如果您使用TList<T>
,请使用其Sort
方法。
答案 1 :(得分:5)
正如大卫在his answer中解释的那样,你将字符串排序为文本字符,而不是整数。
您可以使用TStringList.CustomSort()
方法,在比较字符串时将字符串转换为整数:
function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
var
Value1, Value2: Integer;
begin
Value1 := StrToInt(List[Index1]);
Value2 := StrToInt(List[Index2]);
if Value1 < Value2 then
Result := -1
else if Value2 < Value1 then
Result := 1
else
Result := 0;
end;
SL.Add('18');
SL.Add('20');
SL.Add('3');
SL.Add('44');
SL.Add('53');
SL.CustomSort(MySortProc);
或者,将实际的整数值存储在Objects
属性中,这样您就不必在排序时连续转换字符串:
function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
var
Value1, Value2: Integer;
begin
Value1 := Integer(List.Objects[Index1]);
Value2 := Integer(List.Objects[Index2]);
if Value1 < Value2 then
Result := -1
else if Value2 < Value1 then
Result := 1
else
Result := 0;
end;
SL.Add('18', TObject(18));
SL.Add('20', TObject(20));
SL.Add('3', TObject(3));
SL.Add('44', TObject(44));
SL.Add('53', TObject(53));
SL.CustomSort(MySortProc);
或者,您可以使用StrCmpLogicalW()
让Windows为您比较字符串:
function StrCmpLogicalW(const psz1, psz2: PWideChar): Integer; stdcall; external 'Shlwapi.dll';
function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := StrCmpLogicalW(PChar(List[Index1]), PChar(List[Index2]));
end;
SL.Add('18');
SL.Add('20');
SL.Add('3');
SL.Add('44');
SL.Add('53');
SL.CustomSort(MySortProc);
答案 2 :(得分:0)
这是一种与Remy非常相似的方法,但它不使用CustomSort
,而是使用从TStringList
派生的新类来覆盖CompareStrings
。该实现使用来自 System.Math 的CompareValue
和字符串帮助函数ToInteger
。
优点是新类可以将Sorted
属性设置为true(触发排序顺便说一句)。这使Find
函数起作用,而IndexOf
则使用type
TIntegerStringList = class(TStringList)
protected
function CompareStrings(const S1: string; const S2: string): Integer; override;
end;
function TIntegerStringList.CompareStrings(const S1, S2: string): Integer;
begin
result := CompareValue(S1.ToInteger, S2.ToInteger);
end;
函数。新添加的(整数)字符串也按排序顺序插入。
geom_hex
如果这不是一项要求,我也会出于性能原因选择David的解决方案。
答案 3 :(得分:-1)
您正在使用TStringList
(字符串列表),因此排序适用于字符串,而不是数字。
一个简单的解决方案是使用TStringList
出现的排序方法。
您必须在代码中引入一个小变体。
如果您使用TStringList
,则会将Numbers转换为字符串,反之亦然。使用相同的工作,您可以在TStringList
中插入数字,其格式使排序方法正常工作。
TStringList
(Sorted
属性为False -default - )TStringList
上插入数字(并将其转换为字符串)时,请在左侧部分使用“0”。00000018
00000020
00000003
00000044
00000053
Sorted
属性更改为True
,让TStringList
完成工作。00000003
00000018
00000020
00000044
00000053
StrToInt
转换字符串。答案 4 :(得分:-1)
最快的方法由David描述 - 将字符串转换为整数数组,对该数组进行排序,然后如果必须(或更好地保留整数列表)则重新创建字符串列表。
我会添加最懒惰的方法。对于小型列表(大约100行)来说可能已经足够了,但它会比上面提到的正确方法慢,而且你拥有的项目越多,它就越慢。
懒惰的方法是安装Jedi代码库并使用增强的StringList。
你可以这样做:
var sl: iJclStringList;
begin
sl := JclStringList();
sl.Split( '18, 20, 3, 44, 53', ',' ).Trim().SortAsInteger();
ShowMessage( sl.Join(', ') );
ShowMessage( sl.Join(^M^J) );
sl := nil;
end;
同样,这种方法正在进行大量冗余的额外工作,因此对于任何或多或少的重要数据量,使用整数的正确方法是首选。