C++算法:有向图计数优化版原理及实现

简介: C++算法:有向图计数优化版原理及实现

题目

见前面章节。有向图访问计数的原理及C++实现-CSDN博客

第一版

不需要拓扑排序,也不需要并集查找,直接dfs了。完成以下三个职责:

一,DFS那些端点在环上。

二,DFS环上各点此环的长度。

三,DFS非环上各点。

分析

cur是当前dfs的节点,next为edges[cur]。从后向前分析:

判定处理

ret的值

返回值

找到环尾

ret [cur] = NO - mPreNO[cur]

cur

找到环尾,没找到环首

ret [cur] = ret [next]

同dfs(next...)

之前找到环尾和当前环首

环尾已处理,无需处理

-1

之前找到首尾

ret [cur] = ret [next]+1

-1

判定表

条件一

条件二

结果

mPreNO.count(cur)

找到环尾

dfs(next)返回非-1

cur不等于dfs(next)

找到环尾,没找到环首

cur等于dfs(next)

之前找到环尾和当前环首

dfs(next)返回非-1

之前找到首尾

DSF0过程

DFS(0)

不处理

return -1

DFS(1)

ret[1]=2

return 0

DFS(0)

ret[0]=3-1=2

return 0

DFS(1)过程

DFS(1)

不处理

return -1

DFS(0)

ret[0]=2

return 0

DFS(1)

ret[1]=3-1=2

return 0

FFS(2)过程

DFS(2)

ret[2]=3

Return -1

DFS(0)

不处理

return -1

DFS(1)

ret[1]=2

return 0

DFS(0)

ret[0]=3-1=2

return 0

FFS(4)过程

DFS(4)

ret[4]=3

Return -1

DFS(0)

不处理

return -1

DFS(1)

ret[1]=2

return 0

DFS(0)

ret[0]=3-1=2

return 0

FFS(3)过程

DFS(3)

Ret[3]=4

Return -1;

DFS(2)

ret[2]=3

Return -1

DFS(0)

不处理

return -1

DFS(1)

ret[1]=2

return 0

DFS(0)

ret[0]=3-1=2

return 0

核心代码

class Solution {
public:
    vector<int> countVisitedNodes(vector<int>& edges) {
        m_c = edges.size();
        m_edges = edges;
        m_vRet.assign(m_c, -1);
        for (int i = 0; i < m_c; i++)
        {
            std::unordered_map<int, int> mPreNO;
            dfs(i, mPreNO, 1);
        }
        return m_vRet;
    }
    int dfs(int cur,std::unordered_map<int,int>& mPreNO,int iNO)
    {
        if (mPreNO.count(cur))
        {
            m_vRet[cur] = iNO - mPreNO[cur];
            return cur;
        }
        mPreNO[cur] = iNO;
        const auto& next = m_edges[cur];
        const int iRet = dfs(next, mPreNO, iNO + 1);
        if (iRet == cur)
        {
            return -1;//环结束了
        }
        if (-1 == iRet)
        {
            m_vRet[cur] = m_vRet[next]+1;
        }
        else
        {
            m_vRet[cur] = m_vRet[next];
        }
        return iRet;
    }
    vector<int> m_vRet;
    vector<int> m_edges;
    int m_c;
};

记忆化

如果ret[cur]不为-1,说明cur已经处理。如果cur是环上一点,那说明整个环已经处理,返回-1;如果cur,不是环上一点,也返回-1。

时间复杂度

O(n),任意端点,dfs最多执行两次,一次是主动执行,一次是作为出边被执行。

优化后的代码

class Solution {
public:
    vector<int> countVisitedNodes(vector<int>& edges) {
        m_c = edges.size();
        m_edges = edges;
        m_vRet.assign(m_c, -1);
        for (int i = 0; i < m_c; i++)
        {
            std::unordered_map<int, int> mPreNO;
            dfs(i, mPreNO, 1);
        }
        return m_vRet;
    }
    int dfs(int cur,std::unordered_map<int,int>& mPreNO,int iNO)
    {
        if (-1 != m_vRet[cur])
        {
            return -1;
        }
        if (mPreNO.count(cur))
        {
            m_vRet[cur] = iNO - mPreNO[cur];
            return cur;
        }
        mPreNO[cur] = iNO;
        const auto& next = m_edges[cur];
        const int iRet = dfs(next, mPreNO, iNO + 1);
        if (iRet == cur)
        {
            return -1;//环结束了
        }
        if (-1 == iRet)
        {
            m_vRet[cur] = m_vRet[next]+1;
        }
        else
        {
            m_vRet[cur] = m_vRet[next];
        }
        return iRet;
    }
    vector<int> m_vRet;
    vector<int> m_edges;
    int m_c;
};

再次优化后的代码

用数组代替哈希映射,速度似乎没提升。

