dfs深度优先搜索

Depth First Search (DFS) is an algorithm that searches a graph/tree, in a depth-wise manner.

深度优先搜索DFS )是一种以深度方式搜索图/树的算法。

There are many ways through which we can traverse through a graph.

我们可以通过多种方式遍历图形。

The two most common methods that we can use are:

我们可以使用的两种最常见的方法是:

  • Breadth-First Search (BFS)广度优先搜索(BFS)
  • Depth-First Search (DFS)深度优先搜索(DFS)

Both of these methods are recursive in nature.

这两种方法本质上都是递归的。

The only difference is that a BFS first searches the breadth of the graph/tree, while a DFS searches from top to bottom first, before branching out.

唯一的区别是, BFS首先搜索图/树的宽度,而DFS首先从顶部到底部搜索,然后再进行分支。

Also, a Depth First Search will tell us if two nodes are reachable or not. If the algorithm terminates and we still haven’t found our answer, that means that the two nodes are not connected!

同样,深度优先搜索将告诉我们两个节点是否可达。 如果算法终止,但我们仍未找到答案,则意味着两个节点未连接!

In this article, let us take a look at the DFS Algorithm in detail.

在本文中,让我们详细了解DFS算法。

I will also show you an implementation of this algorithm in C so that you can get a programmer’s perspective on how you can write this algorithm.

我还将向您展示该算法在C语言中的实现,以便您可以从程序员的角度了解如何编写此算法。



深度优先搜索算法 (The Depth First Search Algorithm)

As I mentioned earlier, the depth-first search algorithm is recursive in nature.

如前所述,深度优先搜索算法本质上是递归的。

So, if you want to look for an element in the graph, the DFS procedure will first go as deep as possible from the current node, until you cannot go any further.

因此,如果要在图中查找元素,则DFS过程将首先从当前节点开始尽可能深入,直到无法继续进行为止。

When you hit a dead end, you simply move back and try to find deeper routes from any of those nodes.

当您走到尽头时,您只需退后并尝试从这些节点中的任何一个中查找更深的路线。

You simply keep trying all these ‘deepest’ routes until you have exhausted all possibilities.

您只需继续尝试所有这些“最深”的路线,直到用尽所有可能性。

To apply this algorithm, we need to keep track of the path ‘history‘, that includes the current node visited, so that we can come back to that point.

要应用此算法,我们需要跟踪路径“ history ”,其中包括访问的当前节点,以便我们可以回到这一点。

Since this will try different paths from the path of visited nodes, it is very natural to use a Stack Data Structure.

由于这将尝试与访问节点的路径不同的路径,因此使用堆栈数据结构是很自然的。

To simplify things, I will show you how this algorithm works on a graph.

为简化起见,我将向您展示该算法如何在图形上工作。

算法 (The Algorithm)

Before we start our algorithm, let us have a stack of the nodes in the current path. Initially, it is empty.

在开始算法之前,让我们在当前路径中有一堆节点。 最初,它是空的。

  • If a node to be visited is not there in the stack, we push it onto the stack and mark the node as visited.如果要访问的节点不在堆栈中,则将其推入堆栈,然后将该节点标记为已访问。
  • We then check if the current node matches our search criteria.然后,我们检查当前节点是否符合搜索条件。
  • If it does, we are done!如果是这样,我们就完成了!
  • Otherwise, we need to go to all the nodes adjacent to the current node.否则,我们需要转到与当前节点相邻的所有节点。
  • We will visit all such nodes, in any random order, and keep searching.我们将以任何随机顺序访问所有此类节点,并继续搜索。
  • If all adjacent nodes are already visited, it is a dead end. We must go to the previously visited node and pop the recent node from the stack.如果所有相邻节点都已被访问,那将是一个死胡同。 我们必须转到先前访问的节点并从堆栈中弹出最近的节点。
  • The algorithm will terminate if all the nodes have been searched (stack is empty), or if we get our answer.如果已经搜索了所有节点(堆栈为空),或者得到了答案,该算法将终止。

So this algorithm will search the whole graph and is thus a good way to check if a path exists between two nodes.

因此,该算法将搜索整个图,因此是检查两个节点之间是否存在路径的好方法。

一个例子–搜索顶点 (An Example – Searching for a vertex)

