有向图中判断已选节点间的关系

本算法通过遍历整个有向图来获取已选节点的关系,通过本算法,可以轻松的获得已选节点在其有向图中的流向关系,如下图,通过本算法我们可以和容易的发现A与C的流向关系是A -> C 而不是 A <- C

\

本算法的实现步骤是:

1.遍历整个有向图

2.每次遍历,查看当前节点是否为已选节点之一,

3.若是,建立集合存储已选节点(这里暂且称为临时集合),实现中用的是集合是HashMap

4.若果临时集合元素个数不为0,则将刚加入临时集合的节点名称放入临时集合已有元素的containNode字段中,containNode字段的值是一个类型为String的HashSet

5.若临时集合已有的某个节点的所以子孙节点已遍历完毕,则将该节点所对应的元素从临时集合中删除。

由于本算法需要遍历整个有向图,因此时间复杂度是遍历整个有向图的复杂度;另,关于有向图的遍历方法,在文章《有向图闭环检测值java代码实现》中有论述,这里就不再说明了,当然本算法实现也是有文章《有向图闭环检测值java代码实现》的实现修改得来,对于遍历的修改点如下:

1.修改了traverseInDigraph()方法,取消了对"isFrozen"属性值得检测,因为本算法需要深度的遍历整个有向图。

2.修改了getNextNodeIndexInDigraph()方法,增加了对"isFrozen"的属性值检测,确保可以深度遍历整个有向图。

3.修改了traverseInDigraph(),在前进时将下一节点放入临时集合,后退时将临时集合里对应的元素放入返回之中,同时删除临时集合中对应的元素

具体实现代码如下:

