prim优化版

/**
学习算法,并不仅仅要知道步骤,还需要知道原理。
对于prim算法和kruskal算法的证明,网上有很多,但是总感觉要么不够简洁要么不正确。
个人觉得,这篇博客介绍的切分定理,非常简洁能够给予正确的证明。
https://blog.csdn.net/towads/article/details/71159202

最小生成树
prim算法

优化思路:
g.vertexs = [{id:'A',used:false},{id:'B',used:false},{id:'C',used:false},{id:'D',used:false},{id:'E',used:false},{id:'F',used:false}];
g.edges = [
heap,
heap,
heap,
heap,
heap,
heap
];
heap是最小堆对象。最小堆每个节点是图的顶点。权重小于等于0的节点,不加入堆。
然后每次贪心获取和当前顶点权值最小的边的另一个顶点时,判断另一个顶点是否被使用(used是否为true),
已经被使用那么就pop下一个节点,直到pop出一个未被使用过的顶点。
每次取出一个新的未被使用过的顶点,就将其标记为已使用。
时间复杂度由O(n2)变为O(nlogn),空间复杂度仍然为O(n2),
根据稀疏程度有可能节省空间也可能用了更多空间,但是即使稀疏程度很低,多出的空间也非常少。

优化:之前版本有3层循环,其实可以优化为2层的。
就是将findNextVertexAndEdge方法中查找最小边的逻辑优化一下。
定义一个数组,用于保存当前顶点到 各个顶点 的边的信息。
如果当前顶点到某顶点的权值不是最小,那么它肯定不在考虑范围内,那么该元素就是上次处理的顶点
到该顶点的边的信息。

*/
function newGraph(){
    let g = {
        vertexs:[],//顶点集合
        edges:[]//,//边的集合。使用邻接矩阵,是一个二维数组。
        //edgesNum:0//边的条数
    };
    g.vertexs = ['A','B','C','D','E','F'];
    g.edges = [//0表示两个顶点没有边。大于0表示边的权重。  edge={y:二维数组第一维下标,x:第二维下标,weight:权重}
        [0  ,80  ,0  ,100  ,0  ,20  ],
        [80 ,0   ,90 ,0    ,0  ,0   ],
        [0  ,90  ,0  ,10   ,0  ,70  ],
        [100,0   ,10 ,0    ,60 ,0   ],
        [0  ,0   ,0  ,60   ,0  ,40  ],
        [20 ,0   ,70 ,0    ,40 ,0   ]
    ];
    return g;
}

function newGraph2(){
    let g = {
        vertexs:[],//顶点集合
        edges:[]//,//边的集合。使用邻接矩阵,是一个二维数组。
        //edgesNum:0//边的条数
    };
    g.vertexs = ['A','B','C','D','E','F'];
    g.edges = [//0表示两个顶点没有边。大于0表示边的权重。  edge={y:二维数组第一维下标,x:第二维下标,weight:权重}
        [0  ,1   ,1  , Infinity    ,Infinity  ,1   ],
        [1  ,0   ,2  ,Infinity    ,Infinity  ,Infinity   ],
        [1  ,2   ,0  ,3    ,Infinity  ,Infinity   ],
        [Infinity  ,Infinity  ,3  ,0    ,4  ,Infinity   ],
        [Infinity  ,Infinity   ,Infinity  ,4    ,0  ,5   ],
        [1  ,Infinity  ,Infinity ,Infinity    ,5  ,0   ]
    ];
    return g;
}

function findNextVertexAndEdge(graph,visited){
    //console.info(visited);
    let x=-1,y=-1,w=Infinity;
    for(let j=0;j<visited.length;j++){
        if(visited[j].weight<=0){
            continue;
        }
        if(visited[j].weight < w){
            w = visited[j].weight;
            y = visited[j].y;
            x = visited[j].x;
        }
    }
    if(w <= 0){
        throw new Error('weight value error');
    }
    return {edge:{x,y,weight:w},vertex:x};
}
/**
计算最小生成树
结果元素
edge:
*/
function prim(graph){
    let res = [];
    let v = 0;//当前顶点
    let visited = [];//
    for(let i=0;i<graph.vertexs.length;i++){
        visited.push({
            y:0,//从第一个顶点开始
            x:i,
            weight:Infinity//为负值表示已经使用过,正值表示权值,Infinity表示无穷大
        });
    }
    //console.info(visited);
    for(let i=0;i<graph.vertexs.length-1;i++){//边的个数=顶点个数-1
        for(let j=0;j<visited.length;j++){//更新visited
            let w = graph.edges[v][j];//
            if(w < visited[j].weight){
            //如果当前顶点到某顶点的权值 比 上次顶点到某顶点的权值小,那么待会儿最小权值可能是当前顶点到某顶点的,不可能是上次顶点到某顶点的。
                visited[j].y = v;
                visited[j].x = j;
                visited[j].weight = w;
            }
        }

        //console.info(visited);

        let {edge,vertex} = findNextVertexAndEdge(graph,visited);
        //console.info(edge);
        //console.info(vertex);

        v = vertex;
        visited[v].weight = -visited[v].weight;
        /*
        //console.info(JSON.stringify(min));
        for(let j=0;j<vertexs.length;j++){//把新顶点到已知顶点的权改为其相反数(改成小于0)。
            let w = graph.edges[vertexs[j]][vertex];
            graph.edges[vertexs[j]][vertex] = -w;
            graph.edges[vertex][vertexs[j]] = -w;
        }*/
        res.push(edge);
    }
    return res;
}
function test(){
    let g = newGraph2();
    let res = prim(g);
    console.info(JSON.stringify(res,null,2));
}
test();




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值