Let’s search for the vertex 7 in the graph below, using DFS.

让我们使用DFS在下图中搜索顶点7

When we begin at the start node (6), we mark it as visited, and add it to the stack.

当我们从起始节点( 6 )开始时,我们将其标记为已访问,并将其添加到堆栈中。

Dfs Step One
DFS第一步

Since this is not the node we want, we will visit all its unvisited adjacent nodes, and keep going until we hit a dead end.

由于这不是我们想要的节点,因此我们将访问其所有未访问的相邻节点,并继续进行直到遇到死角。

Since the order doesn’t matter to us, let’s visit vertex 2 next. (We can also choose vertex 5)

由于顺序对我们而言无关紧要,因此接下来让我们访问顶点2 。 (我们也可以选择顶点5

Dfs Step Two
DFS第二步

Now, again, since it is not the destination vertex, let’s again visit all unvisited adjacent nodes of 2. Let’s choose 14.

现在,再次,因为它不是目标顶点,让我们再次访问的所有2 未访问的相邻节点。 让我们选择14

Dfs Step Three
DFS第三步

Again, this is not the destination node. But now, this is a dead-end, since it does not have any adjacent node.

同样,这不是目标节点。 但是现在,这是一个死胡同,因为它没有任何相邻节点。

So, we must go back a step, and pop 14 from the stack. We are now at vertex 2 again.

因此,我们必须退后一步,并从堆栈中弹出14 。 我们现在再次处于顶点2

Dfs Step Four
DFS第四步

Now, we move onto the node 7. Since this is the destination node, we are done!

现在,我们进入节点7 。 由于这是目标节点,因此我们完成了!

Dfs Last Step
DFS最后一步

Hopefully, you can now understand how DFS works. Let’s now move onto a C implementation!

希望您现在可以了解DFS的工作原理。 现在让我们进入C实现!



深度优先搜索的实现 (Implementation of Depth First Search)

We will use the code from our previous article on Graph Theory as a template, and build on from that.

我们将使用上一篇有关图论的文章中的代码作为模板,并以此为模板。

I’ll be adding to this code. So let’s first quickly implement a stack using Linked Lists.

我将添加到此代码中。 因此,让我们首先使用链接列表快速实现堆栈。


#include <stdio.h>
#include <stdlib.h>
#include <limits.h>typedef struct Graph Graph;typedef struct Node Node;struct Node {// To represent the linked list node.// Contains the vertex indexint vertex;// Vertex Keyint key;// And a pointer to the next element in the linked listNode* next;
};struct Graph {// Key Listint* key_list;// Number of verticesint v;// Array of Adjacency ListsNode** adj_lists;
};// Define the Stack here
typedef struct StackNode StackNode;struct StackNode{// Stack of integersint data;StackNode* next;
};int is_empty(StackNode* stack) {// Check if stack is emptyif (!stack)return 1;return 0;
}StackNode* push(StackNode* stack, int data) {// Pushes the data into the stackStackNode* node = (StackNode*) malloc (sizeof(StackNode));StackNode* temp = stack;node->data = data;node->next = temp;stack = node;return stack;
}StackNode* pop(StackNode* stack) {// Pops the head of the stackif (!stack)return NULL;StackNode* temp = stack;stack = stack->next;temp->next = NULL;free(temp);return stack;
}int top(StackNode* stack) {// Return the top of the stackif (!stack)return INT_MIN;return stack->data;
}StackNode* init_stack(int data) {// Initializes the stackStackNode* stack = (StackNode*) malloc (sizeof(StackNode));stack->data = data;stack->next = NULL;return stack;
}void free_stack(StackNode* stack) {// Free the stackif (!stack)return;StackNode* temp = stack;stack = stack->next;temp->next = NULL;free(temp);free_stack(stack);
}void print_stack(StackNode* stack) {if (!stack)return;StackNode* temp = stack;printf("Stack: \n");while(temp) {printf("Data: %d -> ", temp->data);temp = temp->next;}printf("\n");
}

We’ll now show the code for building the graph. Since we denote each vertex using an index as well as its key value, the Node structure looks like this.

现在,我们将显示用于构建图形的代码。 由于我们使用索引及其键值来表示每个顶点,因此Node结构看起来像这样。


