拓扑排序的DFS和BFS

博主以前有一个疑问,DFS和BFS各自的适用范围是?我想你今天看了这篇文章之后会有一个判断!


BFS

数据结构与算法分析:c语言描述(p217)
已经存在一个Indgree入度数组(indgree[v]={(u,v)的数目})
以及一个邻接矩阵,求一个拓扑排序
提示:图中出现环就会拓扑失败
代码风格被我改为了C++

void TopSort(vector<vector<int>> G){    
//图中所有的点都要被遍历到,每次取出一个点,共执行NumVertex次
    for(int counter=0;counter<G.size();counter++){
        int v=findVertexIndgreeZero(G);//寻找入度为0的点
        if(!v){
                cout<<"有环,失败!"<<endl;
                return;
            }
        TopNum[v]=counter;//第counter+1个被遍历到的
        for(int i;i<G[v].size();i++){
            if(G[v][i])
                indgree[i]--;
        }
    }
}

用一句话来概括这个算法就是依次寻找出入度为0的点,然后将其所通的所有点入度都减1。循环直至结束或者报错(有环)。

上面这个算法存在优化的余地,findVertexIndgreeZero()的复杂度为O(n),执行n次为O(N2)次,用一个队列会大大缩减复杂度

void TopSort(vector<vector<int>> G){
    int counter=0;
    queue<int> q;
    for(int i=0;i<G.size();i++){
        if(indgree[i]==0);
        q.push(i);
    }
    while(!q.empty()){
        int u=q.top();q.pop();
        TopNum[u]=counter++;
        for(int i=0;i<G[u].size();i++){
            if(G[u][i]){
                if(--indgree[i]==0)
                    q.push(i);
                }
            }
        }
    if(counter!=G.size())
        cout<<"有环"<<endl;
}

这个结果很有趣,我们可以不严谨地总结一下“拓扑排序就是在找入度0点+更新入度”。
题外话
另外TopNum[i]保存了遍历的顺序。那么能不能TopNum[counter]=i,这样呢。。也可以。因为i和counter肯定是一一映射的关系。如果学习过数据库,那么你就可以说:谁来做主键都可以。

DFS

刘汝佳 算法竞赛入门经典第二版
P167
假设有n个变量,m个二元组(u,v),分别表示u< v。那么寻找一个不等式,包含所有的变量。类似于a< b,b< c ,则输出a< b< c;

我们可以把二元组小于关系看作边关系。这一转换其实很自然
然后,寻找一个不等式,其实就是在寻找一个拓扑顺序。那么,这个问题其实就是一个拓扑排序,完全可以用BFS完成

但是还有另外一种比较有趣的解法,就是使用dfs

我们首先定义一个函数

bool dfs(int u);//u代表当前点,返回点u之后是否存在一个拓扑路线

有了这个定义就不难继续做下去了,我们再想到:当前点u若是想能够返回true,那么它所能到达(u->v)的所有点也应该都能。
那么,失败的具体条件是什么?就是成环

只要之后遍历到的点和之前的点u有关系(v->u),那么就说明成环!返回false。
那么,代码应该是:

int vector<vector<int>> G 
int c[maxn];
int TopNum[maxn];
int t;
bool dfs(int u){
    c[u]=-1;
    for(int i=0;i<G.size();i++)
        if(G[u][i]){
            if(c[i]==-1){//大水冲了龙王庙,这个i点在之前已经被遍历到了,成环!
                return false;       
            }
            if(!c[i]&&!dfs(i)) return false;
        }
        //经过了检验
        c[u]=1;
        TopNum[--t]=u;
        return true;
}
bool topsort(){
    int t=n;
    memset(c,0.sizeof(c));
    for(int i=0;i<G.size();i++)
        if(!c[u])
            if(!dfs(u))
                return false;
    return true;            
}

版权声明:本文为博主原创文章,转载请标明出处。

转载于:https://www.cnblogs.com/fridge/p/4861907.html

### DFSBFS排序相关场景中的应用 #### 1. **DFS的应用** 深度优先搜索(DFS)是一种递归式的遍历方式,常用于图或树的遍历时构建某种顺序关系。尽管它本身并不是一种排序算法,但在某些特定场景下可以通过DFS间接实现排序功能。 - **拓扑排序** 拓扑排序是基于有向无环图(DAG)的一种排列技术,其中每个节点表示一项任务或事件,边代表依赖关系。通过DFS可以有效地完成拓扑排序的任务。具体过程如下:从某个未访问的节点出发执行DFS,在回溯过程中记录退出次序并逆置即可获得拓扑序列[^1]。 ```python def dfs_topological_sort(graph, node, visited, result): visited.add(node) for neighbor in graph[node]: if neighbor not in visited: dfs_topological_sort(graph, neighbor, visited, result) result.append(node) def topological_sort(graph): visited = set() result = [] for node in graph: if node not in visited: dfs_topological_sort(graph, node, visited, result) return result[::-1] ``` #### 2. **BFS的应用** 广度优先搜索(BFS)同样不直接作为排序工具存在,但它能够很好地支持一些涉及层级处理的操作,例如层序遍历二叉树以获取节点间的相对位置信息,这实际上也是一种特殊的“排序”。 - **层序遍历与排序关联** 对于一棵给定的二叉树而言,如果希望按照自顶向下逐层打印各结点,则可借助队列辅助完成此操作——即典型的BFS思路。这种形式下的输出结果本质上反映了另一种意义上的有序性安排[^3]。 ```python from collections import deque def bfs_level_order(root): queue = deque([root]) level_order = [] while queue: current_node = queue.popleft() if current_node is None: continue level_order.append(current_node.val) if current_node.left: queue.append(current_node.left) if current_node.right: queue.append(current_node.right) return level_order ``` #### 3. **两者的主要区别** | 特性 | DFS | BFS | |--------------------|------------------------------------------|-------------------------------------------| | 遍历方向 | 垂直深入 | 水平扩展 | | 数据结构 | 利用栈(显式或隐含于函数调用堆栈之中) | 显式使用队列 | | 时间复杂度 | O(V+E),V为顶点数,E为边的数量 | O(V+E) | | 空间复杂度 | 较高,取决于最大递归深度 | 取决于同一层最多有多少个节点 | | 应用实例 | 如前所述的拓扑排序 | 层次遍历、最短路径计算 | 以上对比可以看出,在面对不同类型的排序需求时,应依据具体情况选取适合的技术手段[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值