运用分支限界法。分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
单源最短路径问题:
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Scanner;
/**
*
* @author 刘宁宁
*/
public class BBShortest {
static float[][] a; // 图G的邻接矩阵
static int v; // 起点编号
static float[] dist; // dist[i]是节点i与起点的当前算得的最短距离
static int[] p; // 前驱数组
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("请输入结点个数");
int n = s.nextInt();
a = new float[n+1][n+1];
for(int i = 0; i < a.length; i++){
Arrays.fill(a[i], Float.MAX_VALUE);
}
p = new int[n+1];
dist = new float[n+1];
System.out.println("请输入边数");
int e = s.nextInt();
for (int i = 0; i < e; i++) {
System.out.println("请输入两点及其距离");
int previous = s.nextInt();
int next = s.nextInt();
a[previous][next] = s.nextFloat();
}
shortest(1, dist, p);
// 从左到右,按列优先依次为1,2,3,,,
System.out.println("最短距离为:" + dist[n] + "\n最短路径为:");
back(p[n]);
System.out.println(n);
}
static class HeapNode implements Comparable {
int i; // 顶点编号
float length; // 当前路长
public HeapNode(int i, float length) {
this.i = i;
this.length = length;
}
@Override
public int compareTo(Object o) {
float xl = ((HeapNode) o).length;
if (length < xl) {
return -1;
}
if (length == xl) {
return 0;
}
return 1;
}
}
public static void shortest(int v, float[] dist, int[] p) {
int n = p.length - 1;
MinHeap heap = new MinHeap(n);
//定义源为初始扩展结点
HeapNode enode = new HeapNode(v, 0);
//dist[j]表示当前结点j到起点的最短距离,初始化为最大值
for (int j = 1; j <= n; j++)
dist[j] = Float.MAX_VALUE;
dist[v] = 0;
while (true) { // 搜索问题的解空间
for (int j = 1; j <= n; j++)
// enode.i是当前的扩展节点, enode.length是该节点到起始点的最短距离
if (a[enode.i][j] < Float.MAX_VALUE && enode.length + a[enode.i][j] < dist[j]) {
// enode.i 到顶点j可达,且满足控制约束
dist[j] = enode.length + a[enode.i][j];
//dist[j]是节点J与起点的当前算得的最短距离
p[j] = enode.i; // p[j]表示p的前驱节点
HeapNode node = new HeapNode(j, dist[j]);
heap.insert(node); // 加入活结点优先队列
}
if (heap.isEmpty()) break;
else enode = heap.removeMin();
}
}
static void back(int i){
if(p[i] == 0){
System.out.print(i + " -> ");
}else{
back(p[i]);
System.out.print(i + " -> ");
}
}
}
// 下标从0开始的最小堆, 子节点 2n+1, 2(n+1)
class MinHeap {
//存储堆中元素
private BBShortest.HeapNode[] items ;
//记录堆中元素个数
private int total;
public MinHeap() {
}
public MinHeap(int capacity) {
this.items = new BBShortest.HeapNode[capacity];
for (int i = 0; i < capacity; i++) {
this.items[i] = new BBShortest.HeapNode(i, 0);
}
}
//获取队列中元素个数
public int size() {
return total;
}
//判断队列是否为空
public boolean isEmpty() {
return total == 0;
}
//判断堆中索引i处的元素是否小于索引j处的元素
private boolean less(int i, int j) {
return items[i].compareTo(items[j]) < 0;
}
//交换堆中i索引和j索引处的值
private void swap(int i, int j) {
BBShortest.HeapNode tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
//往堆中插入一个元素
public void insert(BBShortest.HeapNode t) {
items[++total] = t;
swim(total);
}
//删除堆中最小元素,并返回这个最小元素
public BBShortest.HeapNode removeMin() {
BBShortest.HeapNode min = items[0];
swap(0, total);
total--;
sink(0);
return min;
}
//使用上浮算法,使索引k处的元素能在堆中处于一个正确的位置
private void swim(int k) {
//通过循环比较当前节点和其父节点的大小
while (k > 0) {
if (less(k, (k - 1) / 2)) { // 若果该节点比父节点
swap(k, (k - 1) / 2);
}
k = (k - 1) / 2;
}
}
//使用下沉算法,使索引k处的元素能在堆中处于一个正确的位置
private void sink(int k) {
//通过循环比较当前节点和其子节点中较小值
while (2 * k + 1 <= total) { // 说明该节点有子节点
//找到子节点中的较小值
int min;
if (2 * k + 2 <= total) { // 有右子节点,选左右子结点中值小的赋值给min
if (less(2 * k + 1, 2 * k + 2)) {
min = 2 * k + 1;
} else {
min = 2 * k + 2;
}
} else {
min = 2 * k + 1;
}
//判断当前节点和较小值的大小
if (less(k, min)) { //该节点小于两个子节点
break;
}
swap(k, min); //交换该节点和较小子节点
k = min;
}
}
}
结果: