Efficient data structure for GUIDs

后端 未结 3 1245
南笙
南笙 2020-12-30 07:01

I am in search of a data structure which enables me to quickly (prefarably O(1)-quickly) determine if a given GUID is a member of a Collection of GUIDs or not.

My cu

3条回答
  •  囚心锁ツ
    2020-12-30 07:30

    Very few data structures offer O(1) access. One's the Array, the other one's the HashMap (David's answer), and I only know one other: The Trie. Here follows a simple implementation of a bit-wise Trie: Has some interesting properties:

    • Immune to memory fragmentation since no re-allocations take place.
    • O(1) add and existence test. Of course, the constant involved in O(1) is fairly large.

    The code:

    program Project23;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils, Generics.Collections;
    
    type
    
      PGuidTrieNode=^TGuidTrieNode;
      TGuidTrieNode = record
        Sub:array[Boolean] of PGuidTrieNode;
      end;
      TGuidByteArray = array[0..15] of Byte;
    
      TGuidTrie = class
      protected
        Root: PGuidTrieNode;
      public
        constructor Create;
        destructor Destroy;override;
    
        procedure Add(G: TGUID);
        function Exists(G: TGUID): Boolean;
      end;
    
    { TGuidTrie }
    
    procedure TGuidTrie.Add(G: TGUID);
    var GBA: TGuidByteArray absolute G;
        Node: PGuidTrieNode;
        i: Integer;
        Bit: Integer;
        IsBitSet: Boolean;
    const BitMask: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
    begin
      Assert(SizeOf(G) = SizeOf(TGuidByteArray));
      Node := Root;
      for i:=0 to High(GBA) do
      begin
        for Bit := 0 to 7 do
        begin
          IsBitSet := (GBA[i] and BitMask[Bit]) <> 0;
          if (i = High(GBA)) and (Bit = 7) then
            begin
              // Payload
              Node.Sub[IsBitSet] := Pointer(1);
            end
          else
            begin
              if not Assigned(Node.Sub[IsBitSet]) then
                Node.Sub[IsBitSet] := GetMemory(SizeOf(TGuidTrieNode));
              Node := Node.Sub[IsBitSet];
            end;
        end;
      end;
    end;
    
    constructor TGuidTrie.Create;
    begin
      Root := GetMemory(SizeOf(TGuidTrieNode))
    end;
    
    destructor TGuidTrie.Destroy;
    
      procedure KillNode(Node: PGuidTrieNode);
      var i:Integer;
      begin
        if Assigned(Node.Sub[True]) then
            if Node.Sub[True] <> Pointer(1) then
            begin
              KillNode(Node.Sub[True]);
            end;
        FreeMemory(Node);
      end;
    
    begin
      KillNode(Root);
      inherited;
    end;
    
    function TGuidTrie.Exists(G: TGUID): Boolean;
    var GBA: TGuidByteArray absolute G;
        Node: PGuidTrieNode;
        i: Integer;
        Bit: Integer;
        IsBitSet: Boolean;
    const BitMask: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128);
    begin
      Assert(SizeOf(G) = SizeOf(TGuidByteArray));
      Node := Root;
      for i:=0 to 15 do
      begin
        for Bit := 0 to 7 do
        begin
          IsBitSet := (GBA[i] and BitMask[Bit]) <> 0;
          if not Assigned(Node.Sub[IsBitSet]) then
          begin
            Result := False;
            Exit;
          end;
          Node := Node.Sub[IsBitSet];
        end;
      end;
      Result := True; // Node now contains the Payload
    end;
    
    const G1: TGUID = '{68D09F12-3E0D-4963-B32C-4EE3BD90F69C}';
          G2: TGUID = '{BEED37F6-9757-41DC-8463-AF094392652B}';
    
    var T: TGuidTrie;
    
    begin
      try
    
        T := TGuidTrie.Create;
        try
          if T.Exists(G1) then WriteLn('Exists')
                          else WriteLn('NOT Exists');
          T.Add(G1);
          if T.Exists(G1) then WriteLn('Exists')
                          else WriteLn('NOT Exists');
    
          if T.Exists(G2) then WriteLn('Exists')
                          else WriteLn('NOT Exists');
          T.Add(G2);
          if T.Exists(G2) then WriteLn('Exists')
                          else WriteLn('NOT Exists');
        finally T.Free;
        end;
    
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.
    

提交回复
热议问题