栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

第七课:DFS BFS 树与图的深度、广度遍历、拓扑排序

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第七课:DFS BFS 树与图的深度、广度遍历、拓扑排序

 

目录

一:DFS BFS

1、DFS    深度优先遍历

习题:n皇后问题

 2.BFS  广度优先遍历

习题1:走迷宫

习题2:八数码

 二、树和图的深度、广度优先遍历

1.习题1:树的重心(dfs)

2.习题二:图中点的层次

三、拓扑序列


一:DFS BFS

1、DFS    深度优先遍历

1.传入一个参数,表示选择第u个位置。

2.递归退出条件:走到最深处(然后可能要输出结果)

3.在这个位置尝试每一种可能的状态,如果不可行,则剪枝,如果可行,就选择这个状态,并标记“走过”,选择下一个位置

4.特别注意要回溯,在退出这次递归,回到上一层后“恢复现场"。为接下来的选择做准备。

选取全排列数字作为一个示范:

习题:n皇后问题

思路:

1.在排列数字中,使用了一个数组记录某个数字是否被选择过。类似的,n皇后问题要求同一行、同一列、对角线上都不能有两个皇后存在,又要求每一行必定有一个皇后,我们可以每一次在每一行上选择一个位置放皇后,然后用col、dg、udg三个数组存储某一条线上是否已经有皇后。

2.如何处理两条对角线数组:

对于某一行u。遍历正对角线数组下标有dg[u+i],遍历反对角线有udg[n-u+i]。(自己画图可厘清)

3.处理方法和排列数字非常类似,故不赘述。

#include
using namespace std;
const int N =20;

bool col[N],dg[N],udg[N];//列、正对角线、副对角线
char g[N][N];
int n; 

