我有一個動態分配的整數數組,我想將整數插入到任意位置。在250萬以上的許多整數。如何有效地處理多個插入數組?
我的代碼目前看起來是這樣的:
type
TIntegerArr = array of Integer;
var
FCount: Integer;
FSortedList: TIntegerArr;
procedure Insert(_Value: Integer; _InsertPos: integer);
var
OldList: TIntegerArr;
begin
OldList := FSortedList;
if Length(FSortedList) < FCount + 1 then begin
OldList := FSortedList;
FSortedList := nil;
SetLength(FSortedList, FCount + 100);
CopyMemory(@FSortedList[0], @OldList[0], SizeOf(Integer) * _InsertPos);
end;
MoveMemory(@FSortedList[_InsertPos + 1], @OldList[_InsertPos], SizeOf(Integer) * (FCount - _InsertPos));
FSortedList[_InsertPos] := _Value;
Inc(FCount);
end;
(真正的代碼是具有FSortedList和FCOUNT作爲字段一個類的方法。)
使用的臨時列表,並使用移動而不是用於移動數據的for循環改進了性能已經相當多,因爲它防止數組在需要增長時被複制兩次(一次在現有陣列上的SetLength中,另一次在Move中)。
但最壞的情況插入(SomeValue,0)仍然會移動所有現有的值。
到目前爲止,我正在考慮在數組的開始處引入一個偏移量,而不是每次在前面插入一個新值都要移動所有現有值,我只能在偏移量達到0。例如:
// simple case: inserting at Position 0:
if FOffset = 0 then begin
// [...] reallocate a new array as above
Move(@FSortedList[100], @OldList, SizeOf(Integer) * _InsertPos);
FOffset := 100;
end;
Dec(FOffset);
FSortedList[FOffset] := _NewValue;
(該代碼是未經測試,可能車) 這當然可以擴展到檢查插入點是否是接近的開始或結束,並根據該移動或者第一或最後一個值的位置,因此平均而言,只有1/4的條目必須移動,而不是像現在這樣1/2。
另一種選擇是實現一個稀疏數組。我記得在1990年代的一些商業圖書館看到了這樣的實現,但不記得它是什麼(TurboPower?)。
此過程對於一些排序和索引代碼至關重要,該代碼適用於從幾十個條目到上述數百萬條目的不同大小的數組。
目前該程序運行約2小時(在我的優化之前它接近5個小時),並且我已經知道陣列中的條目數至少會增加一倍。隨着插入性能越差,陣列已經越大,我懷疑如果條目數增加一倍,運行時間至少會增加四倍。
我想就如何調整性能提出一些建議。內存消耗目前不是什麼大問題,但運行時間肯定是。
(這是德爾福2007年,但不應該有太大的差異,除非較新版本的Delphi已經做上述優化的庫Classes.TList不是最優化的。)
EDIT1:剛發現稀疏我上面提到的數組實現:它是TurboPower SysTools的StColl。
編輯2:好吧,一些背景:我的程序讀取一個包含當前240萬個條目的DBase表,並從這些條目生成幾個新表。新表格已經過規範化並在創建之後進行索引(出於性能方面的考慮,我在插入數據之前不會生成索引,請相信我,我先試了一下)。該數組是代碼的核心部分,爲生成的表提供內部排序。新記錄僅附加到表中,但它們的RecNo按排序順序插入到數組中。
請參閱['@ Runner'](http://「改進的切片陣列實現'](http://www.cromis.net/blog/2013/03/improved-sliced-array-implementation/) stackoverflow.com/users/118765/runner)如果這給了任何輸入如何使排序更好。 –
@LURD:謝謝。當他寫這篇博客文章時,我閱讀了這篇博文(該頁面的第一條評論是我的),但已經被遺忘了。 – dummzeuch
告訴我們更多關於這個「可插入數組」的用例。可能的解決方案取決於他們 – MBo