C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

这篇具有很好参考价值的文章主要介绍了C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 前言

对于基环树的讲解,分上、下 2 篇,上篇以理解连通分量、环以及使用深度搜索算法检查连通性和环为主,下篇以基于基环树结构的应用为主。

什么是基环树?

所谓基环树指由n个节点n条边所构建而成的连通图

如下图所示,树结构中共有 7 个节点, 6 条边。此时在树结构上添加一条边,必然会形成一个树环。因树结构中有环,故得此名。基环树也称为环套树。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

如下图基环树结构中有 7 个节点,7 条边。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

上述为无向边基环树。针对于有向边,基环树分:

  • 内向树:树中每个点有且仅有一条出边(或者说每个节点的出度为 1)。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • 外向树:树中每个点有且仅有一条入边(或者说每个点的入度为 1)。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

基于基环树有一项基本操作,寻找基环树上的

下文将深入讲解如何使用深度搜索算法在无向图中查找环结构。

2. 查找环

在图中查找的常用的算法有:

  • 深度搜索算法。
  • 广度搜索算法。
  • 并查集。
  • 拓扑排序算法。

广度深度搜索是原生算法,面对较复杂的图结构时,略显拙劣。较优的方案是使用优化过的并查集拓扑排序算法,其在性能和技巧上都很精妙。

Tips: 关于并查集和拓扑排序算法可查阅我的相关博文。

2.1 问题分析

起点和终点相同的路径称为回路或称为环(Cycle),除第一个顶点与最后一个顶点之外,其他顶点不重复出现的回路称为简单回路,本文仅对简单回路进行讨论。

Tips: 一般而言,回路至少需要 3 个顶点。

图中是否有环的前提条件:边数至少要等于顶点数。

所以对于有 n个顶点的无向(有向)图,是否存在环,可以先检查边的数量 m与顶点数量之间的关系。

Tips: 本文主要以无向图为讨论对象。

  1. 如果 m=n-1。可以是下面的几种情况。
  • 一个连通分量图情况。可以理解为以任何一个顶点为根节点构建成的树结构,此时连通分量为1,显然此情况无法成环。

    Tips: 所谓连通图,指任意两个顶点之间都有路径的图。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • 上文说过,如果存在回路,顶点数量和边数至少要相等,这句话不能狭义地诠释为如果边数小于顶点数,则图中无环。

    m=n-1时,顶点数可以拆分成 n=m+1。这里遵循一个拆分原则,尽可能在已有边数的基础上成全图中某些顶点成环的要求。

    如下图所示,连通分量有 2个,其中一个子图中存在一个环。

    所以当边数少于顶点数时,需要讨论是否存在子图。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

也可以是如下几种图形:

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  1. m<n-1时,如果 m<=2则无法构建成环。其它情况下都能构建出一个有环的子图。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  1. m>=n 因为边数已经大于顶点数,显然图中有环。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

2.2 深度搜索算法思想

2.2.1 连通分量

可以使用深度搜索算法查找图中是否有环,先了解一下深度搜索算法的搜索流程。

Tips: 深度搜索算法可以使用递归和非递归实现。本质是使用栈进行数据存储。

  • 先创建一个栈(非递归实现),用来存储搜索到的顶点。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • A顶点为出发点,且把A顶点压入栈中,用于初始化栈。并在图的A顶点上做已入栈的标志。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • 查询出与A顶点相邻的顶点B、E,并且压入栈中。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • 继续查询与E顶点相邻的顶点(即入栈操作完成后,再查询与栈顶元素相邻的顶点)D,且压入栈中。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • 检查与D顶点相邻的顶点C,且压入栈中。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

经过上述操作后,可发现图结构中所有顶点全部被压入栈中,此时,能证明什么?

至少有一点是可以证明的:栈中的顶点在一个集合或在一个连通分量上。

使用如上搜索方案时,是否能找到此连通分量上的所有顶点?

先看下图的搜索结果:

当查询与 D顶点相邻的顶点时,因 DC没有连通性,故C无法入栈。栈中的顶点在一个连通分量上,是不容质疑的,而实际上 C也是此连通分量上的一份子。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

