单源最短路径、最小生成树及堆的Java实现

1. 堆

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Heap<T extends Comparable<T>> {
	private List<T> list;
	private boolean isMaxHeap = true;
	
	public Heap() {
		list = new ArrayList<T>();
		list.add(null);
	}
	
	public Heap(Collection<T> col) {
		this();
		addAll(col);
	}
	
	private int getLeftChildIndex(int i) {
		return i * 2;
	}
	
	private int getRightChildIndex(int i) {
		return i * 2 + 1;
	}
	
	private void maxHeapify(int rootIdx) {
		int li = this.getLeftChildIndex(rootIdx);
		int ri = this.getRightChildIndex(rootIdx);
		
		int largestIdx;
		if (li <= this.size() && list.get(li).compareTo(list.get(rootIdx)) > 0) {
			largestIdx = li;
		} else {
			largestIdx = rootIdx;
		}
		if (ri <= this.size() && list.get(ri).compareTo(list.get(largestIdx)) > 0) {
			largestIdx = ri;
		}
		
		if (largestIdx != rootIdx) {
			T tmp = list.get(rootIdx);
			list.set(rootIdx, list.get(largestIdx));
			list.set(largestIdx, tmp);
			this.maxHeapify(largestIdx);
		}
	}
	
	private void minHeapify(int rootIdx) {
		int li = this.getLeftChildIndex(rootIdx);
		int ri = this.getRightChildIndex(rootIdx);
		
		int smallestIdx;
		if (li <= this.size() && list.get(li).compareTo(list.get(rootIdx)) < 0) {
			smallestIdx = li;
		} else {
			smallestIdx = rootIdx;
		}
		if (ri <= this.size() && list.get(ri).compareTo(list.get(smallestIdx)) < 0) {
			smallestIdx = ri;
		}
		
		if (smallestIdx != rootIdx) {
			T tmp = list.get(rootIdx);
			list.set(rootIdx, list.get(smallestIdx));
			list.set(smallestIdx, tmp);
			this.minHeapify(smallestIdx);
		}
	}
	
	private void heapify(int i) {
		if (this.isMaxHeap) {
			this.maxHeapify(i);
		} else {
			this.minHeapify(i);
		}
	}
	
	public void add(T e) {
		list.add(e);
		for (int i = this.size() / 2; i > 0; i--) {
			heapify(i);
		}
	}
	
	public void addAll(Collection<T> col) {
		list.addAll(list.size(), col);
		for (int i = this.size() / 2; i > 0; i--) {
			heapify(i);
		}
	}
	
	public void remove(int i) {
		list.set(i, list.get(this.size()));
		list.remove(this.size());
		this.heapify(i);
	}
	
	public T top() {
		return list.get(1);
	}
	
	public T pop() {
		T value = list.get(1);
		this.remove(1);
		return value;
	}
	
	public boolean isMaxHeap() {
		return isMaxHeap;
	}

	public void setMaxHeap(boolean isMaxHeap) {
		boolean lastFlag = this.isMaxHeap;
		this.isMaxHeap = isMaxHeap;
		if (lastFlag != isMaxHeap) {
			for (int i = this.size() / 2; i > 0; i--) {
				heapify(i);
			}
		}
	}

	public int size() {
		return list.size() - 1;
	}
	
	public void clear() {
		this.list.clear();
		this.list.add(null);
	}
	
	public boolean isEmpty() {
		return this.size() == 0;
	}
}

2. 最小生成树的Kruskal算法

import heap.Heap;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		List<Path> mst = Kruskal.execute();
		for (Path path: mst) {
			System.out.println(path.p1 + "\t" + path.p2 + "\t" + path.len);
		}
	}
}

/*
 * 实现最小生成树的Kruskal算法
 * 需要通过标准输入流输入无向图的信息
 * 第一行为两个整数n m,n为总结点树,m为边数
 * 结点的编号从0到n-1
 * 接下来的m行为无向边的信息,格式为f t l
 * f和t分别为某条无向边的两个顶点,l为边权值
 * 测试数据示例见最后
 */
class Kruskal {
	
