最小生成树刷题笔记

2024-05-13 00:44
文章标签 笔记 最小 生成 树刷题

本文主要是介绍最小生成树刷题笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

算法基础:

最小生成树是所有节点的最小连通子图!!!!

首先是prim算法三部曲:

(1)找到距离最小生成树最近的节点。

(2)将距离最小生成树最近的节点加入到最小生成树中。

(3)更新非最小生成树节点到最小生成树的距离。

实现步骤:

首先我们利用一个for循环遍历n - 1遍,因为我们从第1个节点开始将其加入到生成树之中后知道添加到还剩两个节点时,我们可以发现当我们添加玩倒数第二个节点后,最后一个节点的mindist数值在处理倒数第二个节点的第三部更新过程中已经得到了,这个距离不是倒数第二个节点到它的距离grid[cur][j]就是之前已经得到的它与某个节点之间的距离mindist[j]。

(1)寻找最近节点:

判断最近节点的三个条件:1.未在生成树中。  2.距离生成树距离最短     3.选取最短距离我们先随便选出一个节点然后再与其他节点比较

if(!isvisited[j] && (cur = -1 || mindist[cur] > mindist[j])              Cur = j;

这里:Mindist[cur] < mindist[j]包含了两层含义:首先我们要明确我们要找离生成树距离最短的点,mindist数组中存的只就是节点到生成树的最小距离,如果后续的节点的mindist的值要小于前面得出的mindist值,那么就将当前节点的标记j赋值给cur。

这里我们每遍历一层就会将cur置为-1以便我们可以最快选取一个随机节点并遍历后面的节点与之比较。而第二层的mindist[j]的值已经在第一层的第三步更新操作中得到。

(2)将最近节点加入生成树:

isintree[cur] = true;

(3)更新非生成树节点到生成树距离:

利用一个for循环遍历所以非生成树节点,并更新起距离mindist[j] < grid[cur][j] ? Mindist[j] = mindist[j] : mindist[j] = grid[cur][j]

这列的更新操作有两层意思:第一是遍历的外部节点需要与生成树有连接,也就是grid[cur][j] < mindist[j] == INT_MAX,第二是当前节点已经跟生成树有连接但还没选入进入生成树(预备党员哈哈)因为之前节点的更新扩散操作使得与之前节点有连接的j节点有了mindist的数值,所以我们拿grid[cur][j]与之前得到的最近距离mindist作比较保留更小的那个值赋值给mindist[j]

经过上面的分析,我认为prim算法的关键一定是要明确mindist数组的含义:是当前节点到最小生成树的最小距离。因为我们的第一步选点用到mindist数组来根据各个点到生成树(第一步时就是到源点的距离)的最小距离来选取最近节点,我们的第三步更新也是由于新加入的点导致未加入节点到生成树的距离改变,通过grid[cur][j] 与mindist[j]来判断是否需要更改mindist的值。

#include<iostream>
#include<vector>
using namespace std;
int main() {
    int v, e;
    int x, y, k;
    cin >> v >> e;
    // 填一个默认最大值,题目描述val最大为10000
    vector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001));
    while (e--) {
        cin >> x >> y >> k;
        // 因为是双向图,所以两个方向都要填上
        grid[x][y] = k;
        grid[y][x] = k;

    }
    // 所有节点到最小生成树的最小距离
    vector<int> minDist(v + 1, 10001);

    // 这个节点是否在树里
    vector<bool> isInTree(v + 1, false);

    // 我们只需要循环 n-1次,建立 n - 1条边,就可以把n个节点的图连在一起
    for (int i = 1; i < v; i++) {

        // 1、prim三部曲,第一步:选距离生成树最近节点
        int cur = -1; // 选中哪个节点 加入最小生成树
        for (int j = 1; j <= v; j++) { // 1 - v,顶点编号,这里下标从1开始
            //  选取最小生成树节点的条件:
            //  (1)不在最小生成树里
            //  (2)距离最小生成树最近的节点
            //  (3)只要不在最小生成树里,先默认选一个节点 ,在比较 哪一个是最小的
            //  理解条件3 很重要,才能理解这段代码:(cur == -1 || minDist[j] < minDist[cur])
            if (!isInTree[j] && (cur == -1 || minDist[j] < minDist[cur])) {
                cur = j;
            }
        }
        // 2、prim三部曲,第二步:最近节点(cur)加入生成树
        isInTree[cur] = true;

        // 3、prim三部曲,第三步:更新非生成树节点到生成树的距离(即更新minDist数组)
        // cur节点加入之后, 最小生成树加入了新的节点,那么所有节点到 最小生成树的距离(即minDist数组)需要更新一下
        // 由于cur节点是新加入到最小生成树,那么只需要关心与 cur 相连的 非生成树节点 的距离 是否比 原来 非生成树节点到生成树节点的距离更小了呢
        for (int j = 1; j <= v; j++) {
            // 更新的条件:
            // (1)节点是 非生成树里的节点
            // (2)与cur相连的某节点的权值 比 该某节点距离最小生成树的距离小
            // 很多录友看到自己 就想不明白什么意思,其实就是 cur 是新加入 最小生成树的节点,那么 所有非生成树的节点距离生成树节点的最近距离 由于 cur的新加入,需要更新一下数据了
            if (!isInTree[j] && grid[cur][j] < minDist[j]) {
                minDist[j] = grid[cur][j];
            }
        }
    }
    // 统计结果
    int result = 0;
    for (int i = 2; i <= v; i++) { // 不计第一个顶点,因为统计的是边的权值,v个节点有 v-1条边
        result += minDist[i];
    }
    cout << result << endl;

}