所以,使用仅入栈不出栈的搜索方案,无法找到同一个连通分量上的所有顶点。

可以把深度搜索方案改一下,采用入栈、出栈形式。

  • 初始 A入栈。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • A出栈,找到与A相邻的顶点B、E,且入栈。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • E出栈,找到与E相邻的D顶点,且让其入栈。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • D出栈,因没有与D相邻的顶点(每个顶点只能入一次栈)可入栈。继续B出栈,因C是与之相邻的顶点且没有入过栈,C入栈。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

  • 最后C出栈,栈空。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

至此,可以得到一个结论:

  • 在一次深度搜索过程中,入过栈的顶点都在一个集合中(或一个连通分量上)。
  • 使用出栈、入栈方案时,可以保证搜索到一个连通分量上的所有顶点。

**Tips: **使用广度搜索一样能找到一个连通分量上的所有顶点。

原理很简单,深度搜索是按纵深方式进行搜索的(类似于一条线上的蚂蚱),在互相有关联的纵深线上的顶点能被搜索到。

如下图所示:从A开始,有左、右两条纵深线,在搜索完左边的纵深线后。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

可以继续搜索另一条纵深线。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

一次深度搜索只能检查到一条连通分量。如果图中存在多个连通分量时,需要使用多次深度搜索算法。如下图所示:

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

连通分量与找环有什么关系?

连通分量与环之间有很一个简单的关系:一定是存在一个连通分量上。找环之前,先要确定目标顶点是不是在一个连通分量上。否则,都不在一起,还找什么环?

是否在一个连通分量上的顶点一定有环?

如下图,所有顶点都在一个连通分量中,实际情况是,图没有环。所以,仅凭顶点全部在一个连通分量上,是无法得到图中一定有环的结论。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

那么,使用深度搜索算法在图中搜索时,怎么证明图中有环?

根据前面的推断,可以得出结论:

  • 所有搜索的顶点在一个连通分量上,且图或子图边数大于或等于顶点数时,那么图或子图中必然存在环。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

那么?环上的顶点有什么特点?

如果图中存在环,那么,环上每个顶点必然至少有 2条边分别和环上另 2 个顶点相连,边的数量决定与其相邻顶点的数量。

Tips:道理很简单,如果有 n个人通过手牵手围成一个圈,如果其中有一个人只原意出一只手,则圈是无法形成的。 可以把此人移走,剩余人可以围成一个圈。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

2.2.2 小结

查询环上的顶点时,需要满足如下几个要求:

  • 所有顶点在一个连通分量上。
  • 连通分量上的边数要大于或等于顶点数。
  • 环上至少有 2 条边的顶点可认定是环上的顶点。

2.3 编码实现

顶点类:

#include <iostream>
#include <vector>
#define MAXVERTEX  10
using namespace std;
/*
*顶点类
*/
struct Vertex {
	//编号
	int vid;
	//数据
	char val;
	//与之相邻的边数
	int edges;
	//是否访问过
	bool isVisited;
	//前驱节点编号 
	int pvid; 
    
	Vertex() {
		this->vid=-1;
		this->edges=0;
		this->isVisited=false;
	}

	Vertex(int vid,char val) {
		this->vid=vid;
		this->edges=0;
		this->val=val;
		this->isVisited=false;
	}
};

图类: 先在图类提供常规API(对顶点的维护函数,添加顶点和添加顶点之间的关系)

/*
* 图类
*/
class Graph {
	private:
		//所有顶点
		Vertex allVertex[MAXVERTEX];
		//二维矩阵,存储顶点关系(边)
		int matrix[MAXVERTEX][MAXVERTEX];
		//顶点数量
		int num;
	public:
		Graph() {
			this->num=0;
			for(int i=0; i<MAXVERTEX; i++) {
				for(int j=0; j<MAXVERTEX; j++) {
					this->matrix[i][j]=0;
				}
			}
		}
		/*
		*添加顶点,返回顶点编号
		*/
		int addVertex(char val) {
			Vertex ver(this->num,val);
			this->allVertex[this->num]=ver;
			return this->num++;
		}

