Kamis, 22 Januari 2026

Searching Algorithms That I Often Use


As a Computer Science student I have learn a lot of algorithm that not only usable in programming but also in my daily life. For example, I save alot of time when using binary search for searching in ordered objects.

1. Hash Map

Hash Map using integer of hash function result of item's key to map object into bucket array. The object is mapped using modulo of hash value and bucket size as index in the bucket array. And if there is collision the item is added using linked list in the collided index. The item search is doing by calculate hash value of search key and using modulo to find index in the bucket array. And if index in bucket is occupied then continue to compare item's key with search key, if not equal then continue with items in the linked list until the key is equal, if all items is not equal then item is not found.

Using Hash Map is very fast for alot of items and is frequently searched but when new item is added and the bucket need to grow it need to rehash the entire bucket into new bucket.
 
This is an example of my Hash Map classes in Free Pascal: 
  1. type 
  2.   IListIterator = interface
  3.     ['{16585733-6438-4D58-A772-FC6811EB19BB}']
  4.     procedure First;
  5.     function Next: TObject;
  6.     procedure Delete;
  7.   end;
  8.  
  9.   { THashable }
  10.   THashable = class(TObject)
  11.   private
  12.     FNext__: THashable;
  13.   protected
  14.     FHash: Integer;
  15.   public
  16.     constructor Create;
  17.     function IsEqual(AKey: Pointer): Boolean; virtual;
  18.     property Next__: THashable read FNext__;
  19.     property Hash: Integer read FHash;
  20.   end;
  21.  
  22.   { THashMap }
  23.   THashMap = class(TObject)
  24.   private
  25.     FBucket: PObjectArray;
  26.     FCapacity: integer;
  27.     FCount: Integer;
  28.     procedure SetCapacity(NewCapacity: Integer);
  29.   public
  30.     function ObjectByKey(AKey: Pointer): THashable;
  31.   protected
  32.     procedure Grow; virtual;
  33.     function HashKey(AKey: Pointer): Integer; virtual;
  34.     property Capacity: integer read FCapacity write SetCapacity;
  35.     property Count: Integer read FCount;
  36.   public
  37.     destructor Destroy; override;
  38.     procedure Clear;
  39.     function GetObject(AKey: Pointer): THashable;
  40.     procedure Put(AItem: THashable);
  41.     procedure Remove(const AKey: Pointer);
  42.     function GetIterator: IListIterator;
  43.   end;
  44.  
  45.   { THashMapIterator }
  46.   THashMapIterator = class(TInterfacedObject, IListIterator)
  47.   private
  48.     FHashMap: THashMap;
  49.     FIndex: Integer;
  50.     FNode, FBefore: THashable;
  51.     FReread: Boolean;
  52.   public
  53.     constructor Create(AHashMap: THashMap);
  54.     procedure First;
  55.     function Next: TObject;
  56.     procedure Delete;
  57.   end;
  58.  
  59. { THashable }
  60. constructor THashable.Create;
  61. begin
  62.   FHash := 0;
  63.   FNext__ := nil;
  64. end;
  65.  
  66. function THashable.IsEqual(AKey: Pointer): Boolean;
  67. begin
  68.   Result := False;
  69. end;
  70.  
  71. { THashMap }
  72. procedure THashMap.Clear;
  73. var
  74.   I: integer;
  75. begin
  76.   if (FBucket <> nil) then
  77.   begin
  78.     for I := 0 to FCapacity - 1 do
  79.     begin
  80.       if (FBucket^[I] <> nil) then FreeAndNil(FBucket^[I]);
  81.     end;
  82.     FreeMem(FBucket);
  83.     FBucket := nil;
  84.   end;
  85.   FCapacity := 0;
  86.   FCount := 0;
  87. end;
  88.  
  89. destructor THashMap.Destroy;
  90. begin
  91.   Clear;
  92.   inherited;
  93. end;
  94.  
  95. function THashMap.GetObject(AKey: Pointer): THashable;
  96. var
  97.   P: THashable;
  98.   H: integer;
  99. begin
  100.   if (FCount > 0) then
  101.   begin
  102.     H := HashKey(AKey);
  103.     P := THashable(FBucket^[H mod FCapacity]);
  104.     while ((P <> nil) and ((P.Hash <> H) or not P.IsEqual(AKey))) do
  105.       P := P.FNext__;
  106.     Result := P;
  107.   end
  108.   else
  109.     Result := nil;
  110. end;
  111.  
  112. procedure THashMap.Grow;
  113. var
  114.   Delta: Integer;
  115. begin
  116.   if FCapacity > 64 then
  117.     Delta := FCapacity div 4
  118.   else if FCapacity > 8 then
  119.     Delta := 16
  120.   else
  121.     Delta := 4;
  122.   SetCapacity(FCapacity + Delta);
  123. end;
  124.  
  125. function THashMap.HashKey(AKey: Pointer): Integer;
  126. begin
  127.   Result := Integer(AKey);
  128. end;
  129.  
  130. function THashMap.ObjectByKey(AKey: Pointer): THashable;
  131. begin
  132.   Result := GetObject(AKey);
  133.   if (Result = nil) then
  134.     raise EListError.CreateFmt(SListItemNotFoundError,[IntToHex(Integer(AKey),8)]);
  135. end;
  136.  
  137. procedure THashMap.Put(AItem: THashable);
  138. var
  139.   I: integer;
  140. begin
  141.   if (AItem = nil) then exit;
  142.   Inc(FCount);
  143.   if ((FCount * 4) div 3 > FCapacity) then Grow;
  144.   I := AItem.Hash mod FCapacity;
  145.   AItem.FNext__ := THashable(FBucket^[I]);
  146.   FBucket^[I] := AItem;
  147. end;
  148.  
  149. procedure THashMap.Remove(const AKey: Pointer);
  150. var
  151.   P, Q: THashable;
  152.   I, H: Integer;
  153. begin
  154.   if (FCount > 0) then
  155.   begin
  156.     H := HashKey(AKey);
  157.     I := H mod FCapacity;
  158.     P := THashable(FBucket^[I]);
  159.     Q := nil;
  160.     while ((P <> nil) and (P.Hash <> H) and not P.IsEqual(AKey)) do
  161.     begin
  162.       Q := P;
  163.       P := P.FNext__;
  164.     end;
  165.     if (P <> nil) then
  166.     begin
  167.       if (Q = nil) then
  168.         FBucket^[I] := P.FNext__
  169.       else
  170.         Q.FNext__ := P.FNext__;
  171.       P.Free;
  172.       Dec(FCount);
  173.     end;
  174.   end;
  175. end;
  176.  
  177. function THashMap.GetIterator: IListIterator;
  178. begin
  179.   Result := THashMapIterator.Create(Self);
  180. end;
  181.  
  182. procedure THashMap.SetCapacity(NewCapacity: Integer);
  183. var
  184.   P, Q: THashable;
  185.   NewList: PObjectArray;
  186.   I, J: Integer;
  187. begin
  188.   if ((NewCapacity = FCapacity) or (NewCapacity < FCount)) then exit;
  189.   if (NewCapacity > 0) then
  190.   begin
  191.     GetMem(NewList,NewCapacity*SizeOf(TObject));
  192.     FillChar(NewList^,NewCapacity*SizeOf(TObject),0);
  193.     for I := 0 to FCapacity - 1 do
  194.     begin
  195.       P := THashable(FBucket^[I]);
  196.       while (P <> nil) do
  197.       begin
  198.         Q := P;
  199.         P := P.FNext__;
  200.         J := Q.Hash mod NewCapacity;
  201.         Q.FNext__ := THashable(NewList^[J]);
  202.         NewList^[J] := Q;
  203.       end;
  204.     end;
  205.   end
  206.   else
  207.     NewList := nil;
  208.   if (FBucket <> nil) then FreeMem(FBucket);
  209.   FBucket := NewList;
  210.   FCapacity := NewCapacity;
  211. end;
  212.  
  213. { THashMapIterator }
  214. constructor THashMapIterator.Create(AHashMap: THashMap);
  215. begin
  216.   FHashMap := AHashMap;
  217.   FIndex := -1;
  218.   FNode := nil;
  219.   FBefore := nil;
  220.   FReread := False;
  221. end;
  222.  
  223. procedure THashMapIterator.First;
  224. begin
  225.   FIndex := -1;
  226.   FNode := nil;
  227.   FBefore := nil;
  228.   FReread := False;
  229. end;
  230.  
  231. function THashMapIterator.Next: TObject;
  232. begin
  233.   if (FReread) then
  234.   begin
  235.     FReread := False;
  236.   end
  237.   else begin
  238.     if (FNode <> nil) then
  239.     begin
  240.       FBefore := FNode;
  241.       FNode := FNode.FNext__;
  242.     end;
  243.     if (FNode = nil) then
  244.     begin
  245.       Inc(FIndex);
  246.       while ((FIndex < FHashMap.Capacity) and (FHashMap.FBucket^[FIndex] = nil)) do
  247.         Inc(FIndex);
  248.       if (FIndex < FHashMap.Capacity) then
  249.         FNode := THashable(FHashMap.FBucket^[FIndex])
  250.       else
  251.         FNode := nil;
  252.       FBefore := nil;
  253.     end;
  254.   end;
  255.   Result := FNode;
  256. end;
  257.  
  258. procedure THashMapIterator.Delete;
  259. var
  260.   P: THashable;
  261. begin
  262.   if (FNode <> nil) then
  263.   begin
  264.     P := FNode;
  265.     if (FBefore <> nil) then
  266.       FBefore.FNext__ := P.FNext__
  267.     else
  268.       FHashMap.FBucket^[FIndex] := P.FNext__;
  269.     FNode := FNode.FNext__;
  270.     if (FNode = nil) then
  271.     begin
  272.       Inc(FIndex);
  273.       while ((FIndex < FHashMap.Capacity) and (FHashMap.FBucket^[FIndex] = nil)) do
  274.         Inc(FIndex);
  275.       if (FIndex < FHashMap.Capacity) then
  276.         FNode := THashable(FHashMap.FBucket^[FIndex])
  277.       else
  278.         FNode := nil;
  279.       FBefore := nil;
  280.     end;
  281.     FReread := True;
  282.     P.Free;
  283.   end;
  284. end;
  285.  
  286. { TNamedObject }
  287. function TNamedObject.GetName: String;
  288. begin
  289.   Result := FName;
  290. end;
  291.  
  292. procedure TNamedObject.SetName(const Value: string);
  293. begin
  294.   FName := Value;
  295.   FHash := HashName(Value);
  296. end;
  297.  
  298. function TNamedObject.IsEqual(AKey: Pointer): Boolean;
  299. begin
  300.   Result := SameText(FName, String(AKey));
  301. end;
 
