首页 > 编程语言 >算法笔记(十五)——BFS 解决拓扑排序

算法笔记(十五)——BFS 解决拓扑排序

时间:2024-10-10 08:53:57浏览次数:9  
标签:int auto 拓扑 入度 BFS edges push 排序

文章目录

  • 拓扑排序
  • 课程表
  • 课程表 II
  • 火星词典

拓扑排序

有向无环图(DAG图)

  • 有向无环图指的是一个无回路的有向图

在这里插入图片描述

AOV网:顶点活动图

在有向无环图中,用顶点表示一个活动,用边来表示活动的先后顺序的图结构

拓扑排序

  • 找到一个先后顺序,结果可能不唯一
  • 如何拓扑排序?
    • 找到一个入度为零的点,然后输出;
    • 删除与该点连接的边;
    • 重复上述操作,直至图中没有点或没有入度为零的点(图中有环);

  • 实现拓扑排序
  1. 初始化:将所有入度为0的点加入到队列中
  2. 当队列不空的时候,循环:
    1. 拿出队头元素,加入到结果中
    2. 删除与该元素相连的边
    3. 判断:与删除边相连的点入度是否为0;如果为0,将该点加入队列

课程表

题目:课程表

在这里插入图片描述

思路

  • 建立哈希表unordered_map<int, vector<int>> edges;存储有向图的边,edges[b].push_back(a);表示b->a
  • vector<int> in(numCourses);来表示节点的入度
  • 利用上述容器建图
  • 将入度为 0的节点加入队列q
  • BFS进行拓扑排序,入度为0的节点依次出队,并将其邻接节点的入度减1。如果邻接节点的入度减为0,将其加入队列
  • 判断是否有环:vector<int> in(numCourses);中入度都为0即不存在环,返回true

C++代码

class Solution 
{
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) 
    {
        // 1. 初始化
        unordered_map<int, vector<int>> edges;
        vector<int> in(numCourses);

        // 2. 建图
        for(auto& e : prerequisites)
        {
            int a = e[0], b = e[1];
            edges[b].push_back(a);
            in[a]++;
        }

        // 3.拓扑排序
        queue<int> q;
        // 入度为 0 的点进队
        for(int i = 0; i < numCourses; i++)
        {
            if(in[i] == 0) q.push(i);
        }
        // BFS
        while(!q.empty())
        {
            auto t = q.front(); q.pop();
            for(auto e : edges[t])
            {
                in[e]--;
                if(in[e] == 0) q.push(e);
            }
        }
		
		// 4. 判断是否有环
        for(auto e : in)
        {
            if(e) return false;
        }
        return true;
    }
};

课程表 II

题目:课程表 II

在这里插入图片描述
思路

思路和上面一致,只需要在每次将入度为0的点顺序保存即为拓扑排序的顺序。

C++代码

class Solution 
{
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) 
    {
        // 1. 初始化
        unordered_map<int, vector<int>> edges;
        vector<int> in(numCourses);

        // 2. 建图
        for(auto& e : prerequisites)
        {
            int a = e[0], b = e[1];
            edges[b].push_back(a);
            in[a]++;
        }

        // 3.拓扑排序
        queue<int> q;
        vector<int> ret;
        // 入度为 0 的点进队
        for(int i = 0; i < numCourses; i++)
        {
            if(in[i] == 0) q.push(i);
        }
        // BFS
        while(!q.empty())
        {
            auto t = q.front(); q.pop();
            ret.push_back(t);
            for(auto e : edges[t])
            {
                in[e]--;
                if(in[e] == 0) q.push(e);
            }
        }

        for(auto e : in)
        {
            if(e) return {};
        }
        return ret;
    }
};

火星词典

题目:火星词典

在这里插入图片描述

思路

理解题意:

如何搜集信息:

  1. 两层for循环枚举所有的两个字符串组合
  2. 利用双指针,找出字典序规则信息
  • 建图和初始化入度信息
unordered_map<char, unordered_set<char>> edges; // 邻接表存储图
unordered_map<char, int> in; // 节点入度
  • 添加边和检测冲突

    1. 对于单词列表中的每一对单词,比较它们相同位置上的字符。如果找到第一个不同的字符,则添加一条从该字符到另一个字符的边,并增加目标字符的入度。
    2. 如果一个单词是另一个单词的前缀且更长,则这表示存在冲突(因为按照字典序,更长的单词不应该出现在更短的单词之后),此时设置checktrue并返回空字符串表示无解
  • 拓扑排序

    1. 将所有入度为0的节点(字母)加入队列
    2. 取出节点,将其添加到结果字符串中,并减少其所有邻接节点的入度。如果某个邻接节点的入度变为0,则将其加入队列
    3. 重复上述过程直到队列为空
  • 判断成环:检查是否所有节点的入度都为0

C++代码

class Solution 
{
    unordered_map<char, unordered_set<char>> edges; // 邻接表存储图
    unordered_map<char, int> in; // 节点入度
    bool check = false;