void dfs(int u)
{
    if(u==n)
    {
        for(int i=0;i>n;
    for(int i=0;i 

 2.BFS  广度优先遍历

留意初始化。

1 BFS算法:
 2 
 3 通常用队列(先进先出,FIFO)实现
 4 
 5 初始化队列Q;
 6 Q = {起点s};
 7 标记s为已访问;
 8 while(Q非空)
 9 {
10     取Q队首元素u;
11     u出队;
12     if(u==目标状态)
13     {
14         ……
15     }
16     else
17     {
18         所有与u相邻且未被访问的点进入队列;
19         标记u为已访问;
20     }
21 }

 主要思想是使用队列,存储有待遍历的每一层的值。

习题1:走迷宫

 思路:

1.从初始结点开始,层次遍历,计算每一个点在迷宫中距离起点的步数

2.注意事项:每个点只需要遍历一次就好

3.计算距离方式,初始化起点距离为0,每个点距离为上一个点距离+1;

4.最后返回d[最后一个点的坐标];

//这里填你的代码^^
#include
#include
#include
#include
using namespace std;
const int N =110;
typedef pair PII;

int d[N][N],g[N][N];
int n,m;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};

int bfs()
{
    queue  q;
    memset(d,-1,sizeof d);
    d[0][0]=0;
    q.push({0,0});
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int x=t.first+dx[i],y=t.second+dy[i];


            if(x>=0&&y>=0&&x>n>>m;
    for(int i=0;i>g[i][j];
    }


    cout< 

习题2:八数码

 难点:

1.如何存储每个状态

2.如何记录每个状态的距离。

思路

1.使用一个字符串来存储每个状态,用哈希表就可以存储dist值

2.状态转移时就转换成矩阵来进行操作。设定“12345678x”为终点,达到则退出

3.如此看这个问题就是个典型的bfs。运用queue 即可完成

4.图论难点在图的建立、抽象出边、距离、状态。要在这方面开拓思路。 

代码实现:(可以留意坐标转换的公式)

#include
#include
#include
#include
using namespace std;

int bfs(string start)
{
    string end="12345678x";
    queue q;
    unordered_map  d;
    q.push(start);
    d[start]=0;
    
    while(q.size())
    {
        auto t=q.front();
        q.pop();
        int distance=d[t];
        if(t==end)  return d[t];
        //状态转移
        int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

        int k=t.find('x');
        int x=k/3, y=k%3; //找到x在矩阵中的坐标
        for (int i = 0; i < 4; i ++ )
        {
            int a=x+dx[i],b=y+dy[i];
            if(a>=0&&a<3&&b>=0&&b<3)
            {
                swap(t[k],t[a*3+b]);//将x转换
                if(!d.count(t))//如果没有被搜过
                {
                    q.push(t);
                    d[t]=distance+1;
                }
                
                
                swap(t[k],t[a*3+b]);//因为要接着搜原来的t中x与其他方向交换
            }
        }
    }
    return -1;
}

int main()
{
    string start;//初始状态
    for(int i=0;i<9;i++)   
    {
        char c;
        cin>>c;
        start+=c;
    }
    
    cout< 

 二、树和图的深度、广度优先遍历

  图和树一般用邻接表或者邻接矩阵存储,,稀疏图多用邻接表。

所谓邻接表,其实就是在每个节点上拉出一个链表,存储与之相连的其他节点。有向图存储则联通a->b,无向图则连两条边即可。

int h[N],e[N],ne[N],idx;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

树与图的dfs,bfs和上述十分类似,只不过遍历改成遍历一个链表。

1.习题1:树的重心(dfs)

 思路:

1.删除一个节点后,剩余联通块中点数最大值只可能是:该节点中子树某子树的连通点数最大值,或该节点之上的所有节点(总数减去该节点与该节点子树).

 

 2.使用dfs可以返回该节点与其子树的节点个数。

3.使用全局变量ans,在递归dfs的过程中更新答案。

#include
#include
#include

using namespace std;
const int N=100010,M=2*N;
int n;
int h[N],e[M],ne[M],idx;
int ans=N;
bool st[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

int dfs(int u)
{
    st[u]=true;
    int size=0,sum=1;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(st[j])  continue;
        int s=dfs(j);
        sum+=s;
        size=max(size,s);
    }
    
    size=max(size,n-sum);//删除这个节点后,连通块节点的最大值。
    ans=min(size,ans);
    
    return sum;
}

int main()
{
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=0;i>a>>b;
        add(a,b),add(b,a);
    }
    
    dfs(1);//题目给的a b 编号从1开始,自然第一个结点就是1
    cout<

2.习题二:图中点的层次

 思路:

1.有了上面使用邻接表的经验,这道题就是一道很裸的题。

2.直接使用bfs,建队列,初始化,更新距离。。。。return d【n】 即可

#include
#include
#include

using namespace std;

const int N =100010;
int h[N],e[N],ne[N],idx;
int n,m;
int d[N];
queue q;


void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

int bfs()
{
    memset(d,-1,sizeof d);
    d[1]=0;
    q.push(1);
    while(q.size())
    {
        int t=q.front();
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]==-1)
            {
                q.push(j);
                d[j]=d[t]+1;
            }
        }
    }
    
    return d[n];
}


int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i>a>>b;
        add(a,b);
    }

    cout< 

三、拓扑序列

拓扑序列不唯一:

 

思路:

1.找到一堆在同一层次的结点,然后bfs。

2.第一层次的节点必定是入度为0的结点。入度可以作为我们判断先后层次的标准。

3.首先把第一层次的push进队列,然后bfs,遇到一个没有被遍历到的节点就把该节点的入度减一(本质表示我们的遍历更深了一层,,浅显理解的话就是还有其他的结点在这个节点的前面),当一个节点的入度为0的时候,就代表轮到这个节点被遍历了,把这个节点push进队列。

 

 4.拓扑序列在哪里:使用数组模拟队列,其实按每次取出的队头元素的顺序就是拓扑排序。

如果用数组模拟队列的话,pop出队列的节点编号实际上还在数组里存储着,遍历一遍输出就好。

5.判断是否存在有拓扑序列的方式是队列里是否一共进了n个点(n个点都被遍历了

#include
#include
#include
#include
using namespace std;
//找到度数为0的点(必定在拓扑序列的开端),然后层序遍历。
//由a遍历到b节点时,删除a->b边,如果此时b的入度是0,把b加入队列
//如果遍历完了n个点(tt==n-1)  代表存在拓扑序列

//拓扑序列不唯一,此题有special judge
const int N =100010;
int h[N],e[N],ne[N],idx;
int d[N];//d存储每个节点的入度
int n,m;

int q[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++,d[b]++;
}

int topsort()
{
    int hh=0,tt=-1;
    for(int i=1;i<=n;i++)  
    if(!d[i])  q[++tt]=i;
    
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            d[j]--;
            if(!d[j])  q[++tt]=j;
        }
    }
    
    return tt==n-1;
}


int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=0;i>a>>b;
        add(a,b);
    }
    if(topsort())
    {
        for(int i=0;i 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/935627.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号