有向无环图讲解及模板(C++代码)

一、有向无环图

一个无环有向图做有向无环图(Directed Acyclic Graph)。简称DAG 图

图论中,如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。

有向无环图

因为有向图中一个点经过两种路线到达另一个点未必形成环,因此有向无环图未必能转化成树,但任何有向树均为有向无环图。

使用有向无环图解题时,要先判断是否是有向无环题。如果任务x必须在任务y之前完成:x→y,而y→z。也就是说一般在涉及优先级限制的问题时,使用有向无环图的方法。

注意与并查集进行区分

关于并查集模板:https://blog.csdn.net/qq_41687938/article/details/112850265

二、有向无环图求解过程

做有向无环图题的时候,一共就三步:

1.根据题目意思及输入,理清两节点间的指向关系,构建由前指向后的有向无环图

也就是构建一个map,key为当前节点,val数组为当前节点指向的那些节点。

2.根据有向无环图,统计每个节点出现的次数,因为有的节点已经出现过,但还是可能由其他指向路径的节点再指回来,在输出的时候需要遍历其最后出现的地方,所以要记一下它出现的次数。

其中,头节点的次数记为-1,并将头节点保存起来,方便接下来的遍历。

3.因为有向无环图的输出一般都有要求按大小关系输出(本文按升序输出!),也就是构建一个优先队列来完成节点输出。每遍历一个节点就将其所指向的节点压入队列中,实现了某节点的下一层与当前节点层的其他节点的比较。并将遍历到的节点输出。直到队列中所有节点输出。

2.1 具体看题:

一个完整的软件项目往往会包含很多由代码和文档组成的源文件。编译器在编译整个项目的时候,可能需要按照依赖关系来依次编译每个源文件。比如,A.cpp 依赖 B.cpp,那么在编译的时候,编译器需要先编译 B.cpp,才能再编译 A.cpp。 假设现有 0,1,2,3 四个文件,0号文件依赖1号文件,1号文件依赖2号文件,3号文件依赖1号文件,则源文件的编译顺序为 2,1,0,3 或 2,1,3,0。现给出文件依赖关系,如 1,2,-1,1,表示0号文件依赖1号文件,1号文件依赖2号文件,2号文件没有依赖,3号文件依赖1号文件。请补充完整程序,返回正确的编译顺序。注意如有同时可以编译多个文件的情况,按数字升序返回一种情况即可,比如前述案例输出为:2,1,0,3

输入例子1:

"1,2,-1,1"

输出例子1:

"2,1,0,3"

三、C++代码模板: 

class Solution {
public:
    string compileSeq(string input) {
        //首先完成有向无环图的构建
        //统计图中各节点的个数 并标记头节点为-1
        //使用优先队列 按照先小后大的顺序遍历输出节点值
        
        int len = input.size();
        
        /*********构建有向无环图(指向关系)*********/
        map<int, vector<int>> mp;//first为先 second为后, 也就是second依赖于first
        string tmp;
        int idx = 0;
        for(auto& s:input){
            if(s != ',')
                tmp += s;
            else{
                mp[stoi(tmp)].push_back(idx++);
                string().swap(tmp);//清空string
            }
        }
        if(!tmp.empty())
            mp[stoi(tmp)].push_back(idx++);
        
        /**********统计各节点个数 并保存头节点***********/
        vector<int> indexcount(len, 0);//统计各节点个数
        priority_queue<int, vector<int>, greater<>> pq;//保存节点
        for(auto& m:mp){
            if(m.first == -1){
                for(auto& a:m.second){
                    pq.push(a);
                    indexcount[a] = -1;
                }
            }else{
                for(auto& a:m.second)
                    ++indexcount[a];
            }
        }
        
        /************根据指向关系遍历图 并按照优先队列输出结果********/
        vector<int> ans;
        while(!pq.empty()){
            int node = pq.top();
            pq.pop();//输出了就需要从优先队列中弹出
            ans.push_back(node);
            for(auto& m:mp[node]){
                if(--indexcount[m] == 0)//如果该节点是最后一次在图中出现 则放入队列中
                    pq.push(m);
            }
        }
        
        /***********输出结果************/
        string res;
        for(auto& i:ans){
            res += to_string(i);
            res.push_back(',');
        }
        if(!res.empty())
            res.pop_back();
        
        return res;
    }
};

 

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
MFC(Microsoft Foundation Classes)是Microsoft公司开发的一套用于Windows应用程序开发的C++类库,其中包括了许多模板类,如CArray、CList、CMap等。这些模板类可以大大简化开发过程,提高程序效率。 下面以CArray类为例,介绍MFC模板类的代码讲解: ```c++ template<class TYPE, class ARG_TYPE = const TYPE&> class CArray { public: // 构造函数 CArray() noexcept; CArray(int nSize, int nGrowBy = -1); CArray(const CArray& other); CArray(CArray&& other) noexcept; // 析构函数 ~CArray(); // 重载运算符 CArray& operator=(const CArray& other); CArray& operator=(CArray&& other) noexcept; TYPE& operator[](int nIndex); const TYPE& operator[](int nIndex) const; // 其他成员函数 int GetSize() const noexcept; int GetUpperBound() const noexcept; void SetSize(int nNewSize, int nGrowBy = -1); void SetAt(int nIndex, ARG_TYPE newElement); void Add(ARG_TYPE newElement); void RemoveAll() noexcept; void RemoveAt(int nIndex); void InsertAt(int nIndex, ARG_TYPE newElement, int nCount = 1); void Append(const CArray& src); void Copy(const CArray& src); void FreeExtra(); void AssertValid() const; // 迭代器相关 typedef TYPE* iterator; typedef const TYPE* const_iterator; iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; // 受保护的成员变量 protected: TYPE* m_pData; // 数据指针 int m_nSize; // 数组大小 int m_nMaxSize; // 数组最大大小 int m_nGrowBy; // 每次增长的大小 // 私有成员函数 private: void SetGrow(int nNewGrowBy); void SetSizeInternal(int nNewSize); void SetSizeInternalGrow(int nNewSize); }; ``` 上述代码展示了CArray模板类的声明,在实际使用中,我们可以将模板类实例化为不同类型的数组,如: ```c++ CArray<int> arr; // 创建一个int类型的数组 CArray<CString> strArr; // 创建一个CString类型的数组 ``` 其中,CArray的模板参数TYPE表示数组元素的类型,ARG_TYPE表示传入数组元素的类型。CArray提供了一系列成员函数,包括构造函数、析构函数、重载运算符、其他成员函数以及迭代器相关的函数。 通过使用MFC提供的模板类,我们可以快速、方便地实现各种数据结构的操作,提高开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子木呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值