AcWing算法学习笔记:搜索与图论1(DFS + BFS + 树与图的深度优先遍历 + 树与图的广度优先遍历 + 拓扑排序)

搜索与图论

  • 一、DFS
    • ① 排列数字
    • ② n-皇后问题(还没写)
  • 二、BFS
    • ① 走迷宫
    • ② 八数码(还没写)
  • 三、树与图的深度优先遍历(树的重心)
  • 四、树与图的广度优先遍历(图中点的层次)
  • 五、有向图的拓扑序列

比较 空间 特点 数据结构
DFS 0(h) 第一次搜到的答案不具有最短性 stack
BFS 0(2^h) 第一次搜索到的答案一定是最短路 queue

一、DFS

① 排列数字

算法
两个重要概念:回溯和剪枝
想好搜索顺序,构建一颗搜索树
回溯时一定要注意恢复现场

代码

#include 
#include 
#include 

using namespace std;

const int N = 10;

int n;
int path[N];
bool st[N];

void dfs(int u)
{
    //当填充位置为n时,说明已经有n个数了,将路径上的n个数输出
    if (u == n)
    {
        for (int i = 0; i < n; i ++) cout << path[i] << " ";
        cout << endl;
        return ;
    }
    
    //从1~n中顺次判断,如果该数没有被遍历过,则取这个数
    for (int i = 1; i <= n; i ++)
    {
        if (!st[i])
        {
            st[i] = true; //修改状态
            path[u] = i;
            dfs(u + 1); //填充下一位数字
            st[i] = false; //还原状态
        }
    }
}

int main()
{
    cin >> n;
    
    dfs(0); //从path的0号位置开始填充
    
    return 0;
}

② n-皇后问题(还没写)

算法

代码


二、BFS

① 走迷宫

算法
AcWing算法学习笔记:搜索与图论1(DFS + BFS + 树与图的深度优先遍历 + 树与图的广度优先遍历 + 拓扑排序)_第1张图片

代码

#include 
#include 
#include 
#include 

using namespace std;
const int N = 110;

typedef pair <int, int> PII;

int n, m;
int g[N][N];
int st[N][N]; //记录当前节点最少需要几步走到,并且防止每个节点被遍历两次

void bfs()
{
    queue <PII> q;
    q.push({0, 0});
    st[0][0] = 0;

    while (!q.empty()) //可以遍历到所有的0,仅且只有一遍
    {
        auto cur = q.front();
        q.pop();
        
        int dx[4] = {-1, 1, 0, 0};
        int dy[4] = {0, 0, -1, 1};
        for (int i = 0; i < 4; i ++)
        {
            int a = cur.first + dx[i], b = cur.second + dy[i];
            if (a >= 0 && a < n && b >= 0 && b < m && g[a][b] == 0 && st[a][b] == -1)
            {
                q.push({a, b});
                st[a][b] = st[cur.first][cur.second] + 1; //该节点的出边节点所需步数加一
            }
        }
    }
    cout << st[n - 1][m - 1]; //出口处那个点记录的步数一定是最少的
}

int main()
{
    cin >> n >> m;
    
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++)
            cin >> g[i][j];
    
    memset(st, -1, sizeof(st));
    
    bfs();
    
    return 0;
}

② 八数码(还没写)

算法

代码


AcWing算法学习笔记:搜索与图论1(DFS + BFS + 树与图的深度优先遍历 + 树与图的广度优先遍历 + 拓扑排序)_第2张图片

三、树与图的深度优先遍历(树的重心)

代码

const int N = 100010, M = N * 2; //N条边,开2 * N 个点
int h[N], e[M], ne[M], idx; // h存储每个节点的存放位置,e存放每个节点的值,ne存放下一个节点的位置,idx指向当前使用的数组下标
bool st[M]; //记录每个节点是否被遍历过

//初始化
memset(h, -1, sizeof(h));

//将y节点接入以x为头的单链表中
//读入边使用add函数构建图,例如无向图x - y :add(x, y), add(y, x),需要双向连接
void add(int x, int y)
{
    e[idx] = y;
    ne[idx] = h[x];
    h[x] = idx ++;
}

//遍历以u为头的单链表
for (int i = h[u]; i != -1; i = ne[i]){}

//树的深搜,可以返回以u为根节点的子节点数目
int dfs(int u)
{
    st[u] = true;
    
    int sum = 1;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int cur = e[i]; //当前访问节点的内容
        if (!st[cur])
        {
            int s = dfs(cur);
            sum += s;
        }
    }
    return sum;
}

//树的宽搜就是套用普通宽搜模板即可

四、树与图的广度优先遍历(图中点的层次)

算法

代码


五、有向图的拓扑序列

算法
使用邻接表存储有向图,并记录每个点的入度
使用bfs得到拓扑序列,并且可以判断该图中是否有环
每次将入度为0的点放入队列,该点弹出时,将其相连的点入度-1,并将入度为0的点入队
若存在环,整个过程中所有入度为0的点的个数小于总点数
代码

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010;

int n, m, R[N];
int h[N], e[N], ne[N], idx;

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

void bfs()
{
    queue <int> q, res;
    for (int i = 1; i <= n; i ++)
    {
        if (!R[i])
        {
            q.push(i);
            res.push(i);
        }
    }
    
    while (!q.empty())
    {
        auto t = q.front();
        q.pop();
        
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            R[j] --;
            if (R[j] == 0)
            {
                q.push(j);
                res.push(j);
            }
        }
    }
    if (res.size() < n) cout << -1;
    else
    {
        while (!res.empty())
        {
            auto x = res.front();
            res.pop();
            cout << x << " ";
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    cin >> n >> m;
    
    bool flag = false;
    for (int i = 0; i < m; i ++)
    {
        int x, y;
        cin >> x >> y;
        if (x == y)
        {
            flag = true;
        }
        R[y] ++;
        add(x, y);
    }
    if (flag) cout << -1;
    else bfs();
    return 0;
}

你可能感兴趣的:(深度优先,算法,学习)