我需要按排序顺序保留前十个值。我的数据结构是:
TMyRecord = record
Number: Integer;
Value: Float;
end
我将计算一堆浮点值。我需要保持前10个浮动值。每个值都有一个相关的数字。我想添加“sets”...如果我的浮点值高于前10位中的一个,它应该将自己添加到列表中,然后“旧”数字10(现在为11)被丢弃。我应该能够以(浮点值)排序顺序访问列表...
它几乎就像一个TStringList,它维护排序顺序......
Delphi 2010中是否已经内置了这样的内容?
答案 0 :(得分:5)
您可以使用通用列表Generics.Collections.TList<TMyRecord>
和插入排序的组合。
您的数据结构是这样的
TMyRecord = record
Number: Integer;
Value: Float;
end;
var
Top10: TList<TMyRecord>;
您需要使用Generics.Collections
来获取通用列表。
像这样实例化:
Top10 := TList<TMyRecord>.Create;
使用此功能添加到列表中:
procedure Add(const Item: TMyRecord);
var
i: Integer;
begin
for i := 0 to Top10.Count-1 do
if Item.Value>Top10[i].Value then
begin
Top10.Insert(i, Item);
Top10.Count := Min(10, Top10.Count);
exit;
end;
if Top10.Count<10 then
Top10.Add(Item);
end;
这是插入排序的简单实现。使该算法有效的关键是确保列表始终有序。
答案 1 :(得分:4)
大卫的答案很棒,但我认为随着数据的逐步推进,您将非常快速地填充列表,并且值大于列表中已有值的几率可能随着时间的推移而降低。
因此,为了提高性能,我认为您可以在for循环之前添加此行,以快速丢弃不会进入前10名的值:
if (Item.Value <= Top10[Top10.Count - 1].Value) and (Top10.Count = 10) then
Exit;
如果浮点数总是高于某个阈值,那么使用值低于阈值的10个占位记录来初始化数组可能是有意义的,这样您就可以将第一行更改为:
if (Item.Value <= Top10[9].Value) then
Exit;
并改进方法:
procedure Add(const Item: TMyRecord);
var
i: Integer;
begin
// Throw it out if it's not bigger than our smallest top10
if (Item.Value <= Top10[9].Value) then
Exit;
// Start at the bottom, since it's more likely
for i := 9 downto 1 do
if Item.Value <= Top10[i - 1].Value then
begin
// We found our spot
Top10.Insert(i, Item);
// We're always setting it to 10 now
Top10.Count := 10;
// We're done
Exit;
end;
// Welcome, leader!
Top10.Insert(0, Item);
// We're always setting it to 10 now
Top10.Count := 10;
end;
答案 2 :(得分:2)
由于您使用的是固定数量的项目,因此可以使用普通的TMyRecord
数组,例如:
type
TMyRecord = record
Number: Integer;
Value: Float;
end;
const
MaxRecordsInTopTen = 10;
var
TopTen: array[0..MaxRecordsInTopTen-1] of TMyRecord;
NumRecordsInTopTen: Integer = 0;
procedure CheckValueForTopTen(Value: Float; Number: Integer);
var
I, J, NumToMove: Integer;
begin
// see if the new Value is higher than an value already in the list
for I := 0 to (NumRecordsInTopTen-1) do
begin
if Value > TopTen[I].Value then
begin
// new Value is higher then this value, insert before
// it, moving the following values down a slot, and
// discarding the last value if the list is full
if NumRecordsInTopTen < MaxRecordsInTopTen then
NumToMove := NumRecordsInTopTen - I
else
NumToMove := MaxRecordsInTopTen - I - 1;
for J := 1 to NumToMove do
Move(TopTen[NumRecordsInTopTen-J], TopTen[NumRecordsInTopTen-J-1], SizeOf(TMyRecord));
// insert the new value now
TopTen[I].Number := Number;
TopTen[I].Value := Value;
NumRecordsInTopTen := Min(NumRecordsInTopTen+1, MaxRecordsInTopTen);
// all done
Exit;
end;
end;
// new value is lower then existing values,
// insert at the end of the list if room
if NumRecordsInTopTen < MaxRecordsInTopTen then
begin
TopTen[NumRecordsInTopTen].Number := Number;
TopTen[NumRecordsInTopTen].Value := Value;
Inc(NumRecordsInTopTen);
end;
end;
答案 3 :(得分:1)
除了直接的Object Pascal,我不会打扰任何东西。
{$APPTYPE CONSOLE}
program test2; uses sysutils, windows;
const
MAX_VALUE = $7FFF;
RANDNUMCOUNT = 1000;
var
topten: array[1..10] of Longint;
i, j: integer;
Value: Longint;
begin
randomize;
FillChar(topten, Sizeof(topten), 0);
for i := 1 to RANDNUMCOUNT do
begin
Value := Random(MAX_VALUE);
j := 1;
while j <= 10 do
begin
if Value > topten[j] then
begin
Move(topten[j], topten[j+1], SizeOf(Longint) * (10-j));
topten[j] := Value;
break;
end;
inc(j);
end;
end;
writeln('Top ten numbers generated were: ');
for j := 1 to 10 do
writeln(j:2, ': ', topten[j]);
readln;
end.