两点间最短路径及所有路径

private Set<Integer> nodes;
private Map<Integer, LinkedList<Integer>> links;
private Map<Link, Weight> weights;
nodes用于存储所有节点,不重复;links为邻接表;weights为边上的权值。

1、两点间最短路径(dijkstra算法)

     实现代码为:

public Path shortestPath(int a_node, int b_node)
	{
		//从a_node到其他节点的代价,有序排列
		Map<Integer, Path> cost = new HashMap<Integer, Path>();
		//sNodes初始为空
		LinkedList<Integer> sNodes = new LinkedList<Integer>();
		//qNodes初始为整个节点集
		LinkedList<Integer> qNodes = new LinkedList<Integer>();
		for (int node : nodes)
		{
			qNodes.add(node);
			cost.put(node, new Path(new Weight(Float.MAX_VALUE)));
		}
		cost.put(a_node, new Path(new Link(a_node, a_node), new Weight(0.0f)));
		//每次迭代qNodes集中被选中的节点
		int uNode;
		while (!qNodes.isEmpty())
		{
			uNode = extractMin(qNodes, cost);
			sNodes.add(uNode);
			if (uNode == b_node)//已经找到到b_node的最短路径
				break;
			for (int vNode : adjNodes(uNode))//代表qNodes集合中的某一节点
			{
				relax(cost, uNode, vNode);
			}
		}
		
		return cost.get(b_node);
	}
其中extractMin函数从qNodes集合中选取从源点出发代价最小的点。relax函数使用到了松弛(relaxation)技术。对于每个顶点v ∈ V,都设置一个属性d[v],用来描述从源点s到v的最短路径上权值的上界,称为 最短路径估计(shortest-path estimate)。在松弛一条边(u,v)的过程中,要测试是否可以通过u,对迄今找到的到v的最短路径进行改进;如果可以,就更新。一次松弛操作可以减小最短路径估计的值。(摘自《算法导论》)

     如果只要找出源点到特定点的最短路径,可以提前跳出循环,如代码中所示。


2、两点间所有路径

    实现代码如下:

public LinkedList<Path> allPath(int a_node, int b_node, Weight max)
	{
		LinkedList<Path> apath = new LinkedList<Path>();
		int top_node; //即栈顶节点
		int cur_node;//当前的临接节点
		int next_node;//下一个入栈节点
		//遍历过程中使用的栈
		LinkedList<Integer> stack = new LinkedList<Integer>();
		//标记节点是否在栈内,避免回路
		Map<Integer, Integer> states = new HashMap<Integer, Integer>();
		//记录栈中路径长度
		Weight weight;
		for (int node : nodes)
		{
			states.put(node, 0);//0表示不在栈内
		}
		//初始化变量
		stack.add(a_node);
		weight = new Weight();//初始化为0
		states.put(a_node, 1);
		cur_node = -1; //定义为第一个临接节点的前一个
		while (!stack.isEmpty())
		{
			top_node = stack.getLast();
			weight = calWeight(stack);
			//路径是否超出最大权值
			if (weight.compareTo(max) > 0)
			{
				cur_node = stack.removeLast();
				states.put(cur_node, 0);
				continue;
			}
			//找到一条路径
			if (top_node == b_node)
			{
				apath.add(genPath(stack, weight));
				cur_node = stack.removeLast();
				states.put(cur_node, 0);
			}
			else
			{
				next_node = nextNode(top_node, cur_node, states);
				if (next_node < Integer.MAX_VALUE)
				{
					stack.add(next_node);
					states.put(next_node, 1);
					cur_node = -1; //新节点入栈,要从头遍历它的所有临接节点
				}
				else
				{
					cur_node = stack.removeLast();
					states.put(cur_node, 0);
				}
			}
			
		}
		return apath;
		
	}

找出所有路径采用的是遍历的方法,以“深度优先”算法为基础。从源点出发,先到源点的第一个邻接点N00,再到N00的第一个邻接点N10,再到N10的第一个邻接点N20...当遍历到目标点时表明找到一条路径。上述代码以核心数据结构为一个栈,主要步骤:

①源点先入栈,当栈顶为终点时,即找到一条路径;

②栈顶元素N1出栈,新的栈顶元素为N2,从N2的所有邻接点中,以N1为起点,选取下一个N3入栈;

③为避免回路,已入栈元素要记录,选取新入栈顶点时应跳过已入栈的顶点

④当栈为空时,遍历完成。

这里我用了两个特殊的标记:一个值为-1,另一个值为Integer.MAX_VALUE。前者代表遍历某个顶点所有邻接点的开始位置,后者为结束位置。

详细的算法描述可以参考中国海洋大学熊建设、梁磊的论文《两点间所有路径的遍历算法》。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值