图的邻接表实现以及targan算法

这篇博客介绍了如何用邻接表数据结构表示有向图,并实现添加、删除节点和边的操作。接着,文章详细阐述了Targan算法,并通过代码展示了如何找到图中的割点。最后,给出了一个示例图,演示了求解割点的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
点类
*/
#ifndef NODE_H__
#define NODE_H__
#include "arc.h"
#include <memory>
#include <string>

struct node
{
	std::string node_name;
	std::shared_ptr<arc> arc;
	node()
	{
		node_name.clear();
		arc.reset();
	}
	node(const std::string name)
	{
		node_name=name;
		arc.reset();
	}
};

#endif
/**
边类
**/
#ifndef ARC_H__
#define ARC_H__

#include <memory>
#include <string>

struct arc
{
	std::shared_ptr<arc> next;
	std::string node_name;
	arc(const std::string& name)
	{
		next.reset();
		node_name=name;
	}
	arc()
	{
		node_name.clear();
		next.reset();
	}
};

#endif

/**
一个邻接表表示的有向图
**/
#ifndef GRAPH_H__
#define GRAPH_H__

#include <vector>
#include <string>
#include <algorithm>
#include <memory>
#include <map>
#include <iostream>
#include "node.h"
#include "arc.h"

class graph 
{
private:
	typedef std::vector<std::shared_ptr<node> >::iterator iterator;
	std::shared_ptr<arc> null_arc;
public:
	graph()
	{
		null_arc.reset();
	}
	bool add_node(const std::string& name)
	{
		auto index=vec.begin();
		for(;index!=vec.end();++index)
		{
			if((*index)->node_name==name)
				break;
		}
		if(index!=vec.end())
			return false;
		vec.push_back(std::shared_ptr<node>(new node(name)));
		return true;
	}
	void remove_node(const std::string& name)
	{
		//删除节点和删除边
		auto index=vec.begin();
		while(index!=vec.end())
		{
			if((*index)->node_name==name) //删除以这个点起始的点
			{
				index=vec.erase(index);
			}
			else  //删除所有以这个边为终点的边
			{
				if((*index)->arc==null_arc)
					;
				else if((*index)->arc->node_name==name)
				{
					(*index)->arc=(*index)->arc->next;
				}
				else
				{
					auto p=(*index)->arc;
					while(p->next!=null_arc)
					{
						if(p->next->node_name==name)
						{
							p->next=p->next->next;
							break;
						}
						p=p->next;
					}
				}
				++index;
			}
		}
	}
	//删除(from_name,to_name)边
	bool remove_arc(const std::string from_name,
		const std::string to_name)
	{
		auto index_f=get_index_from_name(from_name); //查找from_name的节点
		if(index_f==vec.end()) //如果没有,就返回
		{
			std::cout<<"This arc is not exist!"<<std::endl;
			return false;
		}
		if((*index_f)->arc==null_arc)  //如果from_name节点不包含边,则返回
		{
			std::cout<<"This arc is not exist!"<<std::endl;
			return false;
		}
		if((*index_f)->arc->node_name==to_name) //特殊情况,处理
		{
			(*index_f)->arc=(*index_f)->arc->next;
		}
		else      //否则,循环查找,如果找到就删除
		{
			auto p=(*index_f)->arc;
			while(p->next!=null_arc)
			{
				if(p->next->node_name==to_name)
				{
					p->next=p->next->next;
					break;
				}
				p=p->next;
			}
		}
		return true;
	}
	//在from_name和to_name之间添加一条边
	//前提必须是已经存在from_name和to_name两个点
	bool add_arc(const std::string from_name,
		const std::string to_name)
	{
		auto index_f=get_index_from_name(from_name);
		auto index_t=get_index_from_name(to_name);
		if(index_f==vec.end()|| index_t==vec.end())
		{
			std::cout<<"Please input node first!"<<std::endl;
			return false;
		}
		//查重
		if((*index_f)->arc!=null_arc)
		{
			std::shared_ptr<arc> tem=(*index_f)->arc;
			while(tem!=null_arc)
			{
				if(tem->node_name==to_name)
				{
					std::cout<<"This arc has exist!"<<std::endl;
					return false;
				}
				if(tem->next==null_arc)
					break;
				tem=tem->next;
			}
			tem->next.reset(new arc(to_name));
		}
		else
		{
			(*index_f)->arc.reset(new arc(to_name));
		}
		return true;
	}
	//输出图
	void print_graph()
	{
		auto index=vec.begin();
		while(index!=vec.end())
		{
			std::cout<<"Node:"<<(*index)->node_name;
			std::cout<<" arcs:";
			auto p=(*index)->arc;
			while(p!=null_arc)
			{
				std::cout<<"("<<(*index)->node_name<<","<<p->node_name<<") ";
				p=p->next;
			}
			std::cout<<std::endl;
			++index;
		}
	}
	//查找割点
	//采用targan算法
	std::vector<std::string> get_cut_points(const std::string& node_name)
	{
		std::vector<std::string> cut_points;
		size_t count=0;
		std::map<std::string,int>seq,low;
		dnf(node_name,seq,low,count,cut_points);
		if(count<vec.size()-1)
		{
			cut_points.push_back(node_name);
			for(auto index=vec.begin();index!=vec.end();++index)
			{
				count=0;
				if(seq.find((*index)->node_name)!=seq.end())
					dnf((*index)->node_name,seq,low,count,cut_points);
			}
		}
		return cut_points;
	}
private:
	//深度遍历
	//从node_name节点开始
	//seq为节点的访问顺序
	//low为low(v)=min(seq(v),low(u))
	//其中,v为当前节点,u为v深度遍历的子节点
	//count为访问次序
	//vec用来存储割点
	void dnf(const std::string& node_name,std::map<std::string,int>& seq,
		std::map<std::string,int>& low,size_t& count,std::vector<std::string>& cut_points)
	{
		auto index=get_index_from_name(node_name);
		if(index!=vec.end())
		{
			int min=-1;
			auto p=(*index)->arc;
			seq.insert(make_pair(node_name,count));
			min=count++;
			while(p!=null_arc)
			{
				if(seq.find(p->node_name)==seq.end()) //如果没有访问过
				{
					dnf(p->node_name,seq,low,count,cut_points);
					if(low[p->node_name]<min)
						min=low[p->node_name];
					if(low[p->node_name]>=seq[node_name])
						cut_points.push_back(node_name);
				}
				else if(seq[p->node_name]<min)
					min=seq[p->node_name];
				p=p->next;
			}
			low[node_name]=min;
		}
	}
	//根据节点名字查找该节点在vector中的索引
	iterator get_index_from_name(const std::string& from_name)
	{
		auto index=vec.begin();
		while(index!=vec.end())
		{
			if((*index)->node_name==from_name)
				break;
			++index;
		}
		return index;
	}
private:
	//换成map效率更高
	//std::map<std::string,std::shared_ptr<arc> > vec;
	std::vector<std::shared_ptr<node> >vec;
	//typedef std::vector<std::shared_ptr<node> >::iterator iterator;
};

#endif

/**
利用targan算法求无向图的割点
**/

#include "graph.h"

int main(int argc,char* argv[])
{
	graph g;
	g.add_node("1");
	g.add_node("2");
	g.add_node("3");
	g.add_node("4");
	g.add_node("5");
	g.add_node("6");
	///
	g.add_arc("1","2");
	g.add_arc("1","6");
	g.add_arc("2","5");
	g.add_arc("6","5");
	g.add_arc("5","1");
	g.add_arc("2","3");
	g.add_arc("3","4");
	g.add_arc("5","4");

	std::vector<std::string> vec=g.get_cut_points("1");
	std::for_each(vec.begin(),vec.end(),[](const std::string& str)
	{
		std::cout<<str<<std::endl;
	}
	);
	system("PAUSE");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值