最小生成树系列之prim的深入浅出的理解
1.最小生成树基本概念
(1)最下生成树概念的解读
官方概念:
• 最小生成树:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
• 最小生成树可以用prim算法(今天的主角!!!!!!!)和kruskal算法(下一节来讲)求出。
• 以上全都是瞎**,(我猜,各位看官也没啥兴趣看这些概念吧!!既然如此,那就让我把这些东西揉碎了吧,重新给他解读一下)
重新解读概念:
在我看来,最小生成树就是:用专业点的话来说就是,给出了这么一幅错综复杂城市联通图(为啥要说城市连通图呢,主要是为了给下面的力扣例题做铺垫!!!我这小机灵鬼),城市与城市之间连接是双向的(正如爱情一样,你爱我,我爱你。我可不希望大家做添购哦)想要以最短的路径去走完。
!!!!!没错,就像图中红色的路径那样这样走才能实现最短的路径并且又走完了所有的城市。
是这是怎么让代码实现的呢?(浮夸表情)各位客官!!留步,真的不难!!!请听小生一一道来!!!
(2)最小生成树的解读(重中之重!!!!)
首先,我们把上面按张图拿来,看我把它一顿暴揍!!!!
算法思路解读
第一步,我们需要一个矩阵去存放各个城市间的通向以及他们之间的距离。(各位看到这么复杂的图千万不要落荒而逃,只要把复杂的图拿出来讲解透彻了才能更加深刻的理解算法!!!都给我继续看下去!!!不许跑!!!)
慌了就听我bb两句吧:就像我们出去旅游一样,选择旅游城市的顺序,第一步肯定是要知道我们从哪出发,要到哪,距离车票是多少,先做一个计划表。除非。。。你是一个有!!钱!!人,除非你是个有钱人??有钱人你进来干什么,掉头发?
另外,我们还需要一个数组去做标记这个城市有没有走过。并且首先全给它初始化为全都没去过。
第二步:我们将各个城市间联通的情况一一存放到矩阵中,注意,城市间是双向的,你有没有听过A城市火车可以通往B城市,但是B城市却不通往A城市,有吗?(有的话请各位小伙伴留言告诉我!!(乖巧)
所以,我们城市1同往2的信息录入了,那城市2通往城市1的信息也应该相应的录入矩阵中。
讲到这里,有没有人看出来这个矩阵的排布方式?我相信你们聪明的小脑瓜肯定看出来了吧,没错这是一个对称矩阵。(如果不知道什么是对称矩阵的,都给我去百度去!!!为你们的知识不足去努力去!!!别逼我给你们看汤家凤老师的视频!!)
好,收!(收的表情),我们圆规正转!!
好了观众们,我们即将开始旅游,请抓紧把手(车速不快!!!),就是吓唬吓唬你们!!!
第三步:我们建立一个队列,这个队列厉害了,需要我们去自定义一个比较准则(这里我们定义的是最短距离从小大大依次排序)
同学们!!!排序准则定义方式有几种啊??(你会的)
这里我写一种最简单的定义方式!!!把你会的定义方式全都给我评论输出!!!独吞可不是好孩子!!!我来提醒提醒各位(lambda表达式、结构体、类都可以(只要是传入的准则是对象就可以了!!))
auto cmp = [](auto & a,auto & b){
return a[2]<b[2];
};
第四步:
然后,同学们,请注意!!!
然后我们把该城市通向其他城市的信息存放到队列中。(注意!!!进了队列就会根据距离进行排序!!)
这边呢我们需要几个变量:走过的路径总和,记录城市之间联通数。
第五步:
我们开始迭代寻找最短路径,直到存放城市信息的队列为空,或者到达了一定条件!!这个条件我们稍后揭晓!!!
我们把队列中权值最小的那个通向的城市取出来,然后再把它从队列中删除掉!!首先判断这个通往的城市有没有去过,如果去过的话,我们就不去了。如果没去过我们就去会会他!!怎么会呢!!
既然要会,那就把这个城市把他标记为已经去过的标记。
然后记录走过的总路径之和。
然后城市之间联通数+1.
这个时候我就要揭晓那个悬念了,就是这个条件:城市之间联通数 等于 城市数 -1
这是一个普通的常识,想要连接n座城市需要n-1边!!朋友们!!别问为什么,你自己想想。来,朋友们,我们一起看哈,两座城市,联通数是1吧,(别问我为什么没有一座城市,一座城市和谁连啊!!自己连自己啊)三座城市,联通数是2吧,4座城市,联通数是3吧,以此类推。
好,我们圆规正转。
当达到这个条件呢,我们就返回城市联通的最小距离!!!
如果没有达到这个条件,说明还没有全部连接,那就继续把到达的城市的信息放入队列中!!!
注意!!!
为加一个是否来到过这个城市标志的原因:因为放在队列中的信息每次删除的只是队列头的信息,但是因为我们去过了的城市,我们就不会再去了,所以加这个判断就会保证不会重复访问同一座城市。
最后,如果我们都没进这个迭代的过程,那说明啥??(问好表情脸)说明城市间他没有连接嘛!就返回错误喽!!!
以上算法的原理就全部讲解完了,不知道各位听明白没有啊?你以为这个算法原理理解到这里就可以了嘛?!!!那你就大错特错了!!!整体根据这个思路你肯定就可以写出相应的代码了,但是,想要真正的理解,请你把写出来的代码用你的🧠和🖊给我把这么多结点都给我走一遍!!!看看我当初怎么理解的吧!!!
算法代码实现
以LeetCode1135:联通所有城市的最低成本为例给大家写代码吧!!注意:算法是prim而不是kruskal算法。kruskal算法下一次写!!
int minimumCost(int N,vector<vector<int>> & connections){
//第一步
vector<vector<int>>edges(N+1,vector<vector<int>>());
vector<bool>visited(N+1,false);
//第二步
for(auto elem : connections)
{
edges[0].push_back({elem[1],elem[2]});
edges[1].push_back({elem[0],elem[2]});
}
//第三步
auto cmp = [](auto & a,auto & b){
return a[2]<b[2]
};
priority_queue<pair<int,int>,vector<pair<int,int>,cmp>myQueue;
//第四步
visited[1] = true;
for(auto & elem : edges[1])
myQueue.push_back(elem);
//第五步
int total = 0,edge = 0;
while(!myQueue.empty())
{
int to = myQueue.front().first();
int distance = myQueue.front().second();
myQueue.pop();
if(!visited[to])
{
visited[to] = true;
++edge;
total += distance ;
if(edge == N -1)
return total;
for(auto & elem : edges[to])
myQueue.push_back(elem);
}
}
return -1;
}