This is an example of Hash function used to map object using case insensitive name: 
 
  1. function HashName(const AName: string): integer;
  2. var
  3.   I: integer;
  4.   C: Byte;
  5. begin
  6.   Result := 0;
  7.   for I := 1 to Length(AName) do
  8.   begin
  9.     C := Byte(AName[I]);
  10.     if ((C >= Ord('a')) and (C <= Ord('z'))) then Dec(C,Ord('a')-Ord('A'));
  11.     Result := ((Result SHL 5) OR (Result AND $1F)) + C;
  12.   end;
  13.   Result := Result AND $7FFFFFFF;
  14. end;
This an example of using the Hash Map class to map objects by case insensitive name:
  1. type  
  2.   { TNamedObject }
  3.   TNamedObject = class(THashable)
  4.   private
  5.     FName: string;
  6.   protected
  7.     function GetName: String;
  8.     procedure SetName(const Value: string); virtual;
  9.   public
  10.     function IsEqual(AKey: Pointer): Boolean; override;
  11.     property Name: string read GetName write SetName;
  12.   end; 
  13.  
  14.   { TNamedObjectMap }
  15.   TNamedObjectMap = class(THashMap)
  16.   protected
  17.     function HashKey(AKey: Pointer): Integer; override;
  18.   public
  19.     function GetObject(const AName: String): TNamedObject;
  20.     procedure Remove(const AName: string);
  21.   end;
  22.  
  23.  { TNamedObject }
  24. function TNamedObject.GetName: String;
  25. begin
  26.   Result := FName;
  27. end;
  28.  
  29. procedure TNamedObject.SetName(const Value: string);
  30. begin
  31.   FName := Value;
  32.   FHash := HashName(Value);
  33. end;
  34.  
  35. function TNamedObject.IsEqual(AKey: Pointer): Boolean;
  36. begin
  37.   Result := SameText(FName, String(AKey));
  38. end;
  39.  
  40. { TNamedObjectMap }
  41. function TNamedObjectMap.HashKey(AKey: Pointer): Integer;
  42. begin
  43.   Result := HashName(String(AKey));
  44. end;
  45.  
  46. function TNamedObjectMap.GetObject(const AName: String): TNamedObject;
  47. begin
  48.   Result := TNamedObject(inherited GetObject(Pointer(AName)));
  49. end;
  50.  
  51. procedure TNamedObjectMap.Remove(const AName: string);
  52. begin
  53.   inherited Remove(Pointer(AName));
  54. end;