struct Node {// To represent the linked list node.// Contains the vertex indexint vertex;// Vertex Keyint key;// And a pointer to the next element in the linked listNode* next;
};

The rest of the code is almost the same as that of the previous article, except that we’ll also be adding in the key for every vertex.

其余代码与上一篇文章几乎相同,除了我们还将为每个顶点添加键。


// Adjacency Lists for the Graph
Node** init_adjacency_lists(Graph g) {// Initializes an adjacency matrix for the graphif (g.adj_lists)return g.adj_lists;// Allocates memory for the lists// There is a list for every vertex in the graph// which means there are g.v adjacent listsNode** adj_lists = (Node**) calloc (g.v,  sizeof(Node*));// Set them to NULL initiallyfor (int i = 0; i < g.v; i++)adj_lists[i] = NULL;printf("Initialized Adjacency Lists!\n");return adj_lists;
}void free_list(Node* list) {// Frees all nodes in the list, headed by 'list'Node* temp = list;while(temp) {Node* rm_node = temp;temp = rm_node->next;rm_node->next = NULL;free(rm_node);}
}void free_adj_lists(Graph g) {// Free the adjacency matrixif (!g.adj_lists)return;for (int i=0; i<g.v; i++)free_list(g.adj_lists[i]);free(g.adj_lists);
}void print_list(Node* list) {// Prints the linked listNode* temp = list;while(temp) {printf("Node: %d, Key: %d -> ", temp->vertex, temp->key);temp = temp->next;}printf("\n");
}Node* create_node(int vertex, int key) {// Creates a LinkedList node to hold the vertexNode* node = (Node*) calloc (1, sizeof(Node));node->next = NULL;node->vertex = vertex;node->key = key;return node;
}void add_edge(Graph g, int i, int j) {// Adds an edge connecting two vertices i and jif (!g.adj_lists) {fprintf(stderr, "Adjacency Lists not initialized!\n");exit(1);}else if (i > g.v || j > g.v) {fprintf(stderr, "Invalid Vertex Number\n");exit(1);}// Create the new node in the souce vertex// adjacency list and add the destination// vertex to it// Create a node containing the dst vertex indexNode* node = create_node(j, g.key_list[j-1]);// Insert at the source list// Let's insert at the top, since it doesn't// matter whether we insert at the head or notnode->next = g.adj_lists[i-1];// Make the new node as the headg.adj_lists[i-1] = node;
}void remove_edge(Graph g, int i, int j) {// Sets the edge from i to j as zeroif (!g.adj_lists) {fprintf(stderr, "Adjacency Lists not initialized!\n");exit(1);}// Search for vertex j in i's adjacency listNode* temp = g.adj_lists[i-1];if (!temp) {return;}if (!(temp->next)) {if (temp->vertex == j) {free(temp);g.adj_lists[i-1] = NULL;}return;}while (temp->next) {if (temp->vertex == j) {// We have found an edge! Remove this element.Node* rm_node = temp;temp->next = rm_node->next;rm_node->next = NULL;free(rm_node);return;}temp = temp->next;}// No element found :(return;
}int check_if_exists(Graph g, int i, int j) {// Checks if there is an edge from vertex i to jif (!g.adj_lists) {fprintf(stderr, "Adjacency Lists not initialized!\n");exit(1);}else if (i > g.v || j > g.v) {fprintf(stderr, "Invalid Vertex Number\n");return 0;}// Search for vertex j in i's adjacency listNode* temp = g.adj_lists[i-1];if (!temp) {return 0;}if (!(temp->next)) {if (temp->vertex == j) {return 1;}return 0;}while (temp->next) {if (temp->vertex == j) {// We have found an edge! Remove this element.return 1;}temp = temp->next;}// No element found :(return 0;
}

Let’s now move onto the main part: the DFS() function.

现在让我们进入主要部分: DFS()函数。

DFS()函数 (The DFS() Functions)

Now, it is often common practice to write a recursive function within another wrapper function. So our core DFS algorithm will be wrapped around a function called DFS(), which calls it under the hood.

现在,通常的惯例是在另一个包装函数中编写递归函数。 因此,我们的核心DFS算法将被一个名为DFS()的函数包装,该函数在后台进行调用。

This will check whether a path exists from the start_vertex to our destination vertex, specified by it’s key.

