有一组索引为[0,n]的元素。在任何时候,来自A组的大多数m个元素可以是活动的(在使用中),具有0 <= m <= n的明显条件。我想索引本地数组中的那些活动元素。程序执行时可以动态停用元素,我希望在激活新元素时可以重用其索引。
我想以最有效的方式将元素索引映射到它的本地索引,因为我正在使用本地索引来快速查找活动元素数据。
普通散列函数(元素索引==局部索引)和暴力强制通过关联列表的可能解决方案不能很好地扩展到大n。
数据结构的动态扩展/缩小是一个明显的优势。
谢谢
答案 0 :(得分:2)
我想以最有效的方式将元素索引映射到它的本地索引
没有最快的代码(tanstatfc) - Michael Abrash
您的问题有很多解决方案 第一步是选择数据结构和散列函数。
数据结构可以是:
1平原阵列
这是最简单的解决方案。如果您的分配是blobby (这意味着它们在空间中聚集在一起),这甚至可能是一个很好的解决方案。
以下是它的工作原理:
每个条目可以容纳2GB / 4bytes =此结构中有5亿个条目 这最适用于分组在一起的群集中的数据 如果索引是随机的,这将是低效的。
这是Delphi伪代码:
使用虚拟内存的直接列表的示例代码
type
TElement = record
Data: string; //really a pointer to string in Delphi
function PutInList(IndexNr: integer): PElement;
constructor CreateFromList(Index: integer);
end;
PElement = ^TElement;
TElements = array[0..0] of TElement;
PElements = ^TElements;
const
ArraySize = (1024 * 1024 * 1024 * 2); //2GB
BlockSize = 4096;
NumberOfBlocks = (ArraySize) div (BlockSize); //2GB in 4KB blocks
BitsPerInt32 = 32;
IndexCount = NumberOfBlocks div BitsPerInt32;
var
IndexBlock: array[0..IndexCount-1]; //1 bit for each block = 64KB.
var
Elements: PElements;
function ClaimVirtualBlock: PElements;
begin
FillChar(IndexBlock, #0, SizeOf(IndexBlock)); //Zero init indexblock
Result:= PElements(VirtualAlloc(nil, ArraySize, MEM_RESERVE, PAGE_READWRITE));
end;
function GetAddress(Index: integer): PElement; inline;
var
DestAddress: PElement;
BlockIndex: Integer;
BlockDWord: integer;
BlockMask: integer;
BlockNotAllocated: boolean;
begin
//Create a new element in our list
Integer(DestAddress):= Index * SizeOf(TElement);
BlockIndex:= Integer(DestAddress) div 4096;
BlockMask:= 1 shl (BlockIndex mod 32);
BlockIndex:= BlockIndex div 32;
BlockNotAllocated:= (IndexBlock[BlockIndex] and BlockMask) <> 0;
if BlockNotAllocated then begin
IndexBlock[BlockIndex]:= IndexBlock[BlockIndex] or BlockMask;
if VirtualAlloc(DestAddress, BlockSize, MEM_COMMIT, PAGE_READWRITE) = nil then
raise EOutOfMemoryError.Create('Out of physical memory');
end;
Result:= DestAddress;
end;
function TElements.PutInList(IndexNr: integer): PElement;
begin
Result:= GetAddress(IndexNr);
Result^.Data:= Self.Data;
end;
constructor TElements.CreateFromList(Index: integer);
var
ListElement: PElement;
begin
ListElement:= GetAddress(Index);
Self.IndexNr:= Index;
Self.Data:= ListElement.Data;
end;
2包含链表的数组
这对于具有非常随机索引的数据最有效,几乎没有碰撞变化
成功取决于你的哈希函数,它需要尽可能多地选择不同的数组条目,collisions
太多,你只会一直走同一个链表。
链接列表数组的示例代码
type
PElement = ^TElement;
TElement = record
Index: integer;
Data: string;
Next: PElement;
procedure PutInList;
constructor CreateFromList(AIndex: integer);
end;
const
LargePrimeNumber = 100003;
var
StartArray: array[0..LargePrimeNumber-1] of PElement;
procedure InitList;
begin
FillChar(StartArray, #0, SizeOf(StartArray));
end;
function IndexToArrayHash(AnIndex: integer): integer; inline;
begin
Result:= AnIndex mod LargePrimeNumber;
end;
procedure TElement.PutInList;
var
ArrayIndex: integer;
begin
ArrayIndex:= IndexToArrayHash(Self.Index);
Self.Next:= StartArray[ArrayIndex];
StartArray[ArrayIndex]:= @Self;
end;
constructor CreateFromList(AIndex: integer);
var
ArrayIndex: integer;
Element: PElement;
begin
ArrayIndex:= IndexToArrayHash(AIndex);
Element:= StartArray[ArrayIndex];
while (Element <> nil) and (Element.Index <> AIndex) do begin
Element:= Element^.Next;
end; {while}
if (Element <> nil) then begin
Self.Index:= Element^.Index;
Self.Data:= Element^.Data;
Self.Next:= nil;
end;
end;
3二叉树使用索引中的位来遍历树
使用二叉树的示例代码
type
PElement = ^TElement;
TElement = record
Left, Right: PElement;
Index: integer;
Data: string;
procedure PutInList;
end;
function GetFromList(AIndex: integer): PElement;
var
Root: TElement;
const
GoLeft: false;
GoRight: true;
procedure InitRoot;
begin
FillChar(Root, #0, SizeOf(Root));
end;
function TElement.PutInlist;
var
CurrentElement: PElement;
PrevElement:= PElement;
Depth: integer;
Direction: boolean;
begin
PrevElement:= nil;
CurrentElement:= @Root;
Depth:= 0;
//Walk the tree
while (CurrentElement <> nil) and (CurrentElement.Index < Index) do begin
PrevElement:= CurrentElement;
Direction:= Odd(Index shr Depth);
case Direction of
GoLeft: CurrentElement:= CurrentElement^.Right;
GoRight: CurrentElement:= CurrentElement^.Left;
end; {case}
Inc(Depth);
end; {while}
//Insert the item here
case Direction of
GoLeft: PrevItem^.Left:= @Self;
GoRight: PrevItem.Right:= @Self;
end;
//What to do with the currentItem.
if Assigned(CurrentItem) then begin
Direction:= Odd(CurrentItem^.Index shr Depth);
case Direction of
GoLeft: begin
Self.Left:= CurrentItem;
Self.Right:= CurrentItem^.Right;
end; {GoLeft}
GoRight: begin
Self.Right:= CurrentItem;
Left:= CurrentItem^.Left;
end; {GoRight}
end; {case}
end; {if}
end;
function TElement.GetFromList(AIndex: integer): PElement;
var
CurrentElement: PElement;
Depth: integer;
Direction: boolean;
begin
CurrentElement:= @Root;
Depth:= 0;
//Walk the tree
while (CurrentElement <> nil) and (CurrentElement.Index < Index) do begin
Direction:= Odd(Index shr Depth);
case Direction of
GoLeft: CurrentElement:= CurrentElement^.Right;
GoRight: CurrentElement:= CurrentElement^.Left;
end; {case}
Inc(Depth);
end; {while}
if Assigned(CurrentElement) and (CurrentElement^.Index = AIndex) then begin
Result:= CurrentElement;
end
else Result:= nil;
end;
推荐阅读:
Knuth:TAOCP I:基本算法,第2章ISBN 0-201-03809-9
Cormen,Leiserson&amp; Rivest:算法导论,第三章数据结构ISBN 0-07-013143-0