This is an example of name and row index search helper using Hash Map:

  1. type  
  2.   { TNameIndexItem }
  3.   TNameIndexItem = class(THashable)
  4.   private
  5.     FKey: String;
  6.     FRow: Integer;
  7.     FData: Pointer;
  8.   public
  9.     constructor Create(const AKey: String; ARow: Integer; AData: Pointer=nil);
  10.     function IsEqual(AKey: Pointer): Boolean; override;
  11.     property Key: String read FKey;
  12.     property Row: Integer read FRow;
  13.     property Data: Pointer read FData;
  14.   end;
  15.  
  16.   { TNameIndex }
  17.   TNameIndex = class(THashMap)
  18.   protected
  19.     function HashKey(AKey: Pointer): Integer; override;
  20.   public
  21.     function GetObject(const AKey: String): TNameIndexItem;
  22.     procedure Remove(const AKey: String);
  23.     procedure PutRow(const AKey: String; ARow: Integer; AData: Pointer=nil);
  24.     function FindRow(const AKey: String): Integer;
  25.     function GetRowAndData(const AKey: String; var ARow: Integer;
  26.       var AData: Pointer): Boolean;
  27. //    procedure IndexTable(ATable: ITransportTable; const AColName: String);
  28.   end;
  29.  
  30. { TNameIndexItem }
  31. constructor TNameIndexItem.Create(const AKey: String; ARow: Integer;
  32.   AData: Pointer);
  33. begin
  34.   FKey := AKey;
  35.   FHash := HashName(AKey);
  36.   FRow := ARow;
  37.   FData := AData;
  38. end;
  39.  
  40. function TNameIndexItem.IsEqual(AKey: Pointer): Boolean;
  41. begin
  42.   Result := SameText(FKey, String(AKey));
  43. end;
  44.  
  45. { TNameIndex }
  46. function TNameIndex.FindRow(const AKey: String): Integer;
  47. var
  48.   P: TNameIndexItem;
  49. begin
  50.   P := GetObject(AKey);
  51.   if (P <> nil) then
  52.     Result := P.Row
  53.   else
  54.     Result := -1;
  55. end;
  56.  
  57. function TNameIndex.GetRowAndData(const AKey: String; var ARow: Integer;
  58.   var AData: Pointer): Boolean;
  59. var
  60.   P: TNameIndexItem;
  61. begin
  62.   P := GetObject(AKey);
  63.   if (P <> nil) then
  64.   begin
  65.     ARow := P.Row;
  66.     AData := P.Data;
  67.     Result := True;
  68.   end
  69.   else
  70.     Result := False;
  71. end;
  72.  
  73. function TNameIndex.GetObject(const AKey: String): TNameIndexItem;
  74. begin
  75.   Result := TNameIndexItem(inherited GetObject(Pointer(AKey)));
  76. end;
  77.  
  78. function TNameIndex.HashKey(AKey: Pointer): Integer;
  79. begin
  80.   Result := HashName(String(AKey));
  81. end;
  82.  
  83. //procedure TNameIndex.IndexTable(ATable: ITransportTable;
  84. //  const AColName: String);
  85. //var
  86. //  i, col: Integer;
  87. //begin
  88. //  if (ATable = nil) then
  89. //    exit;
  90. //  col := ATable.FindField(AColName);
  91. //  if (col < 0) then
  92. //    exit;
  93. //  Clear;
  94. //  for i := 0 to ATable.RowCount - 1 do
  95. //    PutRow(ATable.Cells[col,i].AsString, i);
  96. //end;
  97.  
  98. procedure TNameIndex.PutRow(const AKey: String; ARow: Integer;
  99.   AData: Pointer);
  100. begin
  101.   if (GetObject(AKey) = nil) then
  102.   begin
  103.     Put(TNameIndexItem.Create(AKey, ARow, AData));
  104.   end;
  105. end;
  106.  
  107. procedure TNameIndex.Remove(const AKey: String);
  108. begin
  109.   inherited Remove(Pointer(AKey));
  110. end;