	public static List<Path> execute() {
		List<Path> mst = new ArrayList<Path>();
		
		Scanner in = new Scanner(System.in);
		
		int n = in.nextInt();
		int m = in.nextInt();
		
		List<Set<Integer>> sets = new ArrayList<Set<Integer>>();
		for (int i = 0; i < n; i++) {
			Set<Integer> set = new HashSet<Integer>();
			set.add(i);
			sets.add(set);
		}
		
		Heap<Path> heap = new Heap<Path>();
		heap.setMaxHeap(false);
		int f, t, l;
		for (int i = 0; i < m; i++) {
			f = in.nextInt();
			t = in.nextInt();
			l = in.nextInt();
			
			Path path = new Path(f, t, l);
			heap.add(path);
		}
		in.close();
		
		while (sets.size() != 1 && !heap.isEmpty()) {
			Path path = heap.pop();
			int p1 = path.p1;
			int p2 = path.p2;
			
			Set<Integer> p1Set = null;
			for (Set<Integer> s: sets) {
				if (s.contains(p1)) {
					p1Set = s;
					break;
				}
			}
			if (!p1Set.contains(p2)) {
				Set<Integer> p2Set = null;
				for (Set<Integer> s: sets) {
					if (s.contains(p2)) {
						p2Set = s;
						break;
					}
				}
				sets.remove(p2Set);
				p1Set.addAll(p2Set);
				mst.add(path);
			}
		}
		
		return mst;
	}
}

class Path implements Comparable<Path> {
	public int p1;
	public int p2;
	public int len;
	
	public Path(int p1, int p2, int len) {
		this.p1 = p1;
		this.p2 = p2;
		this.len = len;
	}

	@Override
	public int compareTo(Path o) {
		return this.len - o.len;
	}
}

//测试数据
//7 11
//0 1 12
//1 2 32
//2 5 3
//5 6 24
//0 6 18
//0 3 25
//1 3 14
//0 4 34
//3 4 10
//3 5 8
//4 5 7

3. 单源最短路径的Dijkstra算法

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		new Dijkstra().execute();
	}
}

/*
 * 实现单源最短路径的Dijkstra算法
 * 通过标准输入流输入图的信息
 * 本程序实现的是无向图中的单源最短路径,若要求有向图,则只需修改第45行
 * 输入数据第一行为两个整数n m,n为结点数,m为边数
 * 结点的编号从0到n-1
 * 接下来为m行数据,表示每条边的信息,格式为f t l,分别表示起点和终点的编号,以及边权值
 * 最后一行为两个整数,表示要求路径的起点和终点编号
 * 测试数据见最后
 */
class Dijkstra {
	
	private static final int INFINITE = 9999;
	
	public void execute() {
		Scanner in = new Scanner(System.in);
		
		int n = in.nextInt(); // number of points
		int m = in.nextInt(); // number of paths
		
		int[][] paths = new int[n][n];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				paths[i][j] = INFINITE;
			}
		}
		
		int f, t, l;
		for (int i = 0; i < m; i++) {
			f = in.nextInt();
			t = in.nextInt();
			l = in.nextInt();
			paths[f][t] = paths[t][f] = l;
		}
		
		int sp = in.nextInt(); // starting point
		int fp = in.nextInt(); // finishing point
		
		in.close();
		
		int[] dist = new int[n];
		List<List<Integer>> sPaths = new ArrayList<List<Integer>>();
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < n; i++) {
			dist[i] = paths[sp][i];
			if (sp != i) {
				list.add(i);
			}
			if (dist[i] < INFINITE) {
				List<Integer> spa = new ArrayList<Integer>();
				sPaths.add(spa);
			} else {
				sPaths.add(null);
			}
		}
		
		while (!list.isEmpty()) {
			Integer minIdx = list.get(0);
			for (int i: list) {
				if (dist[i] < dist[minIdx]) {
					minIdx = i;
				}
			}
			
			list.remove(minIdx);
			
			for (int i = 0; i < n; i++) {
				if (dist[i] > dist[minIdx] + paths[minIdx][i]) {
					dist[i] = dist[minIdx] + paths[minIdx][i];
					List<Integer> tmp = new ArrayList<Integer>(sPaths.get(minIdx));
					tmp.add(minIdx);
					sPaths.set(i, tmp);
				}
			}
		}
		
		System.out.println(dist[fp]);
		System.out.print(sp + " --> ");
		for (int p: sPaths.get(fp)) {
			System.out.print(p + " --> ");
		}
		System.out.println(fp);
	}
}

//测试数据
//10 14
//0 1 3
//0 5 4
//0 8 5
//1 2 6
//2 3 1
//2 5 4
//3 4 8
//3 6 2
//3 7 5
//4 7 2
//5 6 3
//6 7 7
//8 9 2
//9 7 3
//0 4

4. 附记

由于时间的关系,关于这几个算法的思想将在以后的博文中总结。
总结因为时间紧张的原因,总结了好多东西,只能在有空的时候才能弄到电脑里,再搬到博客上。
希望能找到一个薪水高一些的实习工作吧!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值