当前位置: 首页 > news >正文

并查集解题记录128最长连续序列200岛屿数量547省份数量684冗余连接

首先默写一遍并查集模板(杭电ACM队刘老师那个B站视频讲的很适合入门,理解并查集在干什么)

type UnionFind struct {
	parent []int // 同一个集合里的头,功能就是标注在哪个集合里
	count int
}

func (uf *UnionFind) Init(n int) {
	// 根据数目初始化每个点
	uf.count = n
	uf.parent = make([]int, n)
	for i:= range uf.parent {
		uf.parent[i] = i // 每个元素父节点都是他自己
	}
}

// 查找属于哪一个集合中
func (uf *UnionFind) Find(p int) int {
	if uf.parent[p] != p {
		uf.parent[p] = uf.Find(uf.parent[p]) // 路径压缩
	}
	return uf.parent[p]
}

func (uf *UnionFind) Union(p, q int) {
	rootP := uf.Find(p)
	rootQ := uf.Find(q)
	if rootP != rootQ {
		uf.parent[rootP] = rootQ // 将一个集合的根节点指向另一个集合的根节点
		uf.count-- // 合并后集合数量减少
	}
}

// TotalCount 获取当前集合的数量
func (uf *UnionFind) TotalCount() int {
	return uf.count
}

这个模板没有实现秩优化。可以将深度小的树合并到深度大的树上,或者将节点数少的树合并到节点数多的树上,这样做的目的是让树保持尽可能平衡,避免形成深度很大的树,确保操作的效率。

然后进一步展示一下路径优化在干啥,便于默写出代码:

假设:parent = [0, 0, 1, 2, 3]
表示: 0 ← 1 ← 2 ← 3 ← 4
如果直接 Find(4),你会递归查询:4 → 3 → 2 → 1 → 0(需要 4 步)
如果下次再 Find(4),仍然要 4 步,效率很低!

先做比较简单的母题那种的547省份数量

// 默写并查集模板
type UnionFind struct {
    parent []int
    count int
}

func (uf *UnionFind) Init (total int) {
    uf.parent = make([]int, total)
    uf.count = total
    for i,_ := range uf.parent {
        uf.parent[i] = i
    } 
}

func (uf *UnionFind) Find (q int) int {
    if q != uf.parent[q] {
        uf.parent[q] = uf.Find(uf.parent[q])
    }
    return uf.parent[q]
}

func (uf *UnionFind) Union (p, q int) {
    rootp := uf.Find(p)
    rootq := uf.Find(q)
    if rootp != rootq {
        uf.parent[rootp] = rootq
        uf.count--
    }
}

func (uf *UnionFind) SumCount () int {
    return uf.count
}

func findCircleNum(isConnected [][]int) int {
    uf := &UnionFind{}
    uf.Init(len(isConnected))
    for i:=0 ; i<len(isConnected); i++ {
        for j:=0; j<len(isConnected[0]); j++ {
            if isConnected[i][j]==1 {
                uf.Union(i,j)
            }
        }
    }

    res := uf.SumCount()
    return res
}

现在来思考128最长连续序列。对这道题的思路是先将数据都放入map,然后遍历数组查找每个元素是否有v+1和v-1的元素存在,如果存在就合并这个集合。但是好像会使得集合中存在重复的元素?那就去遍历map而不是数组。还要考虑一个问题,得到并查集后如何统计集合大小?维护一个size数组,记录每个集合的大小,在union时更新。

// 默写一个并查集模板
type UnionFind struct {
    parent []int
    size []int
    count int
}

func (uf *UnionFind) Init (total int) {
    uf.parent = make([]int, total)
    uf.size = make([]int, total) // 初始每个并查集大小为1
    for i:=0; i<total; i++ {
        uf.parent[i] = i
        uf.size[i] = 1
    }
    uf.count = total
}

func (uf *UnionFind) Find (q int) int {
    if q != uf.parent[q] {
        uf.parent[q] = uf.Find(uf.parent[q])
    }
    return uf.parent[q]
}

func (uf *UnionFind) Union (p,q int) {
    rootQ := uf.Find(q)
    rootP := uf.Find(p)
    if rootP != rootQ {
        uf.count--
        uf.parent[rootP] = rootQ // 将一个集合的根节点指向另一个集合的根节点
        uf.size[rootQ] += uf.size[rootP]  // 累加整个集合大小
    }
}

