• 算法基础学习笔记——⑩DFS与BFS\树与图


    ✨博主:命运之光
    ✨专栏:算法基础学习

    在这里插入图片描述

    目录

    DFS与BFS\树与图

    ✨DFS

    ✨BFS

    🍓宽搜流程图如下:

    🍓宽搜流程:

    🍓广搜模板

    ✨树与图

    🍓树是特殊的图(连通无环的图)

    🍓树与图的存储:

    🍓用宽搜框架来搜索图:


    前言:算法学习笔记记录日常分享,需要的看哈O(∩_∩)O,感谢大家的支持!


     DFS与BFS\树与图

    ✨DFS

    //回溯,剪枝

    当使用深度优先搜索(DFS)回溯算法来搜索图时,我们需要考虑以下几个步骤:

    1. 初始化数据结构:创建一个栈(通常使用先进后出的原则)来存储待探索的节点,以及一个集合(通常使用哈希集合或集合)来记录已访问的节点。
    2. 将起始节点放入栈中,并将其标记为已访问。
    3. 进入循环,直到栈为空:
      • 从栈中取出一个节点。
      • 检查该节点是否为目标节点。如果是,则搜索完成,返回结果。
      • 如果不是目标节点,则将其所有未访问过的邻居节点加入栈,并标记为已访问。
      • 继续下一轮循环。
    1. 如果循环结束时仍未找到目标节点,则图中不存在目标节点。

    剪枝:可以提前判断当前方案一定不合法,就不用往下搜

    ✨BFS

    🍓宽搜流程图如下:

    🍓宽搜流程:

    🍓广搜模板

    1. q.push(初始状态);
    2. while(q.empty()){
    3. a=q.front();
    4. q.pop();
    5. for(枚举a的所有可达状态v){
    6. if(本状态v合法){
    7. 执行标记操作;
    8. q.push(v);
    9. }
    10. }
    11. }

    连通块问题:

    例题:全球变暖

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N=1005;
    6. bool v[N][N],book[N*N];
    7. char a[N][N];
    8. int n,w[N][N],s,cnt;
    9. int dx[4]={-1,0,1,0};
    10. int dy[4]={0,1,0,-1};
    11. typedef struct node
    12. {
    13. int x,y;
    14. }node;
    15. queueq;
    16. bool check(int x,int y)
    17. {
    18. if(x<1||x>n||y<1||y>n)
    19. return false;
    20. return true;
    21. }
    22. bool judge(int x,int y)
    23. {
    24. if(check(x+1,y)&&a[x+1][y]=='.')
    25. return false;
    26. if(check(x,y+1)&&a[x][y+1]=='.')
    27. return false;
    28. if(check(x-1,y)&&a[x-1][y]=='.')
    29. return false;
    30. if(check(x,y-1)&&a[x][y-1]=='.')
    31. return false;
    32. return true;
    33. }
    34. void bfs()
    35. {
    36. while(!q.empty())
    37. {
    38. node head,tail;
    39. head=q.front();
    40. q.pop();
    41. for(int i=0;i<4;i++)
    42. {
    43. tail.x=head.x+dx[i];
    44. tail.y=head.y+dy[i];
    45. if(check(tail.x,tail.y)&&a[tail.x][tail.y]=='#'&&w[tail.x][tail.y]==0)
    46. {
    47. w[tail.x][tail.y]=cnt;
    48. q.push(tail);
    49. }
    50. }
    51. }
    52. }
    53. int main()
    54. {
    55. cin>>n;
    56. for(int i=1;i<=n;i++)
    57. {
    58. for(int j=1;j<=n;j++)
    59. {
    60. cin>>a[i][j];
    61. }
    62. }
    63. for(int i=1;i<=n;i++)
    64. {
    65. for(int j=1;j<=n;j++)
    66. {
    67. if(a[i][j]=='#'&&w[i][j]==0)
    68. {
    69. cnt++;
    70. w[i][j]=cnt;
    71. node tmp={i,j};
    72. q.push(tmp);
    73. bfs();
    74. }
    75. }
    76. }
    77. for(int i=1;i<=n;i++)
    78. {
    79. for(int j=1;j<=n;j++)
    80. {
    81. if(a[i][j]=='#')
    82. {
    83. if(judge(i,j))
    84. {
    85. book[w[i][j]]=true;
    86. }
    87. }
    88. }
    89. }
    90. for(int i=1;i<=cnt;i++)
    91. {
    92. if(book[i]==true)
    93. {
    94. s++;
    95. }
    96. }
    97. cout<
    98. return 0;
    99. }

    问题2:

    两个BFS

    例题:Fire

    1. /*
    2. 预处理:预处理出火传染到(i,j)点的最早时间
    3. 人在去想要走到(i,j)点时,到(i,j)点的时刻一定要小于火最早到(i,j)的s时刻
    4. */
    5. #include
    6. #include
    7. #include
    8. using namespace std;
    9. const int N=1000+5;
    10. const int INF=0x3f3f3f3f;
    11. typedef struct Node{
    12. int x,y;
    13. int t;
    14. }Node;
    15. int t,n,m;
    16. int ti[N][N];//ti[i][j]是火最早到(i,j)的时间
    17. char a[N][N];
    18. queue fq,q;
    19. bool vis[N][N];
    20. int _next[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
    21. bool judge(int x,int y)
    22. {
    23. if(x<1||x>n||y<1||y>m)
    24. return false;
    25. return true;
    26. }
    27. void FireBFS()
    28. {
    29. Node _new;
    30. while(!fq.empty())
    31. {
    32. Node tp=fq.front();
    33. fq.pop();
    34. for(int i=0;i<4;i++)
    35. {
    36. _new.x=tp.x+_next[i][0];
    37. _new.y=tp.y+_next[i][1];
    38. if(judge(_new.x,_new.y)&&a[_new.x][_new.y]=='.'&&ti[_new.x][_new.y]==INF)
    39. {
    40. _new.t=tp.t+1;
    41. ti[_new.x][_new.y]=_new.t;
    42. fq.push(_new);
    43. }
    44. }
    45. }
    46. }
    47. int ManBFS(){
    48. Node _new;
    49. while(!q.empty()){
    50. Node tp=q.front();
    51. q.pop();
    52. if(tp.x==1||tp.x==n||tp.y==1||tp.y==m){
    53. return tp.t+1;
    54. }
    55. for(int i=0;i<4;i++){
    56. _new.x=tp.x+_next[i][0];
    57. _new.y=tp.y+_next[i][1];
    58. if(judge(_new.x,_new.y)&&a[_new.x][_new.y]=='.'&&!vis[_new.x][_new.y]){
    59. _new.t=tp.t+1;
    60. if(_new.t
    61. vis[_new.x][_new.y]=true;
    62. q.push(_new);
    63. }
    64. }
    65. }
    66. }
    67. return -1;
    68. }
    69. void init(){
    70. memset(ti,0x3f,sizeof(ti));
    71. memset(vis,false,sizeof(vis));
    72. while(!fq.empty())
    73. fq.pop();
    74. while(!q.empty())
    75. q.pop();
    76. }
    77. int main(){
    78. cin>>t;
    79. while(t--){
    80. init();
    81. cin>>n>>m;
    82. for(int i=1;i<=n;i++){
    83. for(int j=1;j<=m;j++){
    84. cin>>a[i][j];
    85. if(a[i][j]=='F'){
    86. Node tmp={i,j,0};
    87. ti[i][j]=0;
    88. fq.push(tmp);
    89. }
    90. else if(a[i][j]=='J'){
    91. Node tmp={i,j,0};
    92. vis[i][j]=true;
    93. q.push(tmp);
    94. }
    95. }
    96. }
    97. FireBFS();
    98. int ans=ManBFS();
    99. if(ans==-1)
    100. cout<<"IMPOSSIBLE"<
    101. else
    102. cout<
    103. }
    104. }
    105. /*
    106. 2
    107. 4 4
    108. ####
    109. #JF#
    110. #..#
    111. #..#
    112. 3 3
    113. ###
    114. #J.
    115. #.F
    116. */

    ✨树与图

    🍓树是特殊的图(连通无环的图)

    🍓树与图的存储:

    树是一种特殊的图,与图的存储方式相同。

    对于无向图中的边ab,存储两条有向边a->b, b->a。

    因此我们可以只考虑有向图的存储。

    (1) 邻接矩阵:g[a][b] 存储边a->b

    (2) 邻接表:

    1. // 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
    2. int h[N], e[N], ne[N], idx;
    3. // 添加一条边a->b
    4. void add(int a, int b)
    5. {
    6. e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
    7. }
    8. // 初始化
    9. idx = 0;
    10. memset(h, -1, sizeof h);

    树与图的遍历:

    时间复杂度 O(n+m)O(n+m), nn 表示点数,mm 表示边数

    (1) 深度优先遍历

    1. int dfs(int u)
    2. {
    3. st[u] = true; // st[u] 表示点u已经被遍历过
    4. for (int i = h[u]; i != -1; i = ne[i])
    5. {
    6. int j = e[i];
    7. if (!st[j]) dfs(j);
    8. }
    9. }

    (2) 宽度优先遍历

    1. queue<int> q;
    2. st[1] = true; // 表示1号点已经被遍历过
    3. q.push(1);
    4. while (q.size())
    5. {
    6. int t = q.front();
    7. q.pop();
    8. for (int i = h[t]; i != -1; i = ne[i])
    9. {
    10. int j = e[i];
    11. if (!st[j])
    12. {
    13. st[j] = true; // 表示点j已经被遍历过
    14. q.push(j);
    15. }
    16. }
    17. }

    🍓用宽搜框架来搜索图:

    当使用宽度优先搜索(BFS)框架搜索图时,我们可以按照以下步骤进行操作

    1. 选择一个起始节点,并将其添加到队列中,同时将其标记为已访问。
    2. 重复以下步骤直到队列为空:
      • 从队列中取出一个节点作为当前节点。
      • 检查当前节点是否满足搜索条件。如果是,则返回结果或执行相应操作。
      • 遍历当前节点的所有相邻节点。
      • 对于每个未被访问的相邻节点,将其添加到队列中,并将其标记为已访问。
    1. 如果队列为空且没有找到满足条件的节点,则搜索结束,可以返回相应的结果或执行其他操作。

    🍓以下是使用宽度优先搜索框架在C++中搜索图的示例代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. bool bfs(const std::unordered_map<char, std::vector<char>>& graph, char startNode, char targetNode) {
    7. std::queue<char> queue; // 创建一个队列
    8. std::unordered_set<char> visited; // 创建一个集合,用于记录已访问的节点
    9. queue.push(startNode); // 将起始节点放入队列
    10. visited.insert(startNode); // 标记起始节点为已访问
    11. while (!queue.empty()) {
    12. char node = queue.front(); // 从队列中取出一个节点
    13. queue.pop();
    14. if (node == targetNode) // 检查是否为目标节点
    15. return true;
    16. const std::vector<char>& neighbors = graph.at(node); // 获取当前节点的邻居节点
    17. for (char neighbor : neighbors) {
    18. if (visited.find(neighbor) == visited.end()) { // 如果邻居节点未被访问过
    19. queue.push(neighbor); // 将邻居节点加入队列
    20. visited.insert(neighbor); // 标记邻居节点为已访问
    21. }
    22. }
    23. }
    24. return false; // 循环结束,未找到目标节点
    25. }
    26. int main() {
    27. std::unordered_map<char, std::vector<char>> graph = {
    28. {'A', {'B', 'C'}},
    29. {'B', {'D', 'E'}},
    30. {'C', {'F'}},
    31. {'D', {}},
    32. {'E', {'F'}},
    33. {'F', {}}
    34. };
    35. char startNode = 'A';
    36. char targetNode = 'F';
    37. bool result = bfs(graph, startNode, targetNode);
    38. if (result)
    39. std::cout << "The target node '" << targetNode << "' is reachable from the start node '" << startNode << "'.\n";
    40. else
    41. std::cout << "The target node '" << targetNode << "' is not reachable from the start node '" << startNode << "'.\n";
    42. return 0;
    43. }

    在上述代码中,使用std::unordered_map来表示图的邻接表,其中键是节点,值是该节点的邻居节点列表。还使用std::queue来作为队列存储待探索的节点,std::unordered_set用于记录已访问的节点。

    注意,上述代码仅为示例,假设图中的节点标识为字符('A', 'B', 'C'等),您可以根据实际情况进行修改和适应。

  • 相关阅读:
    前端第三天__WebPack是什么?WebPack打包JS文件和CSS文件
    美国NIST公布首批后量子密码标准算法
    WebGPU 中消失的 FBO 和 RBO
    ubuntu16.04修改静态ip
    NAS和私有云盘的区别?1篇文章说清楚
    微信小程序--云开发
    sql建库,建表基础操作
    通过JavaScript 实现简单的全选、不全选的思想
    vlan虚拟局域网学习笔记
    企业级Java EE架构设计精深实践
  • 原文地址:https://blog.csdn.net/VLOKL/article/details/130904299