这将检查从start_vertex到我们的目标顶点的路径( start_vertex key指定)是否存在。

So it’s signature is :

所以它的签名是:


int DFS(Graph g, int start_vertex, int key);

We’ll keep track of the visited nodes using an array of integers int* visited_list. Initially, this is set to Zero.

我们将使用整数数组int* visited_list跟踪访问的节点。 最初,将其设置为零。


// g.v -> Number of Vertices
// This is initialized to zero using calloc()
int* visited_list = (int*) calloc (g.v, sizeof(int));

We also need to set up the stack for the DFS Algorithm.

我们还需要为DFS算法设置堆栈。


// Initialize a stack with the key of the starting vertex
StackNode* stack = init_stack(g.key_list[start_vertex-1]);

Now, we are ready to call the main algorithm function DFS_recursive().

现在,我们准备调用主要算法函数DFS_recursive()


int ret_val = DFS_recursive(g, start_vertex, key, &stack, visited_list, 0);

This takes all the required parameters above. We also have one more parameter called start, which will push to the stack only if it is called recursively.

这将接受上面所有必需的参数。 我们还有一个名为start参数,仅当递归调用该参数start ,该参数才会压入堆栈。

NOTE: We pass the stack address to DFS_recursive(), since otherwise, it will get overwritten, since it is simply a variable. Passing it as a pointer will preserve the stack during such recursive calls.

注意 :我们将stack地址传递给DFS_recursive() ,否则将被覆盖,因为它只是一个变量。 将其作为指针传递将在此类递归调用期间保留stack


int DFS(Graph g, int start_vertex, int key) {// Performs a DFS on the Graph from start_vertex// and returns 1 if the destination key is foundprintf("Start Vertex: %d, Key: %d\n", start_vertex, g.key_list[start_vertex - 1]);if (g.key_list[start_vertex-1] == key) {return 1;}// Keep a visited list of nodesint* visited_list = (int*) calloc (g.v, sizeof(int));// Perform the DFSStackNode* stack = init_stack(g.key_list[start_vertex-1]);int ret_val = DFS_recursive(g, start_vertex, key, &stack, visited_list, 0);// Free Stuff and exitfree_stack(stack);free(visited_list);return ret_val;
}

Now, let’s go to the DFS_recursive() function.

现在,让我们转到DFS_recursive()函数。

This pushes the current node to the stack and marks it as visited.

这会将当前节点压入堆栈并将其标记为已访问。


if (start)*stack = push(*stack, g.key_list[start_vertex-1]);printf("Current Node: key: %d\n", g.key_list[start_vertex - 1]);
// Mark the current node as visited
visited_list[start_vertex - 1] = 1;

Now, we use the exact same algorithm that we mentioned above, in the example graph.

现在,我们在示例图中使用与上面提到的算法完全相同的算法。


int DFS_recursive(Graph g, int start_vertex, int key, StackNode** stack, int* visited_list, int start) {// Recursive DFS function that is used to perform DFS()if (start)*stack = push(*stack, g.key_list[start_vertex-1]);printf("Current Node: key: %d\n", g.key_list[start_vertex - 1]);// Mark the current node as visitedvisited_list[start_vertex - 1] = 1;// While the stack is not emptywhile (!is_empty(*stack)) {if (g.key_list[start_vertex - 1] == key) {return 1;}else {// Not found. Go to the next nodeNode* node = g.adj_lists[start_vertex - 1];if (!node) {// Dead End. Go back after popping current node*stack = pop(*stack);}while (node) {// If any of it's neighbours are not visitedif(visited_list[node->vertex - 1] == 0) {// Go to that node and do a DFS from that nodeif (DFS_recursive(g, node->vertex, key, stack, visited_list, 1) == 1)return 1;else {// Pop the recently visited neighbour from the stack*stack = pop(*stack);}}node = node->next;}}}return 0;
}

Now, that completes our DFS functions. Let’s now test it for our example graph.

现在,这完成了我们的DFS功能。 现在,为示例图进行测试。

Dfs Step 1 1
Dfs步骤1 1