		/*
		*添加顶点之间的关系
		*/
		void addEdge(int from,int to) {
			//无向图中,需要双向添加
			this->allVertex[from].edges++;
			this->matrix[from][to]=1;
			this->allVertex[to].edges++;
			this->matrix[to][from]=1;
		}

		/*
		*深度搜索算法找环
		*/
		void findCycle() { }
		/*
		*显示矩阵中边信息
		*/
		void showEdges() {
			for(int i=0; i<this->num; i++) {
				for(int j=0; j<this->num; j++) {
					cout<<this->matrix[i][j]<<"\t";
				}
				cout<<endl;
			}
		}
};

下图的子图A、B、E、D就是基环树。现使用代码构建下图:

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

//测试
int main() {
	Graph graph;//=new Graph();
	//添加顶点
	int aid= graph.addVertex('A');
	int bid= graph.addVertex('B');
	int cid= graph.addVertex('C');
	int did= graph.addVertex('D');
	int eid= graph.addVertex('E');
	//添加顶点之间关系
	graph.addEdge(aid,bid);  //A ---- B
	graph.addEdge(aid,eid);  //A  --- E
	graph.addEdge(bid,eid);  //B ---- E
	graph.addEdge(did,eid);  // E----D
	graph.showEdges();
	return 0;
}

确认图的构建是否正确。

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

核心函数: 使用深度搜索算法检查图中是否存在环。

/*
*深度搜索算法找环
*/
void findCycle(int from) {
    //记录连通分量中的边数
    int sides=0;
    //栈
    stack<int> myStack;
    //初始化栈
    myStack.push(from);
    //标志已经入过栈
    this->allVertex[from].isVisited=true;
    //存储搜索过的顶点
    vector<int> vers;

    //出栈操作
    while( !myStack.empty() ) {
        //出栈
        int vid= myStack.top();
        //存储搜索顶点
        vers.push_back(vid);
        //删除
        myStack.pop();
        //检查相邻节点
        for(int i=0; i<this->num; i++ ) {
            if( this->matrix[vid][i]==1 ) {
                // i 是 from 的相邻顶点
                sides++;
                //标志此边已经被记录
                this->matrix[vid][i]=-1;
                if(this->allVertex[i].isVisited==false ) {
                    //边对应顶点是否入过栈
                    myStack.push(i);
                    this->allVertex[i].isVisited=true;
                }
            }
        }
    }

    //输出搜索结果
    cout<<"输出连通分量中的顶点:"<<endl;
    for(int i=0; i<vers.size(); i++)
        cout<< this->allVertex[vers[i]].val<<"\t";
    cout<<endl;
    //存储搜索结果 
    allConns.push_back(vers);

    //检查环
    if(sides>=vers.size() && vers.size()>3 )  {
        //边数大于或等于搜索过的顶点数。表示搜索过的顶点在一个集合中,且有环
        cout<<"存在环:"<<endl;
        for(int i=0; i<vers.size(); i++) {
            if( this->allVertex[vers[i]].edges>=2 ) {
                cout<<this->allVertex[vers[i]].val<<"->\t";
            }
        }
        cout<<endl;
    }

    //检查是否还有其它连通分量
    for(int i=0; i<this->num; i++) {
        bool isExist=false;
        //是否已经搜索
        for(int j=0; j<allConns.size(); j++) {
            auto res = find( begin( allConns[j] ), end( allConns[j] ),this->allVertex[i].vid  );
            if(res!=end(allConns[j] )  ) {
                //搜索过
                isExist=true;
                break;
            }
        }
        if(!isExist) {
            findCycle(this->allVertex[i].vid );
        }
    }
}
//显示图中所有连通分量
void showConn() {
    cout<<"图中存在"<<allConns.size()<<"个连通分量"<<endl;
}

测试:

int main() {
    //省略……
	graph.showEdges();
	graph.findCycle(0);
	graph.showConn();
	graph.showEdges();
	return 0;
}

输出结果:

C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节

3. 总结