/**
	 * 
	 * 功能:在有向图中寻找 已选节点的关系
	 * 作者:EugeneJao
	 * @param nodeCollections
	 * @param nodes
	 * @return
	 * @throws Exception
	 */
	public Map<String,Map<String,Object>> seekRelation(Map<String,Map<String,Object>> nodeCollections , List<String> nodes) throws Exception{
		
		Map<String,Map<String,Object>> ret = new HashMap<String, Map<String,Object>>();
		/**
		 * 如果前进至某已选节点则将该已选节点放入inn,
		 * 若已从相应已选节点中后退,则从inn移出该节点
		 * ,感觉就像是供游客暂歇的旅馆
		 */
		Map <String,Map<String,Object>> inn = new HashMap<String, Map<String,Object>>();
		/**
		 * 初始化"起点
		 */
		String startNode = getOriginInDigraph(nodeCollections);
		/**
		 * 初始化"视野"
		 */
		Map<String,String> nodeName = new HashMap<String, String>() ;
		nodeName.put("nextNode", startNode);
		nodeName.put("curNode", startNode);
		nodeName.put("beforeNode", startNode);

		while(!(nodeName.get("beforeNode").equals(nodeName.get("curNode")) 
				&& nodeName.get("nextNode") == null )){
			/**
			 * 遍历每个节点
			 */
			nodeName = traverseInDigraphForSeek(nodeCollections, nodeName, inn, nodes,ret);
			
			
		}
		
		return ret;
	}
		
	/**
	 * 
	 * 功能:在有向图中访问某个网络节点
	 * 作者:EugeneJao
	 * @param nodeCollections
	 * @param nodeName
	 * @param inn
	 * @param ret 
	 * @return
	 * @throws Exception
	 */
	private Map<String,String> traverseInDigraphForSeek(Map<String,Map<String,Object>> nodeCollections , Map<String,String> nodeName ,
			Map<String, Map<String, Object>> inn , List<String> nodes, Map<String, Map<String, Object>> ret) throws Exception{
		/**
		 * 判断前进节点在网络中存不存在
		 */
		if(nodeName.get("nextNode") != null){
			if(!(nodeCollections.containsKey(nodeName.get("nextNode")))){
				throw new Exception("节点网络构建异常,网络中不存在节点:" + nodeName);
			}
		}
		
		if(nodeName.get("curNode") == null || nodeName.get("beforeNode") == null){
			throw new Exception("traverse()方法入参异常:curNode和beforeNode不能为空" + nodeName);
		}
		if(nodeName.get("nextNode") != null 
				&& nodeCollections.get(nodeName.get("nextNode"))== null ){
			throw new Exception("节点"+ nodeName.get("nextNode") +"在节点集合中不存在");
		}
		String nextNodeName =  nodeName.get("nextNode") ;
		String curNodeName = nodeName.get("curNode") ;
		if( nextNodeName  == null){
			if(isSelectedNode(curNodeName, nodes)){
				record(inn, ret, curNodeName);
				this.checkOut(curNodeName, inn);
			}
			nodeName = backInDigraphForSeek(nodeCollections, nodeName);
			
		}else{
			if(isSelectedNode(nextNodeName, nodes)){
				this.checkIn(nextNodeName, inn);
			}
			nodeName = headInDigraphForSeek(nodeCollections, nodeName);
			
		}	
	 /**
	  * 如果不是起始"视野",但是nextNode和curNode相同的,
	  * 说明在一个节点上可能存在包含自身的关系,这样的数据是异常数据
	  * 顺序不可调整至调用head()和back()的语句前面
	  */
		if(nodeName.get("curNode").equals(nodeName.get("nextNode")) &&
				!nodeName.get("curNode").equals(nodeName.get("beforeNode"))){
			int curRelIndex = (Integer) nodeCollections.get(nodeName.get("curNode")).get("curRelIndex") ;
			throw new Exception("节点参数异常:节点" + nodeName.get("curNode")+
					"的关系节点不能包含自身,在relList的第"+ 
					(curRelIndex + 1)+"元素上");
		}
		
		return nodeName ;
		
	}
	
	/**
	 * 
	 * 功能:判断节点是否为已选节点
	 * 作者:EugeneJao
	 * @param curNodeName
	 * @param nodes
	 * @return
	 */
	private boolean isSelectedNode(String curNodeName,List<String> nodes){
		
		boolean ret = false ;
		
		Iterator it = nodes.iterator() ;
		while(it.hasNext()){
			String tmpNodeName = (String) it.next() ;
			if(tmpNodeName.equals(curNodeName)){
				ret = true ;
				break ;
			}
		}
		
		return ret;
		
	}
	
	/**
	 * 
	 * 功能:将节点放入inn中,并进行相关操作
	 * 作者:EugeneJao
	 * @param curNodeName
	 * @param inn
	 */
	private void checkIn(String curNodeName , Map<String,Map<String,Object>> inn){
		
		/**
		 * 将已在inn中的节点的relList增加当前节点
		 */
		Set keySet = inn.entrySet();
		Iterator it = keySet.iterator() ;
				
		while(it.hasNext()){
			Entry tmpSet = (Entry) it.next() ;
			String tmpKey = (String) tmpSet.getKey();
			Map<String, Object> tmpNode = inn.get(tmpKey);
			Set<String> tmpList = (Set<String>) tmpNode.get("containNode");
			tmpList.add(curNodeName);			
			tmpNode.put("containNode", tmpList);
			inn.put(tmpKey, tmpNode);
		}
		
		/**
		 * 往inn中新增节点
		 */
		Map<String,Object> traverNode = new HashMap<String, Object>();
		Set<String> relList= new HashSet<String>();
		traverNode.put("nodeName",curNodeName);
		traverNode.put("containNode", relList);
		inn.put(curNodeName, traverNode) ;
		
	}
	
	/**
	 * 
	 * 功能:将已选节点从inn中删除
	 * 作者:EugeneJao
	 * @param curNodeName
	 * @param inn
	 */
	private void checkOut(String curNodeName , Map<String,Map<String,Object>> inn){
		
		inn.remove(curNodeName);
		
	}
	
	
	/** 功能:在有向图中前进至下一个节点
	* 作者:EugeneJao
	* @param nodeCollections
	* @param nodeName
	* @param inn
	* @return
	 * @throws Exception 
	**/
	@SuppressWarnings("unchecked")
	private Map<String,String> headInDigraphForSeek(Map<String,Map<String,Object>> nodeCollections , Map<String,String> nodeName) throws Exception{
		String  curNodeName = nodeName.get("nextNode") ; //传入参数的nextNode在本方法中即是当前节点
		String beforeNodeName = nodeName.get("curNode") ;//传入参数的curNode在本方法中即是前一节点
		Map<String,Object> curNode = nodeCollections.get(curNodeName);
		List<String> relList = (List<String>) curNode.get("relList") ;
		curNode.put("beforeNode",beforeNodeName) ;//传入参数的beforeNode在本方法中即是当前节点
		int curRelIndex = getNextNodeIndexInDigraphForSeek(curNode);
		/**
		 * 获取下一个前进的目标节点名称,当curRelIndex等于relList的长度时,
		 * 说明当前节点的所有关系节点都遍历完了,已无节点可以前进,只能后退
		 */
		String nextNodeName = null ;
		if(curRelIndex < relList.size()){
			nextNodeName = relList.get(curRelIndex) ; 
		}
	
	
	
		/**
		 * 更新当前节点信息
		 */
		curNode.put("curRelIndex", curRelIndex);//不能调整顺序值调用getNextNodeIndex()方法后面
		nodeCollections.put(curNodeName, curNode);
		/**
		 * 更新nodeName信息
		 */
		nodeName.put("nextNode", nextNodeName);
		nodeName.put("curNode", curNodeName);
		nodeName.put("beforeNode", beforeNodeName);
	
		return nodeName;
	}

	/**
	* 
	* 功能:在无向图中后退至之前的节点
	* 作者:EugeneJao
	* @param nodeCollections
	* @param nodeName
	* @return
	 * @throws Exception 
	*/
	private Map<String,String> backInDigraphForSeek(Map<String,Map<String,Object>> nodeCollections , Map<String, String> nodeName) throws Exception{
		
		/**
		 * 节点后退,说明遍历完当前节点的所有子孙节点后,依然没有闭环
		 * 所以先更新当前节点信息的isFroze字段为true
		 * 在多个父节点的情况下,通过isFrozen字段来区分之前通过另一父节点对改节点的遍历
		 * 同时也通过这个属性介绍算法运算的复杂度
		 */
		String  curNodeName = nodeName.get("curNode");
		Map<String,Object> curNode = nodeCollections.get(curNodeName);
		curNode.put("isFrozen", true);
		
		/**
		 * 更新完Frozen属性后,做后退操作
		 */
		curNodeName = nodeName.get("beforeNode") ; //传入参数的beforeNode在后退操作中即是当前节点		
		curNode = nodeCollections.get(curNodeName);
		List<String> relList = (List<String>) curNode.get("relList") ;
		String beforeNodeName = (String) curNode.get("beforeNode");
		/**
		 * 先更新当前节点beforeNode信息,因为后面要用
		 */
		curNode.put("beforeNode", beforeNodeName);//不能调整顺序值调用getNextNodeIndex()方法后面
		
		int curRelIndex = getNextNodeIndexInDigraphForSeek(curNode);
		/**
		 * 获取下一个前进的目标节点名称,当curRelIndex等于relList的长度时,
		 * 说明当前节点的所有关系节点都遍历完了,已无节点可以前进,只能后退
		 */
		String nextNodeName = null ;
		if(curRelIndex < relList.size()){
			nextNodeName = relList.get(curRelIndex) ; 
		}
		
		/**
		 * 更新当前节点信息
		 */
		curNode.put("curRelIndex", curRelIndex);//不能调整顺序值调用getNextNodeIndex()方法前面
		
		nodeCollections.put(curNodeName, curNode);
		/**
		 * 更新nodeName信息
		 */
		nodeName.put("nextNode", nextNodeName);
		nodeName.put("curNode", curNodeName);
		nodeName.put("beforeNode",beforeNodeName) ;
		
		
		return nodeName;
	}
	
	/**
	 * 
	 * 功能:将即将从inn中请出的node放入ret中
	 * 作者:EugeneJao
	 * @param inn
	 * @param ret
	 * @param curNodeName
	 */
	private void record(Map<String,Map<String,Object>> inn , Map<String,Map<String,Object>> ret , String curNodeName){
		Map<String,Object> curNode = inn.get(curNodeName);
		ret.put(curNodeName, curNode);
	}
	
	/**
	 * 
	 * 功能:在有向图中获取下一节点名称在relList里的索引值
	 * 作者:EugeneJao
	 * @param curNode
	 * @return
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	private int getNextNodeIndexInDigraphForSeek(Map<String,Object> curNode) throws Exception{
		if(!(curNode.get("isFrozen") instanceof Boolean)){
			throw new Exception("节点"+ curNode.get("nodeName") +
					"参数类型异常:isFrozen合法类型为布尔类型,isFrozen:"+ curNode.get("isFrozen"));
		}
		/**
		 * 如果当前节点状态为冻结,怎么说明它与它的子节点之前都被遍历过了,
		 * 为了让这次能够再次遍历,设置状态为非冻结状态,同时返回索引值0
		 */
		if((Boolean) curNode.get("isFrozen")){
			
			curNode.put("isFrozen", false);			
			return 0 ;
		}
		
		List<String> relList = (List<String>) curNode.get("relList") ;
		int curRelIndex = (Integer) curNode.get("curRelIndex") ;
		String beforeNodeName = (String) curNode.get("beforeNode");		
		curRelIndex ++ ;
		/**
		 * 如果curRelIndex大于relList的大小
		 * 则说明当前节点的关系节点全都遍历过了,curRelIndex赋值为relList的大小,
		 * 即说明当前节点的关系节点全都遍历过了,不能前进了,只能后退
		 */
		if(curRelIndex > relList.size()){
			curRelIndex = relList.size() ;
		}
		return curRelIndex ;
	}

