[Golang修仙之路] 算法专题:置换环 与 离散化

93 阅读2分钟

起因是今天做二叉树的层序遍历时,碰见一个题,这个题目其中包含一个环节:

  • 求一个数组,最少需要多少次交换,才能变成严格升序。

针对这个环节,需要引入「置换环」这一知识点。

1. 置换环

直接上例子:

原始数组:[2, 0, 1, 4, 3]

[2, 0, 1] 和 [4, 3] 是原数组中的两个置换环。

有啥用?

结论1: 在置换环内部,搞成升序,只需 n-1 次交换

举例:

  • [2 0 1] 置换环长度=3
  • [1 0 2] 交换1次
  • [0 1 2] 交换1次

共2次

结论2: 置换环之间,不用交换

很重要,也就是说,置换环之间是独立的。

举例:

  • [2 0 1] 变成 [0 1 2] 之后
  • [4, 3] 变成 [3, 4] 之后

整个数组就变成了[0 1 2 3 4]

结论3: 整个数组长度为n,找到m个置换环,则需要n-m次交换

举例: 找到(m=3)个置换环,长度分别为x, y, z

有 x + y + z = n

根据结论1,得到:

总的交换次数 = x - 1 + y - 1 + z - 1 = n - 3

置换环怎么找?

一个有点儿特殊的无序数组[2 0 1 4 3], 特殊在,长度正好是5,这几个值排序后也正好是从0 到 n-1. 如果不是,需要「离散化」

  • 用当前的「值」找下标,循环,直到回到起点。

image.png

代码可以通过一个bool类型的vis数组来实现。

ans += n
vis := make([]bool, n)
for _, v := range a {
    if !vis[v] {
        for ; !vis[v] ; v = a[v] {
            vis[v] = true
        }
        ans -= 1
    }
}

离散化

什么是离散化?

就是数组并不是我们说的特殊数组:

一个有点儿特殊的无序数组[2 0 1 4 3], 特殊在,长度正好是5,这几个值排序后也正好是从0 到 n-1. 如果不是,需要「离散化」

比如一个普通的[7, 5, 8, 9] 经过离散化后,应该保持它们之间相对大小不变,但是变成从 0 到 n-1,[1, 0, 2, 3]

id := make([]int, n)
// id = [1,2,3,4...]
for i := range id {
    id[i] = i
}
// 重新排序id,让id中的值,按照a中的大小升序排列
sort.Slice(id, func(i, j int) bool {
    return a[id[i]] < a[id[j]]
})
// 离散化
for i, v := range id {
    a[v] = i
}

例题

题目:2471. 逐层排序二叉树所需的最少操作数目

完整代码:

func minimumOperations(root *TreeNode) int {
    ans := 0
    q := []*TreeNode{root}
    for len(q) > 0 {
        n := len(q)
        a := make([]int, n)
        for i, node := range q {
            a[i] = node.Val
            q = q[1:]
            if node.Left != nil {
                q = append(q, node.Left)
            }
            if node.Right != nil {
                q = append(q, node.Right)
            }
        }

        id := make([]int, n)
        // id = [1,2,3,4...]
        for i := range id {
            id[i] = i
        }
        // 重新排序id,让id中的值,按照a中的大小升序排列
        sort.Slice(id, func(i, j int) bool {
            return a[id[i]] < a[id[j]]
        })
        // 离散化
        for i, v := range id {
            a[v] = i
        }

        ans += n
        vis := make([]bool, n)
        for _, v := range a {
            if !vis[v] {
                for ; !vis[v] ; v = a[v] {
                    vis[v] = true
                }
                ans -= 1
            }
        }
    }
    return ans
}