class Solution {
public:
    vector<int> countVisitedNodes(vector<int>& edges) {
        m_c = edges.size();
        m_edges = edges;
        m_vRet.assign(m_c, -1);
        int vPreNO[100000];
        for (int i = 0; i < m_c; i++)
        {
            vPreNO[i] = -1;
        }
        for (int i = 0; i < m_c; i++)
        {
            dfs(i, vPreNO, 1);
        }
        return m_vRet;
    }
    int dfs(int cur,int* vPreNO,int iNO)
    {
        if (-1 != m_vRet[cur])
        {
            return -1;
        }
        if (-1 != vPreNO [cur])
        {
            m_vRet[cur] = iNO - vPreNO[cur];
            return cur;
        }
        vPreNO[cur] = iNO;
        const auto& next = m_edges[cur];
        const int iRet = dfs(next, vPreNO, iNO + 1);
        if (iRet == cur)
        {
            return -1;//环结束了
        }
        if (-1 == iRet)
        {
            m_vRet[cur] = m_vRet[next]+1;
        }
        else
        {
            m_vRet[cur] = m_vRet[next];
        }
        return iRet;
    }
    vector<int> m_vRet;
    vector<int> m_edges;
    int m_c;
};

注意

如果用vector<int>记录PreNO,则需要在for循环外初始化,如果for循环内初始化,则时间复杂度变为O(n*n)。

测试环境

VS2022 Win10 C++17

下载

源码下载:

【免费】.有向图计数优化版原理及C++实现资源-CSDN文库


其它

视频课程

如果你觉得复杂,想从简单的算法开始,可以学习我的视频课程。

https://edu.csdn.net/course/detail/38771

我的其它课程

https://edu.csdn.net/lecturer/6176

测试环境

win7 VS2019 C++17 或Win10 VS2022 Ck++17

相关下载

算法精讲《闻缺陷则喜算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

相关文章
|
1月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
机器学习/深度学习 算法 自动驾驶
489 0
|
2月前
|
机器学习/深度学习 算法 搜索推荐
从零开始构建图注意力网络:GAT算法原理与数值实现详解
本文详细解析了图注意力网络(GAT)的算法原理和实现过程。GAT通过引入注意力机制解决了图卷积网络(GCN)中所有邻居节点贡献相等的局限性,让模型能够自动学习不同邻居的重要性权重。
452 0
从零开始构建图注意力网络:GAT算法原理与数值实现详解
|
3月前
|
机器学习/深度学习 算法 文件存储
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
神经架构搜索(NAS)正被广泛应用于大模型及语言/视觉模型设计,如LangVision-LoRA-NAS、Jet-Nemotron等。本文回顾NAS核心技术,解析其自动化设计原理,探讨强化学习、进化算法与梯度方法的应用与差异,揭示NAS在大模型时代的潜力与挑战。
850 6
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
|
3月前
|
传感器 算法 定位技术
KF,EKF,IEKF 算法的基本原理并构建推导出四轮前驱自主移动机器人的运动学模型和观测模型(Matlab代码实现)
KF,EKF,IEKF 算法的基本原理并构建推导出四轮前驱自主移动机器人的运动学模型和观测模型(Matlab代码实现)
129 2
|
3月前
|
算法
离散粒子群算法(DPSO)的原理与MATLAB实现
离散粒子群算法(DPSO)的原理与MATLAB实现
187 0
|
4月前
|
机器学习/深度学习 人工智能 编解码
AI视觉新突破:多角度理解3D世界的算法原理全解析
多视角条件扩散算法通过多张图片输入生成高质量3D模型,克服了单图建模背面细节缺失的问题。该技术模拟人类多角度观察方式,结合跨视图注意力机制与一致性损失优化,大幅提升几何精度与纹理保真度,成为AI 3D生成的重要突破。
403 0
|
4月前
|
算法 区块链 数据安全/隐私保护
加密算法:深度解析Ed25519原理
在 Solana 开发过程中,我一直对 Ed25519 加密算法 如何生成公钥、签名以及验证签名的机制感到困惑。为了弄清这一点,我查阅了大量相关资料,终于对其流程有了更清晰的理解。在此记录实现过程,方便日后查阅。
510 0
|
4月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
148 0
|
5月前
|
消息中间件 存储 缓存
zk基础—1.一致性原理和算法
本文详细介绍了分布式系统的特点、理论及一致性算法。首先分析了分布式系统的五大特点:分布性、对等性、并发性、缺乏全局时钟和故障随时发生。接着探讨了分布式系统理论,包括CAP理论(一致性、可用性、分区容错性)和BASE理论(基本可用、软状态、最终一致性)。文中还深入讲解了两阶段提交(2PC)与三阶段提交(3PC)协议,以及Paxos算法的推导过程和核心思想,强调了其在ZooKeeper中的应用。最后简述了ZAB算法,指出其通过改编的两阶段提交协议确保节点间数据一致性,并在Leader故障时快速恢复服务。这些内容为理解分布式系统的设计与实现提供了全面的基础。