起因是今天做二叉树的层序遍历时,碰见一个题,这个题目其中包含一个环节:
- 求一个数组,最少需要多少次交换,才能变成严格升序。
针对这个环节,需要引入「置换环」这一知识点。
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. 如果不是,需要「离散化」
- 用当前的「值」找下标,循环,直到回到起点。
代码可以通过一个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
}
例题
完整代码:
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
}