2024-02-24:用go语言,给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1,

同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti],

表示在 fromi 和 toi 节点之间有一条带权无向边,

最小生成树 (MST) 是给定图中边的一个子集,

它连接了所有节点且没有环,而且这些边的权值和最小。

请你找到给定图中最小生成树的所有关键边和伪关键边。

如果从图中删去某条边,会导致最小生成树的权值和增加,那么我们就说它是一条关键边,

伪关键边则是可能会出现在某些最小生成树中但不会出现在所有最小生成树中的边。

请注意,你可以分别以任意顺序返回关键边的下标和伪关键边的下标。

输入:n = 5, edges = [[0,1,1],[1,2,1],[2,3,2],[0,3,2],[0,4,3],[3,4,3],[1,4,6]]。

输出:[[0,1],[2,3,4,5]]。

答案2024-02-24:

来自左程云

灵捷3.5

大体过程如下:

1.定义并查集和辅助数组:首先定义并查集的数据结构,包括父节点数组 father、节点大小数组 size、辅助数组 help,以及集合数量 sets。还需要定义边的状态记录数组 status,其中 status[ei] 记录第 ei 条边的状态。

2.初始化并查集:使用 buildUnionSet(n) 函数初始化并查集,将每个节点自成一个集合。

3.构建边数组:使用 buildEdges(e) 函数将输入的边数组 e 转换成包含边信息的二维数组 edges,并按照权值从小到大进行排序。

4.建立图:根据集合编号建立图的相关数据结构,包括链式前向星建图。定义头指针数组 head、边信息数组 info、下一条边指针数组 next,以及边数量 edgeSize。使用 buildGraph(k) 函数初始化这些数组。

5.添加边:使用 addEdge(a, b, ei) 函数向图中添加边,其中 ab 是集合编号,ei 是边的索引。

6.找桥:使用 Tarjan 算法,利用深度优先搜索找到所有的桥。具体实现在函数 tarjan(init, cur, father, ei) 中,其中 init 是起始节点,cur 是当前节点,father 是当前节点的父节点,ei 是当前边的索引。

7.寻找关键边和伪关键边:通过遍历边数组 edges,逐个加入到最小生成树中,并利用并查集判断是否形成环。在每次加入边的过程中,记录是否是关键边或伪关键边。具体过程如下:

  • 初始化起始索引 start = 0

  • 当并查集的集合数量不为 1 时,继续循环。

  • 确定结束索引 end,使得 edges[start][3] != edges[end][3] 或者 end == m

  • 调用 connect(start, end) 连接边,构建大团子的图并找到桥。

  • 遍历 startend 的边,根据边的状态记录到关键边或伪关键边的数组中。

  • 合并集合,更新并查集。

  • 更新 start = end,继续下一轮循环。

8.返回结果:将关键边和伪关键边的数组返回作为结果。

综上所述,总的时间复杂度为 O(m^2 * α(n)),其中 m 是边的数量,n 是节点的数量,α 是阿克曼函数的反函数。总的额外空间复杂度为 O(m + n)。

go完整代码如下:

package main