void dfs_print(Graph g, int src, int dst) {// Find the vertex for the src keyint start = -1;for (int i=0; i<g.v; i++) {if (g.key_list[i] == src)start = i + 1;} if (start == -1) {fprintf(stderr, "Error: Key %d not found\n", src);return;}printf("Performing DFS on the Graph...\n");int ret_val = DFS(g, start, dst);if (ret_val == 1)printf("Key: %d is found!\n", dst);elseprintf("Key: %d is not found.\n", dst);
}int main() {// Graph with 6 verticesint vertex_list[] = {6, 2, 5, 14, 7, 1};Graph g = {vertex_list, 6, NULL};printf("Created a Graph Structure with %d vertices\n", g.v);g.adj_lists = init_adjacency_lists(g);// Let's connect the 6 vertices using edgesadd_edge(g, 1, 2);add_edge(g, 1, 3);add_edge(g, 2, 4);add_edge(g, 2, 5);add_edge(g, 3, 6);add_edge(g, 6, 5);// Print the Adjacency Listsfor (int i=0; i<g.v; i++) {printf("Vertex: %d , Key: %d => ", i+1, g.key_list[i]);print_list(g.adj_lists[i]);}// Print a Depth First Search from 6 to 14printf("\n");dfs_print(g, 6, 7);printf("\n");dfs_print(g, 6, 8);printf("\n");dfs_print(g, 6, 14);printf("\n");free_adj_lists(g);return 0;
}

The full code can be downloaded from the below section. When you compile the complete code, you’ll get the following output.

完整的代码可以从下面的部分下载。 编译完整的代码时,将得到以下输出。

Output

输出量


Created a Graph Structure with 6 vertices
Initialized Adjacency Lists!
Vertex: 1 , Key: 6 => Node: 3, Key: 5 -> Node: 2, Key: 2 ->
Vertex: 2 , Key: 2 => Node: 5, Key: 7 -> Node: 4, Key: 14 ->
Vertex: 3 , Key: 5 => Node: 6, Key: 1 ->
Vertex: 4 , Key: 14 =>
Vertex: 5 , Key: 7 =>
Vertex: 6 , Key: 1 => Node: 5, Key: 7 -> Performing DFS on the Graph...
Start Vertex: 1, Key: 6
Current Node: key: 6
Current Node: key: 5
Current Node: key: 1
Current Node: key: 7
Key: 7 is found!Performing DFS on the Graph...
Start Vertex: 1, Key: 6
Current Node: key: 6
Current Node: key: 5
Current Node: key: 1
Current Node: key: 7
Current Node: key: 2
Current Node: key: 14
Key: 8 is not found.Performing DFS on the Graph...
Start Vertex: 1, Key: 6
Current Node: key: 6
Current Node: key: 5
Current Node: key: 1
Current Node: key: 7
Current Node: key: 2
Current Node: key: 14
Key: 14 is found!

This shows that our code indeed works properly! It also prints the path of the search, so this will give you more insight into what it is doing.

这表明我们的代码确实可以正常工作! 它还会打印搜索的路径,因此这将使您对搜索的操作有更深入的了解。



下载代码 (Download the Code)

The Code is available as a Github Gist. I have tried my best to avoid errors and make the code as clear as possible, but if you spot any, please do mention them to me!

该规范可作为Github Gist使用 。 我已尽力避免出现错误,并使代码尽可能清晰,但如果发现任何错误,请向我提及!



结论 (Conclusion)

Hopefully, you’ve understood what the DFS Algorithm does, using the example and the implementation that we showed you. If you have any doubts or suggestions, please do mention them in the comment section below. Until next time!

希望您已经使用示例和我们向您展示的实现了解了DFS算法的功能。 如果您有任何疑问或建议,请在下面的评论部分中提及它们。 直到下一次!



参考资料 (References)

  • Wikipedia Article on Depth First Search维基百科有关深度优先搜索的文章


翻译自: https://www.journaldev.com/36006/depth-first-search

dfs深度优先搜索

