代码随想录算法训练营第 57 天 |53.寻宝(最小生成树)

代码随想录算法训练营

Day57 代码随想录算法训练营第 57 天 |53.寻宝(最小生成树)



前言

prim法

讲解文档

kruskal法

讲解文档


最小生成树解决的问题:将v个点连接起来的最小花费

一、prim法

1.题目链接

53

2.思路

(1)二维矩阵存图
(2)三步法
1)确定当前距离最小生成树最近的点
遍历所有不在最小生成树的点,找到当前距离最小生成树最近的
2)当前距离最小生成树最近的点加入最小生成树
用数组标记点是否在最小生成树里,is_tree[i]=1表示节点i在最小生成树里
3)更新各个点到最小生成树的距离
注:

  • 为了把v个点连接起来,需要v-1条边
  • 点到最小生成树的距离,是这个点到最小生成树所有节点的距离中的最小值

3.题解

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int v;
    int e;
    cin>>v>>e;
    vector<vector<int>> graph(v+3,vector<int>(v+3,10001));
    //不相连的点认为是无穷远
    std::vector<int>minDis(v+3,10001);
    vector<bool>is_tree(v+3,false);
    int s;
    int t;
    int w;
    for(int i=0;i<e;i++){
        cin>>s>>t>>w;
        graph[s][t]=w;
        graph[t][s]=w;
    }
    for(int i=1;i<v;i++)//链接v-1条线
    {
        int cur=-1;
        int min_dis=10005;
        for(int j=1;j<=v;j++)
        {
            if(!is_tree[j]&&min_dis>minDis[j]){
                min_dis=minDis[j];
                cur=j;
            }
        }
        is_tree[cur]=true;
        for(int k=1;k<=v;k++){
            if(is_tree[k])continue;
            minDis[k]=min(minDis[k],graph[cur][k]);
        }
    }
    int res=0;
    for(int i=2;i<=v;i++)
    {
        res+=minDis[i];
    }
    cout<<res;
    return 0;
}

二、kruskal

1.思路

(1)定义边类型(成员包括端点和长度),边数组存放图
(2)边长排序
(3)遍历边
(4)判断边的两个端点是否有在同一集合内(有共同祖先):用并查集判断
如果是:那么不连接(如果链接就会成环)
如果不是:那么链接,加入最小生成树(加入结果,本题不用标记是否加入最小生成树),并且加入并查集

2.题解

#include<bits/stdc++.h>
using namespace std;
vector<int>father(10001,0);
struct edge{
    int l;
    int r;
    int val;
};
const bool cmp(edge e1,edge e2)
{
    return e1.val<e2.val;
}
int find(int x){
    if(x==father[x]){
        return x;
    }
    father[x]=find(father[x]);
    return father[x];
}
int main()
{
    int v;
    int e;
    cin>>v>>e;
    vector<edge>edges;
    for(int i=1;i<=v;i++){
        father[i]=i;
    }
    int left;
    int right;
    int val;
    for(int i=0;i<e;i++){
        cin>>left>>right>>val;
        edges.push_back({left,right,val});
    }
    sort(edges.begin(),edges.end(),cmp);
    int res=0;
    for(int i=0;i<e;i++)
    {
        int l_root=find(edges[i].l);
        int r_root=find(edges[i].r);
        if(l_root!=r_root){
            father[l_root]=r_root;
            res+=edges[i].val;
        }
    }
    cout<<res;
    return 0;
}

总结

本质都是贪心
prim维护点:遍历点,每次加入最近的点
kruskal维护边:将所有的边排序,遍历边,优先加入短的边,每次连接时先判断连上以后是否成环

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值