测试代码如下:

public static void main(String [] args) throws Exception{
		
		Map<String,Map<String,Object>> nodeCollections = new HashMap<String, Map<String,Object>>() ;
		
		Map<String,Object> node_1 = new HashMap<String, Object>() ;		
		String nodeName_1 = "1" ; 		
		List<String> relList_1 = new ArrayList<String>() ;	
		relList_1.add("2");
		relList_1.add("3");
		int curRelIndex_1 = -1 ;		
		String beforeNode_1 = null ;
		node_1.put("nodeName", nodeName_1) ;
		node_1.put("curRelIndex", curRelIndex_1) ;
		node_1.put("beforeNode", beforeNode_1) ;
		node_1.put("relList", relList_1) ;
		node_1.put("isFrozen", false);
		node_1.put("isRoot", false);
		
		Map<String,Object> node_2 = new HashMap<String, Object>() ;		
		String nodeName_2 = "2" ; 		
		List<String> relList_2 = new ArrayList<String>() ;	
		relList_2.add("4");
		int curRelIndex_2 = -1 ;		
		String beforeNode_2 = null ;
		node_2.put("nodeName", nodeName_2) ;
		node_2.put("curRelIndex", curRelIndex_2) ;
		node_2.put("beforeNode", beforeNode_2) ;
		node_2.put("relList", relList_2) ;
		node_2.put("isFrozen", false);
		node_2.put("isRoot", false);
		
		Map<String,Object> node_3 = new HashMap<String, Object>() ;		
		String nodeName_3 = "3" ; 		
		List<String> relList_3 = new ArrayList<String>() ;	
		relList_3.add("4");
		
		int curRelIndex_3 = -1 ;		
		String beforeNode_3 = null ;
		node_3.put("nodeName", nodeName_3) ;
		node_3.put("curRelIndex", curRelIndex_3) ;
		node_3.put("beforeNode", beforeNode_3) ;
		node_3.put("relList", relList_3) ;
		node_3.put("isFrozen", false);
		node_3.put("isRoot", false);
		
		
		Map<String,Object> node_4 = new HashMap<String, Object>() ;		
		String nodeName_4 = "4" ; 		
		List<String> relList_4 = new ArrayList<String>() ;	
		relList_4.add("5");
		relList_4.add("6");
		int curRelIndex_4 = -1 ;		
		String beforeNode_4 = null ;
		node_4.put("nodeName", nodeName_4) ;
		node_4.put("curRelIndex", curRelIndex_4) ;
		node_4.put("beforeNode", beforeNode_4) ;
		node_4.put("relList", relList_4) ;
		node_4.put("isFrozen", false);
		node_4.put("isRoot", false);
		
		
		Map<String,Object> node_5 = new HashMap<String, Object>() ;		
		String nodeName_5 = "5" ; 		
		List<String> relList_5 = new ArrayList<String>() ;	
		int curRelIndex_5 = -1 ;		
		String beforeNode_5 = null ;
		node_5.put("nodeName", nodeName_5) ;
		node_5.put("curRelIndex", curRelIndex_5) ;
		node_5.put("beforeNode", beforeNode_5) ;
		node_5.put("relList", relList_5) ;
		node_5.put("isFrozen", false);
		node_5.put("isRoot", false);
		
		Map<String,Object> node_6 = new HashMap<String, Object>() ;		
		String nodeName_6 = "6" ; 		
		List<String> relList_6 = new ArrayList<String>() ;	
		relList_6.add("7");
		int curRelIndex_6 = -1 ;		
		String beforeNode_6 = null ;
		node_6.put("nodeName", nodeName_6) ;
		node_6.put("curRelIndex", curRelIndex_6) ;
		node_6.put("beforeNode", beforeNode_6) ;
		node_6.put("relList", relList_6) ;
		node_6.put("isFrozen", false);
		node_6.put("isRoot", false);
		
		
		
		Map<String,Object> node_7 = new HashMap<String, Object>() ;		
		String nodeName_7 = "7" ; 		
		List<String> relList_7 = new ArrayList<String>() ;	
		relList_7.add("5");
		int curRelIndex_7 = -1 ;		
		String beforeNode_7 = null ;
		node_7.put("nodeName", nodeName_7) ;
		node_7.put("curRelIndex", curRelIndex_7) ;
		node_7.put("beforeNode", beforeNode_7) ;
		node_7.put("relList", relList_7) ;
		node_7.put("isFrozen", false);
		node_7.put("isRoot", false);
		
		nodeCollections.put("1",node_1 );
		nodeCollections.put("2",node_2 );
		nodeCollections.put("3",node_3 );
		nodeCollections.put("4",node_4 );
		nodeCollections.put("5",node_5 );
		nodeCollections.put("6",node_6 );
		nodeCollections.put("7",node_7 );
		
		GraphAnalysis analysis = new GraphAnalysis() ;
		long start = System.currentTimeMillis();
			/*boolean result = analysis.isContainLoop(nodeCollections);*/
		List<String> nodes = new ArrayList<String>();
		nodes.add("1");
		nodes.add("4");
		nodes.add("7");
		nodes.add("5");
		Map<String,Map<String,Object>> ret = analysis.seekRelation(nodeCollections, nodes);
		System.out.println(ret);
		/*boolean result = analysis.isContainLoopInDigraph(nodeCollections);
		System.out.println(result);*/
		long end = System.currentTimeMillis();
		
		System.out.println("耗时:" + (end - start) );
	}

如有BUG,欢迎大家提出,谢谢!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值