dfs深度优先搜索_图的深度优先搜索(DFS)相关推荐

  1. 广度优先搜索生成树怎么画_图的深度优先遍历与广度优先遍历以及最小生成树...

    图的深度优先遍历 题目:写出附从每个顶点出发的一次深度优先搜索遍历序列.在纸上画出遍历过程和序列,提交截图. 错误回答 从A点开始遍历:0124-01324-0134-0324-034 从B点开始遍历 ...

  2. 图的深度优先遍历和广度优先遍历_图的深度优先遍历(DFS)与广度优先遍历(BFS)的c语言实现...

    头文件 #pragma warning( disable : 4996)#pragma once#ifndef _GRAPH_H_#define _GRAPH_H_ #define MAX_VERTE ...

  3. 深度优先遍历_二叉树的深度优先遍历,理解框架真的能够套用题目吗?不了解执行过程可能很难。...

    显然这是一个很普通的二叉树的深度优先遍历,从中可以提取出这样的框架: class TreeNode { int val; TreeNode left, right; } public void isF ...

  4. lucene 搜索_使用Lucene的搜索服务器搜索Jira问题

    lucene 搜索 您可能还记得我的第一篇博客文章 ,该文章描述了Lucene开发人员如何使用Lucene搜索应用程序查找我们的Jira问题来食用我们自己的狗粮. 该应用程序已成为许多现代Lucene ...

  5. 深度优先遍历和广度优先遍历_图与深度优先搜索和广度优先搜索

    什么是图? 图是一种复杂的非线性表结构.由若干给定的点一级任意两点间的连线所构成.图通常用来描述事物之间的特定关系, 代表的就是事物, 线就是事物之间所具有的关系.例如社交网络就是一种典型的图关系, ...

  6. 八数码深度优先搜索_树的深度优先搜索(上)

    还记得我们说迭代法的具体应用中的二分法吗?我们讨论了一个查字典的例子.如果要使用二分查找,我们首先要把整个字典排个序,然后每次都通过二分的方法来缩小搜索范围.不过在平时的生活中,咱们查字典并不是这么做 ...

  7. bfs广度优先搜索算法_图的广度优先搜索(BFS)

    bfs广度优先搜索算法 What you will learn? 您将学到什么? How to implement Breath first search of a graph? 如何实现图的呼吸优先 ...

  8. 广度优先搜索_快速入门广度优先搜索

    通过学习图的搜索算法,我们来学习下两种常见的算法:BFS.DFS. 广度优先搜索(BFS) 广度优先搜索(Breadth-First-Search),它更像是一种地毯式.层层推进的搜索策略.先从距离起 ...

  9. 没有搜索_没有明显足够搜索量关键词的类目产品应该怎么办?

    没有明显足够搜索量关键词的类目产品应该怎么办? 如果我们产品的需求搜索量不够多,那我们可以用搭售抱大腿的方式来处理关键词. 最经常的情况就是我在阿里巴巴的产品曝光量不够,点击率更低.收集调整了很多的关 ...

最新文章

  1. 详解微信域名防封的方法以及检测等工具的技术原理
  2. 员工培训案例分析答案_在职员工培训管理办法案例
  3. 计算机整个文稿应用回顾主题,《计算机应用基础》精品课程电子教案-PowerPoint 2003...
  4. java嵌入式db_Java DB嵌入式模式
  5. 库克“一语成谶”:又有 30 万台安卓设备被“感染”了!| 文末福利
  6. 为什么我推荐ImageJ?
  7. 简单mysql主从配置
  8. ComputeShader
  9. 买房建房装修之风水知识,多图
  10. android通过辅助功能收集数据
  11. 程序员非常实用的十个工具网站,值得收藏
  12. springboot+vue校园食堂网上订餐系统-带商家idea
  13. register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
  14. 小确幸BBS论坛-1-前期准备
  15. oracle substr clob,sql – 在CLOB上的SUBSTR的性能
  16. 在Springboot中使用pagehelper实现分页管理
  17. word里单词会被分成两部分
  18. day 53-1 Django基础三之视图函数
  19. 最长公共子序问题 ( LCS, Longest Commom Subsequence )POJ-1458
  20. Java爬虫demo,爬取文章链接

热门文章

  1. Chrome 实现前端页面自动刷新
  2. http://code.svnspot.com/ 免费代码托管
  3. [转载] JavaScrip ajaxt和python flask通过json传递数据的方法
  4. [转载] Python基于机器学习方法实现的电影推荐系统
  5. 博客园五月纪念日——去你的写博无用论
  6. zabbix4.0LTS安装配置
  7. 【luogu】P1772物流运输(最短路+DP)
  8. JAVA-初步认识-第六章-类与对象的关系(细节)
  9. 截获3389远程登陆的密码
  10. 3.3 keras模型构建的三种方式