我已经查看了互联网,并没有在对象pascal中找到DFS(深度优先搜索)和BFS(广度优先搜索)的任何代码示例,因此我可以在Delphi中实现它们。
对于那些不了解图论的人,click here
由于图形对象的方式,我很难自己开发它。我们之间存在vertices
和edges
,我不知道我是使用records
,objects
还是数组,也不知道如何构建这些数据。如果我有一些预览示例,我可以选择最好的方法而不是从头开始(我相信这个网站就在这里)。
有谁知道从哪里开始?
在我的项目中,我必须在其中插入一个新图形,然后进行DFS搜索以捕获所有边缘并发现最接近高速公路的路径。
答案 0 :(得分:3)
简短的回答是,你可以通过保持一个关联数组将被访问的节点映射到前一个节点和被访问的邻居的联合来实现DFS。
以下是解决方案在表面看起来的样子。我们假设您定义一个节点sic ...
type
INode = interface
['{8D3C78A3-3561-4945-898D-19C5AD4EC35B}']
{$REGION 'property accessors'}
function GetNeighbour( idx: Integer): INode;
function GetNeighbourCount: integer;
{$ENDREGION}
function DisplayName: string;
function Link( const Neighbours: INode): boolean; // True iff succesful.
procedure ShutDown; // Dereference all nodes recursively.
property Neighbours[ idx: Integer]: INode read GetNeighbour;
property NeighbourCount: integer read GetNeighbourCount;
end;
我将INode的实现留给了OP,因为(A)它是微不足道的; (B)因为它具有很高的应用特性。
您将能够遍历节点网络Depth-first,sic ...
procedure DFSTraversal( const Start: INode);
var
X: INode;
begin
for X in TGraph.DepthFirstSearch( Start) do
DoSomething( X);
end;
......借助一些宣言sic ......
INodeEnumerator = interface
['{1A8725EB-AE4B-474C-8052-E35852DCD5FC}']
function GetCurrent: INode;
function MoveNext: Boolean;
procedure Reset;
property Current: INode read GetCurrent;
end;
IEnumerableNode = interface
['{DA11A890-01C4-4FD0-85BB-AE9D65185364}']
function GetEnumerator: INodeEnumerator;
end;
TGraph = class
public
class function DepthFirstSearch( const StartingPoint: INode): IEnumerableNode;
end;
深度优先搜索可以很容易地实现,如前所述,使用关联数组将访问节点映射到前面的节点和访问的邻居。该关联数组封装在TDictionary类型中。以下是如何实施......
type
TBaseEnumerableNode = class abstract( TInterfacedObject, IEnumerableNode)
protected
function GetEnumerator: INodeEnumerator; virtual; abstract;
end;
TDepthFirstSearchEnumerable = class( TBaseEnumerableNode)
private
FRoot: INode;
protected
function GetEnumerator: INodeEnumerator; override;
public
constructor Create( const Root: INode);
end;
TBaseNodeEnumerator = class abstract( TInterfacedObject, INodeEnumerator)
private
function GetCurrent: INode;
procedure Reset;
protected
FCurrent: INode;
function MoveNext: Boolean; virtual; abstract;
end;
RTraversalInfo = record
FCurrIndex: integer;
FPredecessor: INode;
end;
TDepthFirstSearchEnumerator = class ( TBaseNodeEnumerator)
private
FVisitedNodes: TDictionary<INode,RTraversalInfo>;
protected
function MoveNext: Boolean; override;
public
constructor Create( const Root: INode);
destructor Destroy; override;
end;
class function TGraph.DepthFirstSearch(
const StartingPoint: INode): IEnumerableNode;
begin
result := TDepthFirstSearchEnumerable.Create( StartingPoint)
end;
constructor TDepthFirstSearchEnumerable.Create( const Root: INode);
begin
FRoot := Root
end;
function TDepthFirstSearchEnumerable.GetEnumerator: INodeEnumerator;
begin
result := TDepthFirstSearchEnumerator.Create( FRoot)
end;
function TBaseNodeEnumerator.GetCurrent: INode;
begin
result := FCurrent
end;
procedure TBaseNodeEnumerator.Reset;
begin // Not used.
end;
constructor TDepthFirstSearchEnumerator.Create( const Root: INode);
var
TravInfo: RTraversalInfo;
begin
FCurrent := Root;
FVisitedNodes := TDictionary<INode,integer>.Create;
TravInfo.FCurrIndex := -1;
TravInfo.FPredecessor := nil;
FVisitedNodes.Add( FCurrent, TravInfo)
end;
destructor TDepthFirstSearchEnumerator.Destroy;
begin
FVisitedNodes.Free;
inherited
end;
function TDepthFirstSearchEnumerator.MoveNext: boolean;
var
ChildIdx: integer;
LastIdx : integer;
TravInfo: RTraversalInfo;
Next : INode;
Child : INode;
GoDown : boolean;
begin
result := assigned( FCurrent);
if not result then exit;
result := False;
Next := FCurrent;
FCurrent := nil;
repeat
TravInfo := FVisitedNodes[ Next];
ChildIdx := TravInfo.FCurrIndex;
LastIdx := Next.NeighbourCount - 1;
GoDown := ChildIdx <= LastIdx;
if GoDown then
begin
Inc( ChildIdx);
TravInfo.FCurrIndex := ChildIdx;
FVisitedNodes[ Next] := TravInfo;
GoDown := ChildIdx <= LastIdx
end;
if GoDown then
begin
Child := FCurrent.Neighbours[ ChildIdx];
result := not FVisitedNodes.ContainsKey( Child);
if result then
begin
FCurrent := Child;
TravInfo.FPredecessor := Next;
TravInfo.FCurrIndex := -1;
FVisitedNodes.Add( FCurrent, TravInfo)
end
else
Next := Child
end
else
Next := TravInfo.FPredecessor
until result or (not assigned( Next))
end;
答案 1 :(得分:1)
我建议查看DelphiForFun Graph Searching
,您可以在其中找到实施depth first search
(DFS)和breadth first search
(BFS)的示例和教程。
DFS的数据包含在TStringList
后代中,其中节点用文本字符串标识,该文本字符串也用作排序的键。使用二进制搜索算法进行排序。
包含指向adjecent节点(adjecency list
)的指针列表的节点数据作为对象存储在字符串列表中。
从教程中引用DFS算法:
Here is the pseudocode for depth first search:
SearchGoalDF(nodenbr, goalkey, maxdepth) - search depth first for all solutions from nodenbr node to goalkey node with depth of maxdepth or less.
Set visited array to false, visited has an boolean entry for each node.
clear stack
push nodenbr node onto stack
call dfs
end.
dfs
pop (retrieve and delete) most current stack entry, temp.
mark temp as visited. {to avoid looping back here as we search on down}
if temp.key=goalkey then notify caller of solution found
else if stack.count<maxdepth then for each node in temp's adjacency list,
push adjacent[i] onto stack
call dfs
mark temp as unvisited {there might be another path through this node to a solution}
end.
希望这会让您开始并了解如何处理节点数据。
请注意,找到最短路径,BFS算法似乎做得更好:
请参阅Shortest path: DFS, BFS or both?
和Why can't DFS be used to find shortest paths in unweighted graphs?
。