本文讲解怎么使用深度搜索算法在无向图中查找环,当然,也可以使用广度搜索算法实现。

检查图中连通性的最好的方案是使用并查集。文章来源地址https://www.toymoban.com/news/detail-430804.html

到了这里,关于C++ 树进阶系列之探讨深度搜索算法查找环的细枝末节的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【经典LeetCode算法题目专栏分类】【第6期】二分查找系列:x的平方根、有效完全平方数、搜索二位矩阵、寻找旋转排序数组最小值

    《博主简介》 小伙伴们好,我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌ 更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍 感谢小伙伴 们点赞、关注! class   Solution :      def   mySqrt ( self ,  x :   int )   -   int :       

    2024年02月04日
    浏览(65)
  • C++深度优先搜索(DFS)算法的应用:树中可以形成回文的路径数

    深度优先搜索(DFS) 状态压缩 给你一棵 树(即,一个连通、无向且无环的图),根 节点为 0 ,由编号从 0 到 n - 1 的 n 个节点组成。这棵树用一个长度为 n 、下标从 0 开始的数组 parent 表示,其中 parent[i] 为节点 i 的父节点,由于节点 0 为根节点,所以 parent[0] == -1 。 另给你一

    2024年02月04日
    浏览(42)
  • 关联规则挖掘:Apriori算法的深度探讨

    在本文中,我们深入探讨了Apriori算法的理论基础、核心概念及其在实际问题中的应用。文章不仅全面解析了算法的工作机制,还通过Python代码段展示了具体的实战应用。此外,我们还针对算法在大数据环境下的性能局限提出了优化方案和扩展方法,最终以独到的技术洞见进行

    2024年02月05日
    浏览(68)
  • 大数据关联规则挖掘:Apriori算法的深度探讨

    在本文中,我们深入探讨了Apriori算法的理论基础、核心概念及其在实际问题中的应用。文章不仅全面解析了算法的工作机制,还通过Python代码段展示了具体的实战应用。此外,我们还针对算法在大数据环境下的性能局限提出了优化方案和扩展方法,最终以独到的技术洞见进行

    2024年01月24日
    浏览(265)
  • LeetCode-1483. 树节点的第 K 个祖先【树 深度优先搜索 广度优先搜索 设计 二分查找 动态规划】

    给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。 树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。 实现 TreeAncestor 类: TreeAncestor(int n, int[] parent) 对树和父

    2024年04月16日
    浏览(39)
  • 【数据结构(C++)】树型查找——二叉搜索树

    目录 1. 二叉搜索树 1.1 二叉搜索树的概念 1.2 二叉搜索树类模板 1.3 二叉搜索树的操作 1.3.1 查找 1.3.2 插入 1.3.3 删除 1.4 二叉搜索树的性能分析 2. 平衡二叉树 2.1 平衡二叉树的概念 2.2 平衡二叉树类模板 2.3 二叉搜索树的插入 3. 红黑树 3.1 红黑树的概念 3.2 红黑树类模板 二叉搜索

    2024年02月10日
    浏览(43)
  • 【算法系列 | 11】深入解析查找算法之—插值查找

    心若有阳光,你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。 我们一起努力,成为更好的自己! 今天第11讲,讲一下查找算法的—插值查找算法 查找算法是计算机科学中的

    2024年02月03日
    浏览(55)
  • 【算法系列 | 8】深入解析查找算法之—二分查找

    心若有阳光,你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。 我们一起努力,成为更好的自己! 今天第8讲,讲一下查找算法的二分查找 查找算法是很常见的一类问题,主

    2024年02月07日
    浏览(55)
  • 【算法系列 | 10】深入解析查找算法之—线性查找

    心若有阳光,你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。 我们一起努力,成为更好的自己! 今天第10讲,讲一下查找算法的线性查找算法 查找算法是计算机科学中的一

    2024年02月08日
    浏览(47)
  • 【算法系列 | 9】深入解析查找算法之—哈希表查找

    心若有阳光,你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。 我们一起努力,成为更好的自己! 今天第9讲,讲一下查找算法的哈希表查找 查找算法是计算机科学中的一类

    2024年02月08日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包