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. [转帖]TiUP Cluster 命令合集

    https://docs.pingcap.com/zh/tidb/stable/tiup-component-cluster TiUP Cluster 是 TiUP 提供的使用 Golang 编写的集 ...

  2. 学到一个编码技巧:用重复写入代替if判断,减少程序分支

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 近期阅读了rust标准库的hashbrown库(也就是一个 ...

  3. linux root用户密码输入正确还是提示access denied

    问题:之前用远程工具连接一直都是好的,第二天上班找开远程工具要输root的密码了,输入用户密码后还是无效,可以确定用户密码是对的,其中有一个远程工具一直是连着的就没有问题. 排查问题: 1.相接用pa ...

  4. slices in Go 1.21

    Go 1.21中新增的 slices包中提供了很多与切片相关的函数,适用于任意类型的切片. 本文内容来自官方文档 BinarySearch 函数签名如下: func BinarySearch[S ~[ ...

  5. CMake出错的处理

    在windows上使用cmake来c++的程序,遇到一个问题 问题排查 试过在电脑上单独使用gcc是可以编译成功的,那么就可能是IDE集成的问题了 IDE的编译工具链从mingw换成vs,编译通过 让 ...

  6. 深度学习应用篇-计算机视觉-语义分割综述[5]:FCN、SegNet、Deeplab等分割算法、常用二维三维半立体数据集汇总、前景展望等

    深度学习应用篇-计算机视觉-语义分割综述[5]:FCN.SegNet.Deeplab等分割算法.常用二维三维半立体数据集汇总.前景展望等 语义分割综述(semantic segmentation) 1 ...

  7. 【3】jupyter notebook【修改主题、字体、字号、代码自动补全、变更工作目录、添加目录插件、解决工具栏隐藏问题、修改默认浏览器、默认打开位置】

    相关文章: [1]Anaconda安装超简洁教程,瞬间学会! [2]Anaconda下:ipython文件的打开方式,Jupyter Notebook中运行.py文件,快速打开ipython文件的方法 ...

  8. 详细了解Transformer:Attention Is All You Need

    1. 背景 在机器翻译任务下,RNN.LSTM.GRU等序列模型在NLP中取得了巨大的成功,但是这些模型的训练是通常沿着输入和输出序列的符号位置进行计算的顺序计算,无法并行. 文中提出了名为Trans ...

  9. 七款云上共享文件系统 POSIX 兼容性大比拼

    当用户在进行文件系统选型时,POSIX 语义兼容性是必不可缺的一项考察指标.JuiceFS 一直非常重视对 POSIX 标准的高度兼容,在持续完善功能.提高性能的同时,尽力保持最大程度的 POSIX ...

  10. mybatis批量插入支持默认值和自定义id生成策略的免写sql插件

    最近做项目时用了免写sql的插件但是发现批量操作不满足现有需求.所以,在原有基础之上扩展了批量的操作支持[支持插入默认值和自定义id生成策略].使用方法如下: 一:在pom文件中引入jar配置 < ...