非递归广度优先遍历无向图(有向图)并打印出其路径

昨天有个朋友找我帮个忙,需要我帮他看个代码,实现从无向图中输出两个节点中的路径。

他给了我一份代码,但是在遍历的过程中测试了很多次,基本都没有输出。我看了一下代码是深度优先遍历的,但是由于他的无向图节点有一万多个,并且每个节点的邻接节点又有几百几千个,在深度遍历过程中这个n叉树得不到控制,遍历路径的时间完全随缘,运行几小时也遍历不完。并且由于是深度遍历,输出的路径也是随缘,不会达到先输出最短路径,后输出最长路径,确实不满足朋友的需求。

为此,我先想到剪枝,虽然是深度遍历的,但是可以控制树的深度,我将树的深度控制到5,即如果达到第5层还没有路径输出,则抛弃当前节点,回溯至父节点。改完之后有所改善,但是改善并不大,还是有很多用例需要运行很长时间得不到输出。

因此,我想将其改为广度遍历,就可以按照路径的长短顺序进行输出。但是写程序的过程中,发现如果简单的用队列的话,只能判断两个节点能否存在路径,并不能向前推出完整的路径,因为前面的父节点已经从队列中删除。思考之后,我用了两个ArrayList,一个queueList加上一个游标来模拟队列,一个indexList存储当前节点的父节点在queueList中的位置,为输出路径做准备。

代码如下,由于时间仓促,没有好好整理,使用过程中有问题欢迎讨论,见谅。

此代码是从读文件然后构造无向图,可以忽视,使用的话可根据自己的情况构造一个Node数组就行了。

广度遍历主类代码:

package lijia;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;

public class MyFindPathBFS {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		ArrayList<ArrayList<Integer>> myList = new ArrayList<ArrayList<Integer>>();
		for(int i=0;i<=13971;i++){
			myList.add(new ArrayList<Integer>());
		}
		FileReader fr = null;
		BufferedReader br = null;
		try {
			fr = new FileReader("1.net");
			br = new BufferedReader(fr);
			String str = null;
			while((str=br.readLine())!=null){
				String[] nodes = str.split(" ");
				int src = Integer.parseInt(nodes[0]);
				int des = Integer.parseInt(nodes[1]);
				if(!myList.get(src).contains(des)){
					myList.get(src).add(des);
				}
				if(!myList.get(des).contains(src)){
					myList.get(des).add(src);
				}
			}
		} catch (FileNotFoundException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		int length = 13972;

		/* 定义节点关系 */
//		int nodeRalation[][] = { { 1 }, // 0
//				{ 0, 5, 2, 3 },// 1
//				{ 1, 4 }, // 2
//				{ 1, 4 }, // 3
//				{ 2, 3, 5 }, // 4
//				{ 1, 4 } // 5
//
//		};
		/* 定义节点数组 */
		Node[] node = new Node[length];
		for (int i = 0; i < length; i++) {
			node[i] = new Node();
			node[i].setName(i);
		}
		/* 定义与节点相关联的节点集合 */
		for (int i = 0; i < length; i++) {
			ArrayList<Node> List = new ArrayList<Node>();
			ArrayList<Integer> temp = myList.get(i);
			int len = temp.size();
			for (int j = 0; j < len; j++) {
				List.add(node[temp.get(j)]);
			}
			node[i].setRelationNodes(List);
			List = null; // 释放内存
		}
		/* 开始搜索所有路径 */
		new MyFindPathBFS().getPaths(node[8073], node[12351], node);
	}
	private void getPaths(Node src,Node des,Node[] nodes) {
		int num = 0;
//		模拟队列
		ArrayList<Node> queueList = new ArrayList<Node>();
//		存储下标
		ArrayList<Integer> indexList = new ArrayList<Integer>();
//		源节点入队
		queueList.add(src);
//		设置源节点的父节点的下标为-1,做标志作用
		indexList.add(-1);
		int index = 0;
//		存储连边,不能来回重复放置连边
		HashSet<String> set = new HashSet<String>();
		while(index<queueList.size()){
//			index即为目前队列的头结点
			Node top = queueList.get(index);
//			如果是目的元素,则打印路径,目前设置了输出前三条路径
			if(top.name==des.name){
				printPath(queueList,indexList,index);
				num = num + 1;
				if(num==3)
					System.exit(1);
			}
//			得到当前节点的邻接节点,并入队,并存储入队新节点的父节点的下标
			ArrayList<Node> relation = top.relationNodes;
			String edgeTopNode = null;
			String edgeNodeTop = null;
			for(Node node:relation){
				edgeTopNode = top.name + "," + node.name;
				edgeNodeTop = node.name + "," + top.name;
				if(!set.contains(edgeTopNode)&&!set.contains(edgeNodeTop)){
					queueList.add(node);
					indexList.add(index);
					set.add(edgeTopNode);
				}
				
			}
//			System.out.print("queueList:");
//			for(Node node:queueList){
//				System.out.print(node.name+" ");
//			}
//			System.out.println();	
//			游标向前加1,即下一个要出队的元素,模拟队列
			index = index + 1;
		}
	}
	private void printPath(ArrayList<Node> queueList,ArrayList<Integer> indexList, int index) {
//		由index节点向前推
		System.out.print(queueList.get(index).name+"<-");
		index = indexList.get(index);
		if(index==-1){
			System.out.println(queueList.get(0).name);
			return;
		}
		while(index!=0){
			System.out.print(queueList.get(index).name+"<-");
			index = indexList.get(index);
		}
		System.out.println(queueList.get(0).name);
		return;
	}
	
}

节点数据结构:

package lijia;

import java.util.ArrayList; 

/* 表示一个节点以及和这个节点相连的所有节点 */

public class Node {
	public int name = 0;
	public ArrayList<Node> relationNodes = new ArrayList<Node>();

	public int getName() {
		return name;
	}

	public void setName(int name) {
		this.name = name;
	}

	public ArrayList<Node> getRelationNodes() {
		return relationNodes;
	}

	public void setRelationNodes(ArrayList<Node> relationNodes) {
		this.relationNodes = relationNodes;
	}
}

输出如下,路径还是从后往前,可在打印路径函数中加入辅助Stack等数据结构,即可实现从前往后打印。

12351<-6224<-7762<-8073
12351<-6224<-9697<-8073
12351<-10757<-9419<-8555<-9376<-8073

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值