leetcode - 1584:连接所有点的最小费用

由于这道题给出的是点集,我们可以想到使用prim来处理点集的最小生成树问题,这道题不好使用kruskal来解决因为还要求边那就需要我们去求各个点的组合会复杂很多。

就是利用题目中的公式建立邻接矩阵在套用上面的模板即可:

class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = points.size();
        vector<vector<int>> grid(n, vector<int>(n, 0));
        // 计算任意两点之间的曼哈顿距离并填充到grid矩阵中
        for(int i = 0; i < n; i++){
            for(int j = i + 1; j < n; j++){
                int x = abs(points[i][0] - points[j][0]);
                int y = abs(points[i][1] - points[j][1]);
                grid[i][j] = grid[j][i] = x + y;
            }
        }
        vector<bool> isvisited(n, false);
        vector<int> mindist(n, INT_MAX);
        mindist[0] = 0; // 从第一个点开始
        int res = 0;
        for(int i = 0; i < n - 1; i++){
            int cur = -1;
            // 选择当前距离生成树最近的节点
            for(int j = 0; j < n; j++){
                if(!isvisited[j] && (cur == -1 || mindist[j] < mindist[cur])){
                    cur = j;
                }
            }
            isvisited[cur] = true;
            // 更新其他节点到生成树的距离
            for(int j = 0; j < n; j++){
                if(!isvisited[j] && mindist[j] > grid[cur][j]){
                    mindist[j] = grid[cur][j];
                }
            }
        }
        for(int i = 1; i < n; i++){
            res += mindist[i];
        }
        return res;
    }

这篇关于最小生成树刷题笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/984209

相关文章

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

MySQL中动态生成SQL语句去掉所有字段的空格的操作方法

《MySQL中动态生成SQL语句去掉所有字段的空格的操作方法》在数据库管理过程中,我们常常会遇到需要对表中字段进行清洗和整理的情况,本文将详细介绍如何在MySQL中动态生成SQL语句来去掉所有字段的空... 目录在mysql中动态生成SQL语句去掉所有字段的空格准备工作原理分析动态生成SQL语句在MySQL

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

Java利用docx4j+Freemarker生成word文档

《Java利用docx4j+Freemarker生成word文档》这篇文章主要为大家详细介绍了Java如何利用docx4j+Freemarker生成word文档,文中的示例代码讲解详细,感兴趣的小伙伴... 目录技术方案maven依赖创建模板文件实现代码技术方案Java 1.8 + docx4j + Fr

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

java中使用POI生成Excel并导出过程

《java中使用POI生成Excel并导出过程》:本文主要介绍java中使用POI生成Excel并导出过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求说明及实现方式需求完成通用代码版本1版本2结果展示type参数为atype参数为b总结注:本文章中代码均为

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

C/C++随机数生成的五种方法

《C/C++随机数生成的五种方法》C++作为一种古老的编程语言,其随机数生成的方法已经经历了多次的变革,早期的C++版本使用的是rand()函数和RAND_MAX常量,这种方法虽然简单,但并不总是提供... 目录C/C++ 随机数生成方法1. 使用 rand() 和 srand()2. 使用 <random