prim算法:
首先我们选择0顶点作为最小生成树的第一个顶点,
与最小生成树0的的边有四个分别是 0-4,0-7,0-2,0-6
用他们对应的顶点值作为索引存入最小优先队列中,用他们的权值作为索引值
因此可得
根据最小优先队列我们可以得到权值最小的值,此时找到0-7,因此我们将7进入树中
此时0和7组成了最小生成树,其他顶点又是一个集合,7也有了横切边
7-4 7-5 7-1 7-2 将这些顶点存入最小优先队列中,
由于第一次切分时 索引 4 和 索引 2 已经存入了值,此时应该比较 7-4 和0-4的权值, 7-2和0-2权值,哪个边小,索引留哪个边
此时最小索引中的值
此时再次通过最小索引优先队列找到 7-1边0.19,因此将1进入最小生成树中,
此后不断重复,直至找到所有的顶点,最终结果此图的最小生成树了
public class IndexMinPriorityQueue<T extends Comparable<T>> {
/** item [F,E,D,C,B,A]
* 0 1 2 3 4 5
* pq [0,5,4,2,3,1] 此时pq是调整后台的堆
* qp [0,1 2 3 4 5]
* 如果交换 pq数组 如果 i=1,j=4 既让pq中的值进行替换
*
*
*/
T[] item;
int[] pq;//保存每个元素在item数组中的索引,pq数组需要堆排序
int[] qp;//保存qp的逆序,pq的值作为索引,pq的索引作为值
int N;
public boolean less(int i ,int j){
//因为交换的item数组,但传过来的是pq中的索引其实操作的是pq数组,但pq数组保存的item数组中索引,
return item[pq[i]].compareTo(item[pq[j]]) < 0;
}
public void swap(int i,int j){
//交换pq数组中的数据
int temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
// 再次更新qp中数据
qp[pq[i]]=i;
qp[pq[j]] = j;
}
//判断k对应的元素是否存在
public boolean contains(int k){
return qp[k]!=-1;
}
public IndexMinPriorityQueue(int capacity){
this.item = (T[]) new Comparable[capacity+1];
this.pq = new int[capacity+1];
this.qp = new int[capacity+1];
this.N = 0;
//默认情况下队列中没有存储任何数据,让qp中的元素都为-1,及没有存储任何元素
for (int i = 0; i < qp.length; i++) {
qp[i] = -1;
}
}
//往队列中插入一个元素,并关联索引i
public void insert(int i,T t){
//判断i是否被关联,如果已经被关联,则不让插入
if(contains(i)) return ;
//元素个数+1;
N++;
//把数据存储到对应的i位置
item[i] = t;
//把i存储到pq中
pq[N] = i;
//通过qp来记录pq中的i
qp[i]=N;
//通过堆上浮
swim(N);
}
//删除队列中最小的元素,并返回改元素关联的索引
public int delMin(){
//获取最小元素关联的索引
int minIndex = pq[1];
//交换pq索引1处的值和做大索引处的值
swap(1,N);
//删除qp中对应的内容
qp[pq[N]] = -1;
//删除pq中最大索引处的内容,即删除最小值
pq[N]=-1;
//删除item中对应的内容
item[minIndex] = null;
//元素个数减一
N--;
//下层调整
sink(1);
return minIndex;
}
//删除索引i关联的元素
public void delete(int i){
//找到i在pq中索引
int k = qp[i];
//交换pq中索引k处的值和索引N处的值
swap(k,N);
//删除qp中的内容
qp[pq[N]] = -1;
//删除pq中内容
pq[N] = -1;
//删除item中内容
item[k] = null;
//元素的数量-1
N--;
//堆的调整
sink(k);
swim(k);
}
//把索引i关联的元素修改为t
public void changeItem(int i,T t) {
//修改item数组i位置的元素为t
item[i] = t;
//找到i在pq中出现的位置
int k = qp[i];
//堆调整
sink(k);
swim(k);
}
private void swim(int k) {
while(k>1){
if(less(k,k/2)){
swap(k,k/2);
}
k = k/2;
}
}
private void sink(int n) {
int min = 0;
while(2*n<=N){
if(2*n+1<=N){
if(less(2*n+1,2*n)){
min = 2 * n + 1;
}else{
min = 2 * n;
}
}else{
min = 2 * n;
}
if(less(n,min)){
break;
}else{
swap(n,min);
}
n = min;
}
}
public static void main(String[] args) {
IndexMinPriorityQueue<String> indexQueue = new IndexMinPriorityQueue<>(5);
indexQueue.insert(0,"A");
indexQueue.insert(1,"C");
indexQueue.insert(2,"F");
//测试修改
indexQueue.changeItem(2,"B");
while(indexQueue.N!=0){
int index = indexQueue.delMin();
System.out.println(index+" ");
}
}
public boolean isEmpty(){
return N==0;
}
}
public class EdgeWeightedGraph {
private final int v;
private int E;
private Queue<Edge>[] adj;
public EdgeWeightedGraph(int v) {
//初始化顶点数量
this.v = v;
this.E = 0;
this.adj = new Queue[v];
for (int i = 0; i < adj.length; i++) {
adj[i] = new Queue<Edge>();
}
}
public int v(){
return v;
}
public int E(){
return E;
}
public void addEge(Edge e){
int v = e.either();
int w = e.other(v);
adj[v].enqueue(e);
adj[w].enqueue(e);
E++;
}
public Queue<Edge> adj(int v){
return adj[v];
}
//获取无向图的所有边
public Queue<Edge> eges(){
Queue<Edge> allEdges = new Queue<>();
//遍历图中的每一个顶点,找到该顶点的邻接表,邻接表中存储了该顶点关联的每一条边
for (int i = 0; i < v; i++) {
//遍历v顶点的邻接表,找到每一条和v关联的边
for (Edge edge : adj(i)) {
if (edge.other(v)<v) {
allEdges.enqueue(edge);
}
}
}
return allEdges;
}
}
public class Edge implements Comparable<Edge> {
private final int v;//顶点一
private final int w;//顶点二
private final double weight;//当前边的权重
public Edge(int v, int w, double weight) {
this.v = v;
this.w = w;
this.weight = weight;
}
public double weight() {
return weight;
}
public int either() {
return v;
}
public int other(int vertex ){
if(vertex == v){
return w;
}else{
return v;
}
}
@Override
public int compareTo(Edge that) {
int cmp ;
//如果当前边的权重值大,则让cmp = 1;
if(this.weight>that.weight){
cmp = 1;
}else if(this.weight<that.weight){
cmp = -1;
}else {
cmp = 0;
}
return cmp;
}
}
/**
* 8
* 16
* 4 5 0.35
* 4 7 0.37
* 5 7 0.28
* 0 7 0.16
* 1 5 0.32
* 0 4 0.38
* 2 3 0.17
* 1 7 0.19
* 0 2 0.26
* 1 2 0.36
* 1 3 0.29
* 2 7 0.34
* 6 2 0.40
* 3 6 0.52
* 6 0 0.58
* 6 4 0.93
*/
import com.example.demo.code10.queue.Queue;
import com.example.demo.study.tree.youxianqueue.IndexMinPriorityQueue;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class PrimAlgorithm {
//索引代表顶点,值表示当前顶点和最小生成树的最短边
private Edge[] edgeTo;
//索引代表顶点,值表示当前顶点和最小生成树之间的最短边的权重
private double[] distTo;
//索引代表顶点,如果当前顶点已经在树中,则值为true
private boolean [] marked;
//存放树中顶点和非树中顶点的有效的横切边,生成树和非生成树的有效横切边不止一条,我们很容器去除权值较小的边
private IndexMinPriorityQueue<Double> pq;
public PrimAlgorithm(EdgeWeightedGraph G) {
this.edgeTo = new Edge[G.v()];
this.distTo = new double[G.v()];
for (int i = 0; i < distTo.length; i++) {
distTo[i] = Double.POSITIVE_INFINITY;
}
this.marked = new boolean[G.v()];
//初始化pq
pq = new IndexMinPriorityQueue<Double>(G.v());
//默认让顶点0进入到树中,但是树中只有一个顶点0, 因此顶点默认没有和其他的顶点相连,所以distTo对应位置的值存储0.0
//假的树0.0
distTo[0] = 0.0;
pq.insert(0,0.0);
//遍历索引最小优先队列,拿到 最小切边对应的顶点,把该顶点加入到最小生成树中
while(!pq.isEmpty()){
visit(G,pq.delMin());
}
}
//将顶点v添加到最小生成树中更新数据
private void visit(EdgeWeightedGraph g, int v) {
//把顶点v添加到最小生成树中
marked[v] = true;
//更新数据
for (Edge e : g.adj(v)) {
//获取e边另外一个顶点(当前顶点是v)
//判断另外一个顶点是不是已经在树中,如果在树中则不做任和处理,如果不在树中更新数据
int w = e.other(v);
if(marked[w]){
continue;
}
//判断边e的权重是否小于从w顶点到树中已经存在的最小边的权重
if (e.weight()<distTo[w]) {
//更新数据
edgeTo[w] = e;
distTo[w] = e.weight();
if(pq.contains(w)){
pq.changeItem(w,e.weight());
}else{
pq.insert(w,e.weight());
}
}
}
}
//获取最小生成树的所有边
public Queue<Edge> edges(){
Queue<Edge> allEdges = new Queue<>();
for (int i = 0; i < edgeTo.length; i++) {
if(edgeTo[i]!=null){
allEdges.enqueue(edgeTo[i]);
}
}
return allEdges;
}
public static void main(String[] args) throws Exception {
//创建图
BufferedReader reader = new BufferedReader(new InputStreamReader(PrimAlgorithm.class.getClassLoader().getResourceAsStream("traffic_project.txt")));
int total = Integer.parseInt(reader.readLine());
EdgeWeightedGraph G = new EdgeWeightedGraph(total);
int edgesNumber = Integer.parseInt(reader.readLine());
for (int e = 0; e < edgesNumber; e++) {
String[] strs = reader.readLine().split(" ");
int v = Integer.parseInt(strs[0]);
int w = Integer.parseInt(strs[1]);
double weight = Double.parseDouble(strs[2]);
//构建无向边
Edge edge = new Edge(v, w, weight);
G.addEge(edge);
}
//生成最小生成树
PrimAlgorithm primAlgorithm = new PrimAlgorithm(G);
Queue<Edge> edges = primAlgorithm.edges();
for (Edge edge : edges) {
int v = edge.either();
int w = edge.other(v);
double weight = edge.weight();
System.out.println(v+"-"+w+"::"+weight);
}
}
}