import (
"fmt"
"sort"
) const MAXN = 101
const MAXM = 201 // 边状态的记录
// status[ei] = 0,代表ei这个边既不是关键边也不是伪关键边
// status[ei] = 1,代表ei这个边是伪关键边
// status[ei] = 2,代表ei这个边是关键边
var status [MAXM]int // 并查集相关
var father [MAXN]int
var size [MAXN]int
var help [MAXN]int
var sets int // 并查集初始化
func buildUnionSet(n int) {
for i := 0; i < n; i++ {
father[i] = i
size[i] = 1
}
sets = n
} // 并查集向上找代表节点
func find(i int) int {
r := 0
for i != father[i] {
help[r] = i
i = father[i]
r++
}
for r > 0 {
r--
father[help[r]] = i
}
return i
} // 并查集合并集合
func union(i, j int) {
fi := find(i)
fj := find(j)
if fi != fj {
if size[fi] >= size[fj] {
father[fj] = fi
size[fi] += size[fj]
} else {
father[fi] = fj
size[fj] += size[fi]
}
sets--
}
} // 边相关
var edges [MAXM][4]int var m int func buildEdges(e [][]int) {
for i := 0; i < m; i++ {
edges[i][0] = i
edges[i][1] = e[i][0]
edges[i][2] = e[i][1]
edges[i][3] = e[i][2]
}
sort.Slice(edges[:m], func(i, j int) bool {
return edges[i][3] < edges[j][3]
})
} // 通过集合编号建图相关
// 链式前向星建图
// 为啥用这玩意儿建图?没啥,就是想秀
var head [MAXN]int
var info [MAXM][3]int
var next [MAXM]int
var edgeSize int func buildGraph(k int) {
for i := 0; i < k; i++ {
head[i] = -1
edgeSize = 0
}
} func addEdge(a, b, ei int) {
next[edgeSize] = head[a]
info[edgeSize][0] = ei
info[edgeSize][1] = a
info[edgeSize][2] = b
head[a] = edgeSize
edgeSize++
} // 哈希表相关
// 一个集合,给一个编号
var id [MAXN]int // 找桥相关
var dfn [MAXN]int
var low [MAXN]int
var cnt int func findBridge(k int) {
for i := 0; i < k; i++ {
dfn[i] = 0
low[i] = 0
}
cnt = 0
for init := 0; init < k; init++ {
if dfn[init] == 0 {
tarjan(init, init, -1, -1)
}
}
} func tarjan(init, cur, father, ei int) {
cnt++
dfn[cur] = cnt
low[cur] = cnt
for i := head[cur]; i != -1; i = next[i] {
edgei := info[i][0]
nodei := info[i][2]
if nodei != father {
if dfn[nodei] == 0 {
tarjan(init, nodei, cur, edgei)
low[cur] = min(low[cur], low[nodei])
} else {
low[cur] = min(low[cur], dfn[nodei])
}
}
}
if low[cur] == dfn[cur] && cur != init {
status[ei] = 2
}
} func findCriticalAndPseudoCriticalEdges(n int, e [][]int) [][]int {
m = len(e)
buildUnionSet(n)
buildEdges(e)
var bridge []int
var pseudo []int
start := 0
for sets != 1 {
end := start + 1
for end < m && edges[start][3] == edges[end][3] {
end++
}
connect(start, end)
for i := start; i < end; i++ {
ei := edges[i][0]
if status[ei] == 2 {
bridge = append(bridge, ei)
} else if status[ei] == 1 {
pseudo = append(pseudo, ei)
}
union(edges[i][1], edges[i][2])
}
start = end
}
return [][]int{bridge, pseudo}
} // 大团子,一个集合,缩成一个点
// 当前的边,[start...end)
// 做图!大团子的图,找桥!
func connect(start, end int) {
for i := start; i < end; i++ {
id[find(edges[i][1])] = -1
id[find(edges[i][2])] = -1
}
k := 0
for i := start; i < end; i++ {
if id[find(edges[i][1])] == -1 {
id[find(edges[i][1])] = k
k++
}
if id[find(edges[i][2])] == -1 {
id[find(edges[i][2])] = k
k++
}
}
buildGraph(k)
for i := start; i < end; i++ {
index := edges[i][0]
a := id[find(edges[i][1])]
b := id[find(edges[i][2])]
if a != b {
status[index] = 1
addEdge(a, b, index)
addEdge(b, a, index)
}
}
findBridge(k) sort.Slice(info[:edgeSize], func(i, j int) bool {
if info[i][1] != info[j][1] {
return info[i][1] < info[j][1]
}
return info[i][2] < info[j][2]
}) right, left := 0, 0
for left < edgeSize {
right = left + 1
for right < edgeSize && info[left][1] == info[right][1] {
right++
}
for i := left + 1; i < right; i++ {
if info[i][2] == info[i-1][2] {
status[info[i][0]] = 1
status[info[i-1][0]] = 1
}
}
left = right
}
} func min(a, b int) int {
if a < b {
return a
}
return b
} func main() {
n := 5
edges := [][]int{{0, 1, 1}, {1, 2, 1}, {2, 3, 2}, {0, 3, 2}, {0, 4, 3}, {3, 4, 3}, {1, 4, 6}}
result := findCriticalAndPseudoCriticalEdges(n, edges)
fmt.Println(result)
}

