C++算法:图中的最短环

news/2024/7/15 18:31:36 标签: 算法, c++, 数据结构, 图论, BFS, 广度有序搜索,

题目

现有一个含 n 个顶点的 双向 图,每个顶点按从 0 到 n - 1 标记。图中的边由二维整数数组 edges 表示,其中 edges[i] = [ui, vi] 表示顶点 ui 和 vi 之间存在一条边。每对顶点最多通过一条边连接,并且不存在与自身相连的顶点。
返回图中 最短 的长度。如果不存在,则返回 -1 。
是指以同一节点开始和结束,并且路径中的每条边仅使用一次。
2 <= n <= 1000
1 <= edges.length <= 1000
edges[i].length == 2
0 <= ui, vi < n
ui != vi
不存在重复的边

分析

在这里插入图片描述

返回还是真

利用BFS求到A的最短距离,B和C到A的距离都为1,处理BC是发现B和C都已经和A连通,说明存在。注意:求EFG到点D的距离,处理完DE ED EF FE FG后,处理GF,发现F和G都和D连通。判断是返回,还是真有两种思路:
一,记录已经使用的双向边,枚举新边的时候,忽略。此方案容易理解。
二,记录各点的最短距离的前一点。此方案性能。

BFS_18">各点都要BFS

如果以H为源点,则最短的长4。以k为源点,最短的是3。

多个连通区域

由于所有点都会作为起点,所以所有点都会处理。和起点不连通的点不会重复处理。

不会遗漏任意

某个包括x的奇数长度的,假定其长度为len2+1,上有两个点距离x为len,假定先处理的为x1,后处理的为x2。处理x1->x2是发现此。假定此长为偶数,假定其长度为len2+2。上有两个点距离x为len,假定先处理的为x1,后处理的为x2。距离x为len+1的点为x3,则处理x2->x3时,发现此

不会误判

发现cur和next都和源点连通,那说明next在cur之前已经处理,也就是vDis[next] <= vDis[cur]。vDis[next]不会比v[cur]小2,否则源点->next->cur更短。也就是vDis[next]和vDis[cur]相等或少1。源点到next的最短路径,不会包括cur,否则vDis[next]大于v[cur]。两者相等的情况,cur的最短路径不会包括next。少1的情况,如果cur的最短路径包括next,则最后一条边是next->cur。

方案一代码

class Solution {
public:
int findShortestCycle(int n, vector<vector>& edges) {
CNeiBo2 neiBo(n, edges, false);
for (int i = 0; i < n; i++)
{
Do(neiBo.m_vNeiB, i);
}
return (INT_MAX == m_iMinCycle) ? -1 : m_iMinCycle;
}
void Do(const vector<vector>& vNeiB, int src)
{
int n = vNeiB.size();
vector<unordered_set> setHas(n);
vector vDis(n, -1);
queue q;
vDis[src] = 0;
q.emplace(src);
while (q.size())
{
const auto cur = q.front();
q.pop();
for (const auto& next : vNeiB[cur])
{
if (setHas[next].count(cur))
{
continue;
}
setHas[cur].emplace(next);
if (-1 != vDis[next])
{
m_iMinCycle = min(m_iMinCycle, vDis[cur] + vDis[next] + 1);
continue;
}
vDis[next] = vDis[cur] + 1;
q.emplace(next);
}
}
}
int m_iMinCycle = INT_MAX;
};

方案二代码

class Solution {
public:
  int findShortestCycle(int n, vector<vector<int>>& edges) {
    CNeiBo2 neiBo(n, edges, false);
    for (int i = 0; i < n; i++)
    {
      Do(neiBo.m_vNeiB, i);
    }
    return (INT_MAX == m_iMinCycle) ? -1 : m_iMinCycle;
  }
  void Do(const vector<vector<int>>& vNeiB, int src)
  {
    int n = vNeiB.size();
    vector<int> vDis(n, -1), vPre(n,-1);
    queue<int> q;
    vDis[src] = 0;
    vPre[src] = -1;
    q.emplace(src);
    while (q.size())
    {
      const auto cur = q.front();
      q.pop();
      for (const auto& next : vNeiB[cur])
      {   
        if (-1 != vDis[next])
        {
          if (vPre[cur] != next)
          {
            m_iMinCycle = min(m_iMinCycle, vDis[cur] + vDis[next] + 1);
          }
          continue;
        }
        vDis[next] = vDis[cur] + 1;
        vPre[next] = cur;
        q.emplace(next);
      }
    }   
  }
  int m_iMinCycle = INT_MAX;
};

方案三

方案一和方案二时间复杂度都是O(n^2),方案一比方案二慢。方案三相比方案一,稍稍提速。
void Do(const vector<vector>& vNeiB, int src)
{
int n = vNeiB.size();

vector<int> vDis(n, -1);
queue<int> q;
vDis[src] = 0;
q.emplace(src);
while (q.size())
{
  const auto cur = q.front();
  q.pop();
  for (const auto& next : vNeiB[cur])
  {
    if (m_vHasDo[next][cur])
    {
      continue;
    }
    m_vHasDo[cur][next] = 1;
    if (-1 != vDis[next])
    {
      m_iMinCycle = min(m_iMinCycle, vDis[cur] + vDis[next] + 1);
      continue;
    }
    vDis[next] = vDis[cur] + 1;
    q.emplace(next);
  }
}

}

