IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Lazy Prim介绍 -> 正文阅读

[数据结构与算法]Lazy Prim介绍

Lazy Prim介绍

0.前言

关于MST大家都知道Prim,但是Prim也有多种类别。这里介绍一下Lazy Prim。

因为前段时间有个项目需求就是这个。


1.切分定理

1.定义:在一给定的无向连通图G = (V, E) 中,找到一颗生成树,使得这V-1条边的权值之和最小,这样的生成树就是最小生成树。
而所谓的生成树,简单的说就是在所有的边之间找到V-1条边,使得所有顶点连通且没有形成环。
最小生成树其实是最小权重生成树的简称。
那么如何找到这样的最小生成树呢?这里,就可以用prim算法。这里,我们先介绍lazy prim算法。
在介绍lazy prim算法之前,我们先来介绍一个重要的定理——切分定理。它是prim算法的核心。
我们以一个实例作为讲解。
在这里插入图片描述

这样的一个图表示的各个边的权值如下:

好了,到底什么是切分定理呢?这里我们先介绍几个定义:

切分:把图中的节点分为两部分,称为一个切分(Cut)

横切边:如果一个边的两个端点,属于切分(Cut)不同的两边,这个边称为横切边(Crossing Edge);

切分定理:给定任意切分,横切边中权值最小的边必然属于最小生成树。

如果文字描述不是太理解,小伙伴们可以看下面这张图。

在这里插入图片描述

我们看到这个图有两个部分,蓝色部分和红色部分。而所有绿色的边,就是所谓的横切边,因为绿色的边的两个端点属于属于不同的部分。
那么根据切分定理,我们知道0~7的这条边就是最小生成树的一条边,因为其权值最小。
那么问题来了,为什么这条边属于最小生成树?
这里我们给出证明:

我们看上面这个图,我们可以把这个图分为两个部分。由于图的连通性,那么在这两部分中必然有横切边,并且我们假定绿色的边权值最小。因为要保持连通性,在这三条边中必然要选择一条边使得这两部分连通。如果我们假定左右两部分都已经是最小生成树了,那么从三条边中选择,为了保持总权值最小,必然是选择绿色的边。这就是切分定理。
如果按照这个定理,聪明的小伙伴们可能对这个lazy prim算法有了眉目。既然给定任意切分,横切边中权值最小的边必然属于最小生成树,那么我们完全可以从第0个顶点开始,第0个顶点与其他部分切分,找到最小权值边,然后向下一个顶点扩散。再把这个顶点与第0个顶点当作同一部分与剩下的顶点切分,再找到下一个权值最小边,依此类推。当然,这里我们需要注意不能让这个树变成了环。也就是同一部分的顶点之间的边不能选择。
下面我们用几个图解释一下。
将 0 作为起始点,开始切分,逐步将蓝色阵营的顶点 转换到红色阵营中 。

具体的模拟过程,可以参考这篇博客。

https://blog.csdn.net/IMDASHUAI/article/details/105199649


2.思路

标记一个起点。

  • 每次把横切边都丢到优先队列
  • 取最小的加入生成树,然后标记这两个点。
  • 直到没有横切边。

这种算法时对应我们下面那个代码的,不过这个写法不是最优的。

代码见第一个java。


更体现lazy的一种:

  • 选取起点,加入与他相连的边到优先队列,标记起点。
  • 每次取出队首,如果不是横切边,pop掉之后不管。
  • 否则访问未标记点,然后加边到队列,标记点。更新MST的weight

代码见第二个cpp。

3.代码

代码用到了jar包,见注释。

需要每次遍历全图,时间不优。

 Download link: https://algs4.cs.princeton.edu/code/algs4.jar
		In in = new In(args[0]);
		EdgeWeightedGraph G = new EdgeWeightedGraph(in);
        boolean[] marked = new boolean[G.V()]; // In the early Monday tutorial,  Boolean is a class whereas boolean is a primitive.
        Queue<Edge> mst = new Queue<Edge>(); // A linked list could also work.
        double weight = 0.0;
        marked[0] = true;

        while (true) {
            // In order to find a minimum crossing edge, we loop through all the edges in the graph and
            // add all of the crossing edges to a minimum priority queue.
            MinPQ<Edge> minpq = new MinPQ<Edge>();

            for (Edge e : G.edges()) {
                int u = e.either();
                int v = e.other(u);
                // B)  Check the condition if they are cross edges and insert to pq
                if (marked[u] != marked[v])
                    minpq.insert(e);
            }

            // If the priority queue is empty, then this means there are no more crossing edges so we are done.
            if (minpq.isEmpty()) {
                break;
            }

            // If the priority queue is not empty, then the minimum element in it is a minimum crossing edge.

            // C) get the edge minimum from pq
            Edge cross_edge = minpq.delMin();
            // D) Update the (current) MST and  Mark the other vertex
            mst.enqueue(cross_edge);
            int u = cross_edge.either();

            int v = cross_edge.other(u);
            weight += cross_edge.weight();
            if (!marked[v]) marked[v] = true;
            if (!marked[u]) marked[u] = true;

        }
        // Once an MST has been found, print its edges and total weight.
        for (Edge e : mst) {
            StdOut.println(e);
        }
        StdOut.printf("%.5f\n", weight);  //打印边权

更快点的做法。

#include<iostream>
#include<queue>
#define INF 1e4
using namespace std;
double G[8][8] =
{
 //0   1   2   3   4   5   6   7 
 {INF,INF,0.26,INF,0.38,INF,0.58,0.16},
 {INF,INF,0.36,0.29,INF,0.32,INF,0.19},
 {0.26,0.36,INF,0.17,INF,INF,0.40,0.34},
 {INF,0.29,0.17,INF,INF,INF,0.52,INF},
 {0.38,INF,INF,INF,INF,0.35,0.93,0.37},
 {INF,0.32,INF,INF,0.35,INF,INF,0.28},
 {0.58,INF,0.40,0.52,0.93,INF,INF,INF},
 {0.16,0.19,0.34,INF,0.37,0.28,INF,INF}
};//数组初始化
//定义一个结构体,表明这是一个由v、u连接的边,权值为w
//并将运算符重载,比较大小的是比较权值
struct Edge {
 int v;
 int u;
 double w;
 bool operator < (const Edge& a) const {
  return w > a.w;
 }
};
//标记数组,判断一个顶点是否已经在红色阵营了
bool marked[8];
//优先队列
priority_queue<Edge,vector<Edge>,less<Edge> > w;
//vis函数是用来把x顶点放入红色阵营
//这里是赋值为1
//并把所有与x顶点连接的边放入优先队列
void vis(int x) {
 marked[x] = 1;
 for (int i = 0; i < 8; i++) {
  if (G[x][i] == INF)
   continue;
  w.push({ x,i,G[x][i] });
 }
}
int main()
{
 //先访问第0个顶点
 vis(0);
 //最小权值
 double minw = 0;
 while (!w.empty()) {
  Edge e = w.top();
  w.pop();
  //如果顶点u和v的都在红色阵营,那么值一定相同,这时我们就跳过
  if (marked[e.u] == marked[e.v])
   continue;
  //访问顶点u
  vis(e.u);
  minw += e.w;
 }
 cout << minw;
 return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-16 22:43:30  更:2022-03-16 22:49:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 11:34:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码