func (uf *UnionFind) maxSize () int {
    max := 1
    for i:=0; i<len(uf.size); i++ {
        if uf.size[i] > max {
            max = uf.size[i]
        }
    }
    return max
}

func longestConsecutive(nums []int) int {
    // 检查为空的情况
    if len(nums) == 0{
        return 0
    } 
    // 去重且放入map用于查找
    // key是用于查找的数值,v是在并查集里面的index,初始化为递增
    m := make(map[int]int)
    p := 0
    for _, value := range nums {
        if _, ok := m[value]; !ok {
            m[value] = p
            p += 1
        }
    }
    // 组织并查集
    uf := &UnionFind{}
    uf.Init(len(m))
    for k,index := range m {
        if v,ok := m[k+1]; ok {
            uf.Union(index, v)
        }
        if v,ok := m[k-1]; ok {
            uf.Union(index, v)
        }
    }

    max := uf.maxSize()

    return max
}

684.冗余连接 这道题读起来感觉像去掉一条边,使得并查集依然为1个。倒着来选择一条边不被遍历,有点暴力。或者有没有办法做并查集的拆分?应该不可能这么幺蛾子。看了佬的思路,说的是:依次扫描所有的边,把边的两端点都合并 union() 到一起。如果遇到一条边的两端点已经在一个集合里面了,就说明是多余边,删除。最后输出这些边即可。 对啊,可以把不关键的边又收集起来,然后输出最后一个即可。

// 先默写并查集模板
type UnionFind struct {
    parent []int
    // count int
}

func (uf *UnionFind) Init (total int) {
    // uf.count = total
    uf.parent = make([]int, total+1) // 坑!对于n条边的图,节点编号是1到n
    for i,_ := range uf.parent {
        uf.parent[i] = i
    }
}

func (uf *UnionFind) Find (q int) int {
    if q != uf.parent[q] {
        uf.parent[q] = uf.Find(uf.parent[q])
    } 
    return uf.parent[q]
}

func (uf *UnionFind) Union (p,q int) bool {
    rootp := uf.Find(p)
    rootq := uf.Find(q)
    if rootp != rootq {
        uf.parent[rootp] = rootq
        // uf.count--
        return false
    } else {
        // 这条边冗余的
        return true
    }
}

func findRedundantConnection(edges [][]int) []int {
    uf := &UnionFind{}
    uf.Init(len(edges))
    var arr []int // 记录冗余边的index
    for i, v := range edges {
        if uf.Union(v[0], v[1]) {
            arr = append(arr, i)
        }
    }

    res := edges[arr[len(arr)-1]]
    return res
}

200岛屿数量应该是指把水平和竖直方向有关联的加入一个集合,最后返回集合数量。需要将二维问题映射到一维并查集。(但是我感觉dfs或者bfs更好通过吧,到时候来补上)(过段时间再写这个解法这样就能巩固一下并查集模板)

相关文章:

  • 【人工智能】从 Llama 到 DeepSeek:开源大模型的演进与技术对比
  • 青少年编程与数学 02-011 MySQL数据库应用 19课题、存储引擎
  • 第三卷:覆舟山决战(85-108回)
  • 环境试验中温湿度循环测试的常见盲区分析
  • 第38周:文献阅读
  • kafka 4.x docker启动kafka4.0.0 docker-compose启动最新版kafka 如何使用docker容器启动最新版kafka
  • 步进电机 cia402协议 报文自己的理解 (笔记)
  • 怎么在一台服务器上配置两套不同的前后端分离系统
  • Docker 环境安装步骤
  • 《初级社会工作者》考试题,附答案解析
  • 蓝桥杯16天刷题计划一一Day01
  • 2007-2019年各省地方财政一般公共服务支出数据
  • 《C语言实现金字塔图案打印》
  • 【新手初学】读取数据库数据
  • 服务器数据恢复—多块硬盘出现坏道导致raid5阵列崩溃的数据恢复案例
  • linux 常见命令使用介绍
  • python每日十题(13)
  • CentOS 7 部署RuoYi 项目
  • MES系统需要采集的数据及如何采集
  • Problem D: 抽象类
  • 湖州做网站推广的公司/女生seo专员很难吗为什么
  • 公司起名字大全免费四个字/上海seo优化服务公司
  • wordpress主题站/aso排名
  • 郑州网站建设选智巢/成都全网推广哪家专业
  • 沈阳怎么做网站/石家庄seo网站排名
  • 淄博企业建网站/百度快照优化排名怎么做