2. Hash Marking

Hash marking using integer of hash function result of item's key to mark list items. Because comparing an integer only need 1 cpu clock and comparing text need 1 cpu clock per character we can skip comparing entire key by first comparing only hash values and only if it is equal then we continue to compare the key.

This method is very effective if item's key is a string or large complex type such as struct and number of items is limited or frequently recreated. For very alot of items and is frequently searched using Hash Map is faster.

This is an example of using hash marking to search for an object in a linked list:
  1. function TChain.GetObject(AKey: Pointer): THashable;
  2. var
  3.   P: THashable;
  4.   H: Integer;
  5. begin
  6.   P := FFirst;
  7.   H := HashKey(AKey);
  8.   while ((P <> nil) and ((P.Hash <> H) or not P.IsEqual(AKey))) do
  9.     P := P.FNext__;
  10.   Result := P;
  11. end;

3. Binary Search

Binary Search work with ordered list by repeatly search the middle first and if not the equal then search the half part of the list that is possibly still cotains the items until the item is found or range start > range end.

This is an example of searching data using binary search:
  1. function TIntegerList.IndexOfB(AItem: Integer): Integer;
  2. var
  3.   Low,High,Mid: Integer;
  4. begin
  5.   Result := -1;
  6.   Low := 0;
  7.   High := Count - 1;
  8.   Mid := (Low + High) div 2;
  9.   while (Low <= High) do
  10.   begin
  11.     if (Items[Mid] > AItem) then High := Mid - 1
  12.     else if (Items[Mid] < AItem) then Low := Mid + 1
  13.     else begin
  14.       Result := Mid;
  15.       exit;
  16.     end;
  17.     Mid := (Low + High) div 2;
  18.   end;
  19. end;

Building New PC To Study LLM

Since early 2025, I've been using AI LLMs like ChatGPT or DeepSeek to learn programming or help with research. Using AI LLMs is easier b...