    void add(string& s1, string& s2)
    {
        int n = min(s1.size(), s2.size());
        int i = 0;

        for(; i < n; i++)
        {
            if(s1[i] != s2[i])
            {
                // a -> b
                char a = s1[i], b = s2[i];
                if(!edges.count(a) || !edges[a].count(b))
                {
                    edges[a].insert(b);
                    in[b]++;
                }
                break;
            }
        }
        if(i == s2.size() && i < s1.size()) check = true;
    }
public:
    string alienOrder(vector<string>& words) 
    {
        // 1. 建图 + 初始化入度信息
        for(auto word : words)
            for(auto ch : word)
                in[ch] = 0;

        int n = words.size();
        for(int i = 0; i < n; i++)
        {
            for(int j = i + 1; j < n; j++)
            {
                add(words[i], words[j]);
                if(check) return "";
            }
        }

        // 2.拓扑排序
        queue<char> q;
        for(auto& [a, b] : in)
            if(b == 0) q.push(a);

        string ret;
        while(!q.empty())
        {
            char t = q.front(); 
            q.pop();

            ret += t;
            for(auto ch : edges[t])
            {
                in[ch]--;
                if(in[ch] == 0) q.push(ch);
            }
        }

        // 3.判断成环
        for(auto& [a, b] : in)
            if(b) return "";


        return ret;
    }
};

标签:int,auto,拓扑,入度,BFS,edges,push,排序
From: https://blog.csdn.net/m0_74317866/article/details/142792487

相关文章

  • 基于C语言的排序
    排序的概念:排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]......
  • 【数据结构与算法】排序算法
    3.7排序算法概述比较排序算法算法最好最坏平均空间稳定思想注意事项冒泡O(n)O(n2n^2......
  • 排序算法之选择排序
    选择排序的思想是每次从未排序的部分中选择最小的元素,然后将其放在已经排序部分的末尾。遍历数组,从第一个元素开始,将其视为当前最小元素。在未排序的部分中,找到最小的元素,并记录其索引。将最小的元素与当前位置的元素交换位置重复步骤2和步骤3,直到遍历完整个数组比如有一......
  • 理解网络拓扑、虚拟机NAT、桥接
    理解网络拓扑、虚拟机NAT、桥接一、以校园网为例,物理机连接后进行ping操作首先于物理机本身的WLAN设置中查看当前连接的WiFi属性,获取本机获取到的IP地址使用win+R打开命令行,使用ping操作ps:校园网已有加密处理二、以同学的手机热点为例,物理机连接后进行ping操作操作同上......
  • 八大排序--05堆排序
    假设数组arr[]={5,7,4,2,0,3,1,6},请通过插入排序的方式,实现从小到大排列:方法:①利用完全二叉树构建大顶堆;     ②对顶元素和堆底元素进行交换,除堆底元素之外其余元素继续构造大顶堆;     ③重复步骤②,直到所有元素都不参与构建,整个数组排序完成【完全......
  • 八大排序--04希尔排序(缩小增量排序)
    假设数组arr[]={0,3,1,2,5,7,4,6},请通过插入排序的方式,实现从小到大排列:方法:将数组按照数组长度的一半为间隔进行分组,组内进行插入排序,小的数据在前面,大的数据在后面; 将数据按照数组长度一半的一半为间隔进行分组,组内进行插入排序;重复上述步骤,直到无法继续分组(步长为1时),整个......
  • 八大排序--03插入排序
    假设数组arr[]={5,7,4,2,0,1,6},请通过插入排序的方式,实现从小到大排列:方法:插入排序默认待排数组中的第一个是已经排好序的数值;定义游标从第二个数据开始不断向后方进行遍历,并将游标指向的数据不断插入到排好序的数组中。【实现插入的方式:】定义游标指向为j,以及游标的下一......
  • 算法学习--3 (快速排序)
    引言快速排序(QuickSort)是计算机科学中最流行的排序算法之一,它基于“分治”思想,通过递归地将数组分成两部分并分别排序,从而实现排序的目的。与冒泡排序和选择排序等简单算法相比,快速排序在平均情况下的性能非常优越,因此广泛应用于实际场景。本文将详细介绍快速排序的工作原理......
  • 算法学习--4 (插入排序)
    引言插入排序(InsertionSort)是一种简单且直观的排序算法,常用于小规模数据的排序。它的工作原理与人类排序扑克牌的方式类似,每次将一个元素插入到已经排好序的部分,直到所有元素都插入完成。本文将介绍插入排序的原理、实现代码、时间复杂度分析以及优缺点。插入排序的基本原......
  • 数据结构课程设计大项目————迷宫问题(邻接矩阵,prim生成算法,DFS寻路,BFS寻路,路径回溯
    一.前言迷宫问题是数据结构中最值得实践的大项目之一,本文主要讲解思路,提供的代码大部分都有注释(没有的就是太多了懒得写了QAQ)。为了更好的表现效果,该程序使用了easyx可视化,easyx简单易学(大概一天到两天就可以学会),上手简单。该程序由c语言实现,本人水平有限程序可优化空间很大。......