2024-02-24:用go语言,给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1, 同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti的更多相关文章

  1. ROS的安装-> rosdep init /update报错2022.02.24实测有效

    ROS的安装-> rosdep init /update报错2022.02.24实测有效   一. 解决rosdep_init问题 正常执行sudo rosdep init会报错,如下: ERR ...

  2. c语言main函数返回值、参数详解(返回值是必须的,0表示正常退出)

    C语言Main函数返回值 main函数的返回值,用于说明程序的退出状态.如果返回0,则代表程序正常退出:返回其它数字的含义则由系统决定.通常,返回非零代表程序异常退出. 很多人甚至市面上的一些书籍,都 ...

  3. 动态语言切换(续)-designer中的retranslateUi(带源码)

    本站所有文章由本站和原作者保留一切权力,仅在保留本版权信息.原文链接.原文作者的情况下允许转载,转载请勿删改原文内容, 并不得用于商业用途. 谢谢合作.原文链接:动态语言切换(续)-designer中 ...

  4. 并查集例题02.带权并查集(poj1182)

    Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A.现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底 ...

  5. C 语言学习的第 02 课:C 语言的开发环境

    工欲善其事,必先利其器.不知道还是不是记得上一篇文章中说到的,计算机本身是一个数据输入及输出的设备.所以,为了将你大脑中的各种 idea 输入到电脑,且最终生成能够执行的程序,总是要预备点什么的. 通 ...

  6. Objective-C的对象模型 http://foredoomed.org/blog/2014/02/24/object-modeling-of-objective-c/

    Objective-C是一门面向对象,并且在C的基础上加入了Smalltalk式的消息机制而形成的编程语言,它主要被苹果公司用于开发Mac OS X和iOS操作系统.既然Objective-C是面向对 ...

  7. 让你提前知道软件开发(24):C语言和主要特征的发展史

    文章1部分 再了解C语言 C语言的发展历史和主要特点 作为一门众所周知的计算机编程语言,C语言是谁发明的呢?它是怎样演进的?它有何特点?究竟有多少人在使用它? 1. C语言之父 C语言是1972年由美 ...

  8. 让你提前知道软件开发(24):C语言和主要特征的历史

    文章1部分 再次了解C语言 C语言的发展历史和主要特点 作为一门众所周知的计算机编程语言,C语言是谁发明的呢?它是怎样演进的?它有何特点?究竟有多少人在使用它? 1. C语言之父 C语言是1972年由 ...

  9. 2015/12/24:嵌入式C语言的位操作随笔

    今晚是平安夜,首先祝大家平安夜快乐,明天是圣诞,祝大家圣诞快乐!!        好了,这周都特别有空,上班也非常轻松,基本就是看看内核驱动,学学安卓,没什么正事的开发活干.今晚,我们来总结一例在现实 ...

  10. 02树莓派4B—C语言编程——PWM

    01树莓派直接输出PWM波 —— 硬件PWM程序  (推荐使用) #include <stdio.h> #include <wiringPi.h> #include <s ...

随机推荐

  1. [转帖]018、数据库管理之TiDB升级

    升级 使用TiUP进行补丁升级(HotFix) 版本升级流程 升级准备-更新TiUP 升级准备- 编辑TiUP Cluster 升级准备- 集群监控状态检查 升级TiDB 集群 验证TiDB集群升级结 ...

  2. [转帖]linux查看端口及端口详解

    https://www.cnblogs.com/the-tops/p/6126941.html   今天现场查看了TCP端口的占用情况,如下图   红色部分是IP,现场那边问我是不是我的程序占用了tc ...

  3. [转帖]比较不同CPU下的分支预测

    https://plantegg.github.io/2023/04/16/%E6%AF%94%E8%BE%83%E4%B8%8D%E5%90%8CCPU%E4%B8%8B%E7%9A%84%E5%8 ...

  4. [转帖]ssd/san/sas/磁盘/光纤/RAID性能比较

    https://plantegg.github.io/2022/01/25/ssd_san%E5%92%8Csas%E7%A3%81%E7%9B%98%E6%80%A7%E8%83%BD%E6%AF% ...

  5. [转帖]CentOS 7 下用 firewall-cmd / iptables 实现 NAT 转发供内网服务器联网

    https://www.cnblogs.com/hope250/p/8033818.html 自从用 HAProxy 对服务器做了负载均衡以后,感觉后端服务器真的没必要再配置并占用公网IP资源.而且由 ...

  6. [转帖]关于redis,你需要了解的几点!

    github:https://github.com/windwant 博客园 首页 新随笔 联系 订阅 管理 随笔 - 227  文章 - 4  评论 - 36  阅读 - 73万   一.关于 re ...

  7. [转帖]CPU Utilization is Wrong

    Brendan Gregg's Blog home CPU Utilization is Wrong 09 May 2017 The metric we all use for CPU utiliza ...

  8. Redis labs 的安装

    Install and setup This guide shows how to install Redis Enterprise Software, which includes several ...

  9. CDP技术系列(一):使用bitmap存储数十亿用户ID的标签或群体

    一.背景介绍 CDP系统中目前存在大量由用户ID集合组成的标签和群体,截止当前已有几千+标签,群体2W+. 大量的标签都是亿级别数据量以上,例如性别.职业.学历等均,甚至有群体中的ID数量达到了数十亿 ...

  10. JavaScript一种新的数据结构类型Map

    什么是map 它类似于对象,是键值对的集合,但键的范围不局限在于字符串.各种类型的值(包含对象)都可以作为键. 如果同一个键被多次赋值,后面的值将会覆盖其那面的值.如果读取一个未知的键,返回的是und ...