2023年4月版本

class Solution {
public:
int findShortestCycle(int n, vector<vector>& edges) {
m_iN = n;
m_vNeiB.resize(n);
for (const auto&v : edges)
{
m_vNeiB[v[0]].emplace_back(v[1]);
m_vNeiB[v[1]].emplace_back(v[0]);
}

	for (int i = 0; i < n; i++)
	{
		bfs(i);
	}
	return (INT_MAX == m_iRet) ? -1 : m_iRet;
}
void bfs(int iRoot)
{
	std::vector<int> vDis(m_iN, -1);
	vDis[iRoot] = 0;
	std::queue<pair<int,int>> que;
	que.emplace(iRoot, -1);//当前节点,父节点
	while (que.size())
	{
		const int iPre = que.front().first;
		const int iPrePre = que.front().second;
		que.pop();
		for (const auto& next : m_vNeiB[iPre])
		{
			if (-1 == vDis[next])
			{
				vDis[next] = vDis[iPre] + 1;
				que.emplace(next, iPre);
			}
			else
			{
				if (next == iPrePre)
				{
					continue;
				}
				m_iRet = min(m_iRet, vDis[iPre] + 1 + vDis[next]);
			}
		}
		
	}
}
vector<std::vector<int>> m_vNeiB;

int m_iN;
int m_iRet = INT_MAX;

};

其它

视频课程

如果你觉得复杂,想从简单的算法开始,可以学习我的视频课程。
https://edu.csdn.net/course/detail/38771
我的其它课程
https://edu.csdn.net/lecturer/6176

测试

win7 VS2019 C++17

相关下载

算法精讲《闻缺陷则喜算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653


http://www.niftyadmin.cn/n/5083599.html

相关文章

《进化优化》第3章 遗传算法

文章目录 3.1 遗传学的历史3.2 遗传学3.3 遗传学的历史3.4 一个简单的二进制遗传算法3.4.1 用于机器人设计的遗传算法3.4.2 选择与交叉3.4.3 变异3.4.5 遗传算法参数调试 3.5 简单的连续遗传算法 遗传算法模仿自然选择来解决优化问题。 为研究遗传算法&#xff0c;得遵守自然选…

uniapp 返回上一步携带参数

1. 下一步 // 返回上一页setTimeout(() > {let pages getCurrentPages();let prevPage pages[pages.length - 2];prevPage.$vm.schoolName this.formList;uni.navigateBack({delta: 1});}, 1000) 2. 返回上一步, 携带参数 // 获取下一步返回的数据onShow() {let pages …

CentOS 7下JumpServer安装及配置(超详细版)

前言 Jumpserver是一种用于访问和管理远程设备的Web应用程序&#xff0c;通常用于对服务器进行安全访问。它基于SSH协议&#xff0c;提供了一个安全和可管理的环境来管理SSH访问。Jumpserver是基于Python开发的一款开源工具&#xff0c;其提供了强大的访问控制功能&#xff0c;…

小程序使用uni.createAnimation只执行一次的问题

思路&#xff1a; 在页面创建的时候&#xff0c;创建一个临时动画对象调用 step() 来表示一组动画完成通过动画实例的export方法导出动画数据传递给组件的animation属性还原动画页面卸载的时候&#xff0c;清除动画数据 <template><view class"content"&g…

使用 Python 中的小波变换信号驾驭股票价格的波动

一、简介 股票上涨和下跌,创造出像海浪一样难以预测的模式和走势。然而,就像科学家通过了解下面的水流来预测波浪的运动一样,我们也可以使用类似的工具破译股票市场的一些模式。 通过利用小波变换的力量,我们深入表面,试图揭示驱动股价的深层原因。这段旅程不仅仅涉及数字…

Elasticsearch 和 Arduino:一起变得更好!

作者&#xff1a;Enrico Zimuel 使用 Arduino IoT 设备与 Elasticsearch 和 Elastic Cloud 进行通信的简单方法 在 Elastic&#xff0c;我们不断寻找简化搜索体验的新方法&#xff0c;并开始关注物联网世界。 来自物联网的数据收集可能非常具有挑战性&#xff0c;尤其是当我们…

深入理解强化学习——标准强化学习和深度强化学习

分类目录&#xff1a;《深入理解强化学习》总目录 强化学习的历史 早期的强化学习&#xff0c;我们称其为标准强化学习。最近业界把强化学习与深度学习结合起来&#xff0c;就形成了深度强化学习&#xff08;Deep ReinforcemetLearning&#xff09;。因此&#xff0c;深度强化…

366. 寻找⼆叉树的叶⼦节点

366. 寻找⼆叉树的叶⼦节点 这道题混用二叉树递归 「遍历」和「分解问题」 两种思维模式。 class FindLeaves:"""366. 寻找⼆叉树的叶⼦节点https://leetcode.cn/problems/find-leaves-of-binary-tree/"""def solution(self, root):self.res …